8 ms·
This is a gross mischaracterization of functional programming and basically attacks a straw man--one that's lamentably common when talking about functional prog
by tikhonj 13y ago
This is a gross mischaracterization of functional programming and basically attacks a straw man--one that's lamentably common when talking about functional programming.
I'm going to repost a comment I wrote on the blog. It's long and really needs editing, but I hope it gets my thoughts across. I think the part about OOP is also misguided, but it's so obviously tacked on to a rant about functional programming that I just ignored it.
Haskell’s “purity” is not about getting rid of side-effects but about controlling side-effects. It’s making side-effects first-class citizens: now you can write code that talks about having or not having them!
You can still have side-effects however you want, you just have to be explicit about it. To some extent, the fact that this uses monads is incidental: all that’s important is that there is some IO type, some ST type and so on–the fact that they all form monads is almost an implementation detail. That’s why some of the most exciting Haskell features–STM, the IO manager and so on–are all about effects. Clearly, Haskell more than acknowledges effects, so the entire diatribe about ignoring the existence of side-effects is attacking a straw man.
Besides, you can’t simply replace a first-class system for managing effects with static analysis, without essentially reproducing the same restrictions. How would you do something like Haskell’s deterministic parallelism model or reliable STM or the very aggressive loop fusion (and general rewriting) Haskell uses? There’s a reason you don’t see these things done nearly as well in any other languages: all of these fall apart as soon as you introduce side-effects, so you need some way to help the programmer ensure things like this are only used safely.
And this is exactly how types like IO and ST help make code safer. Sure, within an ST block, you have stateful code that’s just as hard to analyze. But you can guarantee that this does not leak outside the block. Similarly, functions can rely on their inputs not doing anything untoward however they’re used. This allows you to explicitly state the assumptions about your inputs: how is this a bad thing? In turn, this makes writing code that takes advantage of these properties much easier: you can ask that your inputs do not cause side-effects in a way that’s self-documenting and easy to verify. Then you’re free to re-evaluate your inputs or call functions however many times you want, as concurrently as you want. At the extreme, this can even be used for security purposes: see Safe Haskell.
Sure you can write pure functions in any language. And you can write side-effecting procedures in Haskell too. But the difference is that Haskell lets you be explicit about whether you want side-effects or not–it’s just another part of your interface. And this is how types like IO and ST help make your code easier to think about: any code that is not in a type like IO or ST can only depend on its arguments, making all the dependencies more explicit. (Note, again, how this is all independent of “monads”–it’s all about effects, and the types just happen to form monads.) This does not make static analysis too much easier, but that was never the point–the goal is to make the code easier to think about, and knowing that there are no hidden state dependencies certainly does that. A static analyzer can follow data flow easily, but it requires quite a bit of thinking for the programmer to do the same!
The core motivation for managing effects à la Haskell is not “mathematical purity”: it’s software engineering. We want code that is easier to think about, has better guarantees and is more modular. The goal is to make code less complex by removing hidden dependencies between distant parts of your program (mutable state) and the effect of evaluation on the meaning of your program (side-effects in general). You can refactor and move around most Haskell code without worrying about breaking its surroundings because any dependencies are explicit. You can extract something into a function, consolidate multiple function calls into one or split one into multiple and generally change your code up quite a bit without worry–these actions cannot change the code’s correctness because effects are managed for you.
Ultimately, functional programming like Haskell is not just normal programming with side-effects outlawed. Instead, it’s a different basis for programming which allows you to manage side-effects explicitly. In this light, papers like “solved problem but with monads” are entirely reasonable: they’re about bringing things over to this new basis. And this goes the other way too: there’s a reason why you don’t see good STM, deterministic parallelism, stream fusion (and rewrite-rule optimizations in general), anything like DPH and anything like Safe Haskell in other languages.
- Negitivefrags 13y agoWith regard to declaring the lack of side effects in an interface, I'd just like to mention that C++ is very nice in this regard too, and I think it's an important feature of C++ that is often overlooked. Yes, it's true that with casting and so on you are not actually ensuring anything when you declare a function const like you are with Haskell, but you announce to other programmers who will use your code that: 1) There will be no observable state changes 2) That the function is thread safe This is a very useful thing for a language to support in it's function declarations, and other languages could do well to learn from that.
- eru 13y agoD actually has a better model for pure functions than C++. See http://dlang.org/function.html#pure-functions http://dlang.org/function.html#pure-functions
- luikore 13y agoModern C/C++ compilers can detect a sub set of pure functions and do possible aggressive optimisations. There's even an `__attribute__ ((pure))` in GCC for explicit pureness.
- quchen 13y agoThat is not true. In C++ you can always retrieve the current time, store parameters in a database, print to stdout, or retrieve a global variable without changing the interface. Merely announcing purity to other programmers does not solve this problem: announcements can be wrong, missing, incomplete, and maybe most importantly the compiler doesn't know about them.
- emn13 13y agoHeck, mere "announcements" might even be quite useful, especially since it is slightly compiler supported. But const in C++ does not announce purity, nor does it announce thread-safety. I just announced that it won't observably change non-mutable members, that's it.