3 ms·
"Stepwise refinement" is an intuitive and powerful concept, in part because it helps you grok and debug in a "fractal" kind of way, level by level. But it does
by tabtab 3y ago
"Stepwise refinement" is an intuitive and powerful concept, in part because it helps you grok and debug in a "fractal" kind of way, level by level. But it does have limits at a larger scale. (Event-driven architecture is often better for big.)
It's something Functional Programming either lacks or doesn't do as smoothly. The intermediate results and state are highly useful for debugging, and Functional doesn't "like" intermediate state. Yes, it can be emulated, but the emulation is rarely good as the real thing. For one, the emulated state lacks useful variable and token names, because it's machine-generated.
- Ma8ee 3y agoFunctional doesn’t like state. That’s the whole point. Functions that can be run in isolation with well defined input is much easier to debug.
- tabtab 3y agoIn theory. Practice has proven messier to many. Maybe some have a "functional mind", but they shouldn't extrapolate their head to others. Functional has been around for 60+ years. If it were the magical productivity and Lego-modularity golden hammer, it would have mainstreamed already. (Long LINQ is also hard to debug for many of us.)
- Ma8ee 3y agoIt is becoming mainstream. Not by pure functional languages taking over, but by developers adopting a functional style and by better and better support for functional programming in mainstream languages.
- tabtab 3y agoI'm not sure I'd agree. A lot of the use-cases are for niches or systems programming. I don't see it for business CRUD that often unless either the framework forces it on one, or they are using LINQ, which as I pointed out, can be tricky to debug if long. SQL is largely functional, but it's also hard to debug. The addition of the WITH clause to SQL greatly helped, as one can paste sections to test. Still, not as x-ray-able as imperative. Imperative still wins most x-ray contests. Maybe fast code readers don't need debugging as often? Could be, but I'm average at it.
- ParetoOptimal 3y ago> The intermediate results and state are highly useful for debugging, and Functional doesn't "like" intermediate state. s/foldr/scanl Only sort of kidding. Can you give aconcrete example of some task you'd like intermediate state around? Also, do you know about the validate monad?
- tabtab 3y agox = 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