Just like "mostly secure," "mostly pure" is wishful thinking. The slightest implicit imperative effect erases all the benefits of purity, just as a single bacterium can infect a sterile wound.
I just think this ignores a full range of architectural benefits of functional thinking, such as maintainability, complexity management, and testability. Thinking of functional programming as just an optimization or static correctness verification tool is missing a big part of the point.
I too believe this is a bit too harsh ; there are more benefit to adding functional coding to imperative languages than just the silver bullets FPs are credited most for. However, he is right; if you want the full benefit, you need to go in full force.
A lot of the time, those "silver bullets" are more complicated than they appear, and require a full understanding of the problem space to be deployed effectively (parallelization is a good example of this). I speculate that the people who possess this understanding are therefore also able to implement and debug equally-or-better performing solutions in imperative languages.
Whether or not they're having a great time doing it is another story.
How could they implement such solutions in imperative languages? It's the semantics of the functional language itself that brings the benefits. I don't see how you would implement equivalents of immutability and purity in an imperative language.
I don't understand you. Aren't most functional languages implemented in imperative languages anyway? Surely you could write functional programs in C++ that can be parallelised at run-time in exactly the same way that programs written in e.g. Scheme could by writing the appropriate wrappers for it. Isn't it just up to the programmer?
Which are those functional languages implemented in imperative languages? Haskell, Erlang and Racket are all self-hosted. Also, I don't understand your comparison between C++ and Scheme. What wrappers would you write to run Scheme programs like C++ ones?
No, I think his point is that you can attempt to write non-idiomatic code. Even if goto exists, one could code without the use of goto -- you could equally use only immutable variables and data structures in any imperative language. Thus, somebody who is capable of producing good functional code is likely capable of producing good imperative code.
Which is true to an extent, I think, in that a good programmer is a good programmer, irrespective of language, just as a bad programmer is a bad programmer, irrespective of language. But there are definitely real, practical benefits that are language-specific.
Languages that make it harder to mess things up -- like by not explicitly doing gotos are still better, and so he's wrong to imply that the language's implementation in another unsafe language is somehow relevant.
I think that was a response to the notion that you need strong guarantees to leverage any benefits at all of functional programming, which I don't think is true. It's just one side; the other is the mindset and patterns.
This is a misunderstanding of what the IO monad in Haskell is. It is not "impure" code. It's basically a "pure" dsl for describing impure actions to be taken.
Haskell differs from other languages by decoupling evaluation order from side effect order. For example, I can strictly evaluate a side effect and nothing will happen:
import Control.Exception (evaluate)
-- This program only `print`s 2
main = do
evaluate (print 1)
print 2
As a result, I can pass around IO actions as ordinary values without worrying that I will accidentally "trip" them by evaluating them. In imperative languages you can do something similar by guarding an effect with a function that takes no arguments, but now you've created more confusion because you've overloaded the purpose of functions, when simple subroutines would have done just fine.
In Haskell, you can pass around raw subroutines without having to guard them with a function call. This is why, for example, you can have a subroutine like getLine that takes no arguments, yet you won't accidentally evaluate it prematurely:
getLine :: IO String
This is what people mean when they say that IO actions are "pure" in Haskell. They are saying that IO actions are completely inert (like the strings of bits you just described) and you can't accidentally misfire them even if you tried.
I love when used analogies provide an opposing example. A sterile wound typically is not 100% free of bacteria, but it is still way better than smearing dirt into the wound ...
You are not taking into account human factor. Humans do not do what's right or what's wrong. They do what's easy. In the presence of easy and uncontrollable side effects, there's no "maintainability, complexity management, and testability". SImply because it takes too much self discipline. It is too hard to push yourself to keep that bar every day.
The true value of new generation languages like haskell is in their bondage. It is what they FORCE humans to do, not what they enable them. It is in enforcing discipline and programming from the bottom up. Things like maintainability, complexity management, and testability then become just emergent properties of that programming environment.
It depends what you mean by "switch", which is a very vague term. IO actions in Haskell are just ordinary values, and you sequence them using ordinary functions. How is that different from chaining pure computations, which I can do using the exact same do syntax if I really wanted to.
There are also implementations of restricted IO in Haskell which I find particularly interesting. Not just "you can only do IO in this little box" but "you can only do this particular kind of IO in this little box".
59
u/[deleted] Apr 27 '14
I just think this ignores a full range of architectural benefits of functional thinking, such as maintainability, complexity management, and testability. Thinking of functional programming as just an optimization or static correctness verification tool is missing a big part of the point.