|Syntactic Confectionery Delight|
Re: pissed off about functional programmingby Roboguy (Initiate)
|on Aug 13, 2014 at 00:24 UTC||Need Help??|
Is this post about functional programming in general or functional programming in Perl? The way its worded implies that it is about FP in general, but its content suggests that it is specifically about Perl. Many FP languages are not dynamically scoped for example. Also, there are FP languages where variable reassignment is (at least) heavily restricted (Haskell for instance). I'm going to use Haskell to discuss a few points here because I am pretty familiar with it and it will be very uncontroversial to call it a functional programming language.
Regarding the 1st myth, I'm not sure that I've heard anyone argue that FP is lambda calculus. Now, I'm not familiar with the term "simultaneity" in the context of lambda calculus and I'm not sure I completely understand your description. I would like to point out, however, that there are many different ways that a lambda calculus expression can be reduced and there is no default reduction strategy if you just refer to "lambda calculus" in general. There are many potential reduction strategies that can be (and are) used. As a result, you can't really make a statement like "This is how reduction is always done in lambda calculus".
For the 2nd myth, I believe you are saying that the two paradigms are the same because they are both Turing equivalent. Again, I don't think I've ever heard anyone argue that this is not the case, and this is not what is meant when someone says FP is different from IP (in my experience, anyway). It would be tough to argue that programming in assembly isn't any different from programming in Perl. This is the sort of difference I hear people talk about (not to imply that IP is like assembly when compared to FP, I just want to illustrate the difference that I believe is often being discussed). The way that you program in FP is generally very different than IP (recursion is rarely used in IP compared to FP, for instance).
Now for the 3rd myth, this particular issue can be a bit heated, but I feel that referential transparency is something that is particularly misrepresented here so please hear me out (not to say that I believe you are intentionally trying to misrepresent it, but I think that the description you gave has some important inaccuracies). Referential transparency only means that, with respect to scope, a binding can always be replaced by the value it was bound to and visa versa (this is the basis of some very important optimization rules in Haskell compilers, such as stream fusion, that would otherwise have the potential to break working programs and would change their meaning if referential transparency didn't hold). There's nothing really more to it than that. I feel that dynamic scoping is a red herring here because referential transparency always only holds (when it does hold) with respect to a certain scope. It doesn't have much meaning if you don't take the appropriate scope rules into account. It also doesn't hold in the presence of unrestrained mutation. Also, I don't think that you'll find many people that will claim that Perl is always referentially transparent (the Perl example you gave doesn't have a Haskell counterpart that demonstrates the same non-referential transparency). There are languages that are referentially transparent. Haskell almost always is, even in the presence of IO. There are very few exceptions to this in Haskell, but they are very frowned upon and there is no major code base that I'm familiar with that takes advantage of this in a visible way (except for some very low-level bindings to C libraries). These few exceptions are necessary to interface with C code and to achieve certain kinds of efficiency goals (FP and efficiency is another interesting topic, but that's for another time). If you feel that this invalidates my argument, I would also like to point out that you can enable an option in the GHC Haskell compiler to prevent any of these unsafe things from being used. So, at the very least, when this option (Safe Haskell) is enabled, the language is a complete example of this.
The 4th myth has some interesting nuances to it. Let's start by discussing what variable assignment means. The first thing I'm going to do is completely throw out the idea that FP doesn't have any form of assignment. You can (in the vast majority of FP languages) assign a value to a name. I doubt that anyone would argue this and I don't think that this is what you were referring to in this section, but I want to be specific and as unambiguous as possible here. The real issue here is reassignment.
Okay, so the next thing we have to talk about is what does it mean for something to get reassigned? I don't think it would be a stretch to say that this means that the value stored at the memory location associated with a given name gets changed to some other value so that, from that point forward, that name refers to the new value until it gets changed again. This is possible in Haskell. There are some extremely important stipulations with this though. This is not possible with the way that names are normally bound in Haskell, you have to go through a special type in order to do this (either ST or IO). Not only that, but the variable can never escape this type and interact with any "pure" code. This is partly how referential transparency is maintained in Haskell.
Full disclosure: I have worked with functional programming for a while now so I am a bit biased towards it, but let me finish up by saying what I'm not trying to suggest: That FP is somehow fundamentally better than IP or that it is some kind of magical silver bullet. That's just not true and I believe that when people say that kind of thing it actually hurts the credibility of FP pretty badly in addition to just being outright wrong, so I am very against that sort of statement. It is different though, and I hope I've demonstrated that (and some other things) in this post.
PS: If you would like specific Haskell examples of anything that I discussed above, any other Haskell examples you'd like me to provide or any clarification, feel free to ask and I'll do my best to write something up!
Sorry, this is a little more long winded than I hoped it'd be. Thanks for reading!