Ask HN: Functional Programming Differences
I found out today that I'm speaking at the local Code Camp next weekend. The topic is introducing F# and functional programming to a lot of C# and VB Microsoft guys.
I've got some good sample code, but my question is this: what are the key things you would tell an imperative programmer about functional programming? I need about a dozen key points to get across before I start drowning them in code.
So far I'm thinking: "start little, grow big", "stay immutable", "factor relentlessly", "learn recursion", and "treat functions as first-class citizens"
Any other blurbs come to mind that capture the differences in thinking between imperative and functional code?
29 comments
[ 5.1 ms ] story [ 77.1 ms ] threadAnother point I'd consider is "avoid side-effects".
That's because it's relatively rarely needed: http://www.info.ucl.ac.be/~pvr/paradigms.html (I don't recall the HN submission.) The paper says that any feature in a programming language makes it more difficult to analyse it. First class functions are one of them. Named state (with assignment) is another. So, when such a feature isn't needed (for, say, modularity), the complexities it introduces are unnecessary.
I think one of the biggest intellectual hurdles with functional programming is that we know, deep down, that modern computers don't work like that. They do have side-effects and changing, carried state. It's a revelation to realize that the programming model you use to write programs doesn't have to match how the computations will be physically carried out.
Closures might be another thing to mention. For instance, you could show how they can be used to implement objects (which should be familiar to C# people).
Great concepts, sure. But in an hour? Might be too much to get their heads around.
One of my code samples is going to be lifted from this great blog entry: http://diditwith.net/2008/03/14/WhyILoveFARefactoringTale.as... where he basically covers some of these concepts by way of example.
I think no matter how I do it, it's going to look (to them) like some hand-waving and magic. Can't really help that, I don't think.
The composition operator can easily be seen as a function with two arguments, which show "first class" functions:
And poof, you can introduce currying:The thing about recursion is that for imperative guys, the first place they go is the for...next loop. Whereas in FP the first place you should go is the built-in stuff like map, fold, etc. But the second place is recursion (with the tail calls done correctly), not for..next
Here's what I've been using for slicing:
(Sorry for my bad english)
I like Haskell for the intellectual challenge, but if the aim is to write clear example code that will be understandable to a large audience, you'd be much better off writing it in Python.
What I say is by using a functional style (might it be in java, c++, haskell, scheme, python, or whatever), it makes testing, threading and understanding things easier.
For instance, it's easier to test an immutable class since once it is created, it will never change.
Threading is also simplified since you don't have to think about your object being corrupted by 2 threads mutating it.
Understanding is simplified in the sense that it's easier to quickly get an idea of what an object is doing because it only has 1 state at a time.
But please, don't think I didn't understand your point about obscurely named function and bad code.. Often when people show functionnal code, they try to show how short the code is so they make it practically unreadable for the novices - which is indeed not the best way to show the vertue of FP.
"you'd be much better off writing it in Python." => Nothing stop you from using FP in python :)
- Functional design is about breaking a problem up into a series of transformations (functions) on data, rather than modelling object states and behaviors.
- Compose the transformations by treating functions as first-class citizens. Introduce map, filter, foldr, etc. here
- "Functional" = Avoid side-effects! (wrap an I/O layer around your pure core processing instead) Otherwise the composing breaks.
(optional):
- "Start little, grow big", "factor relentlessly" - these are good concepts in ALL programming, and they apply here too.
I think if you just get those points in an hour, along with a nice example (I think F# comes with a web crawler that illustrates all these points), you'll have done very well.
But would supplying code unstick him? It seemed unlikely. Thinking about the equation "Concepts + details = code" I wondered if there was a way of conveying the concepts first, before getting down to the details, and wrote:
Put on your Dijkstra robe, and sit in your arm chair, with a cup of tea, and write on a pad of paper, made from dead tree, using a pen with real liquid ink. This is mathematics not computation :-)
compare this to peano arithmetic If you want to learn recursion you should move quite quickly to writing code because that animates your recursions and clears up any lingering misconceptions. On the other hand, jumping from the words "learn recursion" direct to code is a big leap. Recursion has its own soul, separate from its embodiements in various programming languages.If you think "what", recursion is easy. If you think "how", recursion is magic. I think that is the biggest leap. I forgot how I made the jump, however.
Actually, Object oriented programmers already do this. If they use "class polymorphism", "mixins", or any mechanism where an object's method could be determined at runtime, they use first class functions. They just don't know it yet. So, take an example where they would use mixins, then present them a closure or lambda equivalent. That should make functions less scary. (Side note: try to avoid the terms "first class" and "anonymous", "closure", and "lambda". "Ordinary" and "literal" are better, less scary. They point out the fact that functions are as mundane as integers, or strings, so of course they could be parameters or return values.)
"stay immutable"
This is indeed crucial. But effectively impossible until you don't change the way you think. Most imperative programmers think about what the objects should do. Of course, without side effect, they won't do anything, making programming impossible. So they have to think more about what the objects should be. Then, they will be able to avoid side effects until they are really needed.
"factor relentlessly"
Talk about that only when you have talked about functions (don't say "first class"!), as an example of how you could really use them. `sort`, `map` and `filter` would be my favourite examples. Insist on the low syntactic burden of factoring.
What you are getting at, though, is that constructs they are already familiar with can be expressed functionally. That is a good point, and it always helps make concepts more concrete when you provide a mapping from current knowledge to new knowledge.
Also, the ability to reason about your code is a big selling point of side-effect free code. When you don't have to keep track of a bunch of state in your head, it's much easier to reason about what code does, and find any bugs or unexpected behavior.