Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

Re: pissed off about functional programming

by Roboguy (Initiate)
on Aug 13, 2014 at 00:24 UTC ( #1097201=note: print w/replies, xml ) Need Help??

in reply to pissed off about functional programming

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.
Let me explain my view of the other part of referential transparency (as it exists in Haskell) by specifically examining the IO type. When you write a sequence of IO actions, it can be viewed as referentially transparent in that this sequence of IO values (that is, IO actions) can be seen as producing a series of instructions which are then interpreted by the computer. This is in contrast to the idea of an IO value itself actually performing an action. It is a subtle distinction and I can see how it could be debated as to whether it is useful to interpret it in this way (you could argue that you can view C, for instance, in the same way and that it is a pointless philosophical difference). The important thing to realize about this, however, is that this second part of Haskell's referential transparency is given more meaning by the first part. The first part is really the important part, the part you can actually see in practice that actually matters in a real sense. A function in Haskell (Safe Haskell, if you are insistent on that distinction) will evaluate to the same value every time it is called with the same argument. Incidentally, this is a very clear, practical difference that you can see between Haskell and imperative languages.
One last thing that I would like to say about this last item is that, as I was hinting at in the start, you can have apparent reassignment that is actually not reassignment in the sense of physically changing the value at the location of an already-assigned variable. You can simulate it with a completely "pure" internal implementation, though this isn't as efficient as actually changing the value. If this technique is implemented in Haskell, you would again notice that the apparent mutation is (in a very natural way) separated entirely from pure code. This separation is more or less the same as the separation that occurs when using ST or IO (which do use "real" mutation internally). Because of this, you can start to get some decent intuition about this separation by thinking of it as working in this way.

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!

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1097201]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (4)
As of 2020-01-19 00:09 GMT
Find Nodes?
    Voting Booth?