3 ms·
x = foo01(); y = foo02(x); z = foo03(x, y); Contrast with: foo03(foo01(), foo02(foo01()) ); x, y, and z were named by humans, giving the
by tabtab 3y ago
x = foo01();
y = foo02(x);
z = foo03(x, y);
Contrast with:
foo03(foo01(), foo02(foo01()) );
x, y, and z were named by humans, giving them domain meaning, whereas a debugger would give the second one no name or machine-made names for intermediate state. (Here, z, y, and z have no meaning because it's a foo-bar example, but in practice they'd have better names.)
The first is also often easier to read than the second, at least to my eyes. I know devs who can read the second quickly, but I don't think it's the norm. The LINQ "dot style" can be a little easier to read, but has similar problems.
- Ma8ee 3y ago> Here, z, y, and z have no meaning because it's a foo-bar example, but in practice they'd have better names As would foo0x. I don't think many functional programmers would object to writing code as you did in your first example if you think it improves readability and if you don't change what the names refer to.
- garethrowlands 3y agoYour first example is just as functional as your second. And, quite possibly, better style. But your overall point is fair. A function, once composed, curried or having captured data, is very much like an object. Indeed its representation in memory is likely very similar to an object (mostly functional languages) or actually identical (Java). Your debugger won’t show the data in such a function but it’s happy to show the data in an object. That’s a big advantage of objects over functions. Hopefully debuggers will get better at this eventually.
- ParetoOptimal 3y agoThis example: x = foo01(); y = foo02(x); z = foo03(x, y); could easily be written in Haskell as: let x = foo01 y = foo02 x z = foo03 x y in z My thinking is that functional languages don't preclude you from having intermediate state, but I will agree that composition is used often. Typically when working in a repl I don't really miss the intermediate state because my debugging consists of: The lambdas must flow. λ> foo01 "expected result" λ> foo02 "UNexpected result" Then I'll debug foo02 in isolation. > foo03(foo01(), foo02(foo01()) ); I think I'd write this as the let example above with variables. Otherwise I guess I'd need to use something like: foo03 foo01 . foo02 $ foo01 Here's a Haskell playground link with these examples if you're curious or had something else in mind and want to modify the example: https://play.haskell.org/saved/MeRGyCjr https://play.haskell.org/saved/MeRGyCjr
- Izkata 3y agoI'd go with this, which I find the easiest to read: x = foo01(); foo03(x, foo02(x)); In my head I tend to see code as a directed graph, and this one most closely matches that graph without creating too big a jumble on one line. The first one that also has y and z has additional nodes and edges, making the graph more complicated than it needs to be. Your second one would have the simplest graph, except on one line like that it obscures that foo01() is called twice. If that second one was actually foo04(), then I'd prefer this as the simplest form (which I'd actually originally written before noticing foo01() was in there twice), because it has the simplest directed graph of all, and the code matches it very closely: foo03( foo01(), foo02(foo04()) ); If the variables and function names were long enough, then I'd use that last format even with my first example, to avoid the whole "jumble of letters and numbers" while still keeping the directed graph simple: the_big_x = the_big_foo01() zimzamfoo03( the_big_x, bang_bar02(the_big_x) );