4 ms·
To paraphrase a recentish comment from jerf, “sometimes you just have a long list of tasks to do”. That stuck with me. Now I’m a bit quicker to realize when I’m
by onionisafruit 2y ago
To paraphrase a recentish comment from jerf, “sometimes you just have a long list of tasks to do”. That stuck with me. Now I’m a bit quicker to realize when I’m in that situation and don’t bother trying to find a natural place to break up the function.
- runevault 2y agoFor me it depends. Sometimes I find value in making a function for a block of work I can give its own name to, because that can make the flow more obvious when looking at what the function does at a high level. But arbitrarily breaking up a function just because is silly and pointless.
- lostdog 2y agoPlus, laying the list of tasks out in order sometimes makes it obvious how to split it up eventually. If you try to split it up the first time you write it, you get a bunch of meaningless splits, but if you write a 300 line function, and let it simmer for a few weeks, usually you can spot commonalities later.
- runevault 2y agoThat's also true, though in this case I'm not necessarily worried about commonalities, just changing the way it reads to focus on the higher level ideas making up the large function. But revisiting code after a time, either just because you slept on it or you've written more adjacent code, is almost always worth some time to try and improve the readability of the code (so long as you don't sacrifice performance unnecessarily).
- F-W-M 2y agoDefine that function directly in the place where it is used (e.g. as a lambda, if nesting of function definitions is not allowed). Keeps the locality and makes it obvious that you could just have put a comment instead.
- tugu77 2y agoA useful trick is to then at least visually structure those 150 lines with comments that separate some blocks of functionality. Keeps the linear flow but makes it still easier to digest.
- mr_mitm 2y agoWhy not just do something like this then? This: myfunction(data) { # do one thing to the data ... # now do another ... } becomes that: myfunction(data) { do_one_thing_to_the_data(data) now_do_another(data) } do_one_thing_to_the_data(data) { ... } now_do_another(data) { ... } Still linear, easier to get an overview, and you can write more modular tests.
- ropejumper 2y agoBecause now you have to jump around in order to see the sequence of events, which can be very frustrating if you have to constantly switch between two of these functions. Plus, if we're dealing with a "long list of tasks" that can't be broken up in reusable chunks, it probably means that you need to share some context, which is way easier to do if you're in the same scope. One thing I find useful is to structure it in blocks instead, so you can share things but also contain what you don't want shared. So e.g. in rust you could do this: let shared_computation = do_shared_computation(); let result_one = { let result = do_useful_things(); other_things(&shared_computation); result } ... I think it's a nice middleground. But you still can't write modular tests. But maybe you don't have to, because again, this is just a long list of tasks you need to do that conceptually can't be broken down, so maybe it's better to just test the whole thing as a unit.
- deleted 2y ago[deleted]
- rocqua 2y agoIf the arguments to the function required are small, then breaking such a block down makes sense. Otherwise, it usually feels like an unnatural function to me.