I’d met Richard earlier, a month ago at Pittsburgh TechFest. He does not claim to be an expert at functional programming, but is enthusiastic about concepts and techniques that he can and does apply to improving software quality in many dimensions. Since I have been a functional programming enthusiast and practitioner for twenty years, I had these goals in attending his presentations:
- evaluate what Richard and others have done with, and think is important about, functional programming
- offer a few corrections, elaborations, suggestions as appropriate for the situation
- gather information on how I may be able to effectively explain functional programming to those who are new to it
Two years ago, in 2011, I had attended the first incarnation of the group faithfully, attending several months of meetings before it disbanded when the founder left Pittsburgh. I learned quite a bit from those meetings.
Comments on Richard’s presentation
Immutability and “functional languages”
Richard talked about immutability as being part of what “functional programming” is about.
Yes, it is, and in addition, common best practices in many programming languages these days argue for favoring immutability. I want to emphasize that you don’t have to go full-blown into a specialized “functional language” in order to take advantage of immutability as desired: for example, this tip has long since been known as at least a “design pattern” in the Java world, in the Ruby world, and basically everywhere else.
Richard suggested that “functional languages” don’t allow mutation. By my definition, this is not actually true. I didn’t raise an objection during the talk because I didn’t want to sidetrack it, but here I have space to elaborate a little. I would argue (but that would have to be another blog post) that the cleanest, most intuitive and novice-accessible “functional languages”) are those in the ML family originally developed in the 1970s and lives on today’s popular, industrial-strength dialects of Standard ML, OCaml, and F#.
ML fully supports mutation, through reference cells.
Personally, because of notions like this, I’ve been thinking that maybe it would be best if we all stopped using the term “functional language”, because it has sadly become misleading and ambiguous. In particular, people instantly think “Haskell” when they use the word “functional language”, when in fact Haskell is a very unusual, unique language (and ecosystem) among languages that one could call “functional”.
Expressions, functions, evaluation, and values
As Richard put it, “favor expressions evaluating to a value”. The focus of functional programming is on returning a value, from an expression that includes function calls, rather than munging some mutable global state, or modifying state through a reference passed into a function as a parameter.
Recursion vs. looping?
Richard noted that one characteristic of functional programming is the use of recursion instead of looping. He gave examples of writing tail-recursive functions.
One nitpick (which again, I did not bring up during the talk): recursion and looping (as in through “normal” constructs such as
for) are not actually opposed. I will write a blog post later about why conventional looping is best thought of as a derived construct built on top of recursion, and why it might be best to consider conventional looping to be a historical accident and mistake (but a very useful invention given the historical circumstances).
Efficiency of recursion?
Recursion for parallelism
Higher-order functions, first-class functions
Richard: “A function is an object”.
It’s really as simple as that. Functions can be created, stored, passed around. Functions can return other functions.
valudDidChange are just functions. They are not special things, “methods”.
1 2 3 4 5 6 7 8 9 10 11 12
Richard gave some standard examples of using higher-order functions such as
As a matter of style, he recommended against long, obscure one-liners that involve chaining. I agree. For readability and testability of pipelines, I have found that it is good to break things up into intermediate steps. (Just recently, Tim Lesher also recommended against stupidity masking as excessive cleverness in his Python lightning talk about functional programming with
Regrettably, Richard brought up the term “monads”. I’m close to thinking that this word should be banned, because too many confusions about them have ruined the word. I have yet decided what the best alternative phrase might be. Maybe something like “computational context”?
Ironically, Richard admitted he was going to abuse the word, and for the sake of example, he said, he was going to say that “jQuery is a monad”. I didn’t want to derail his talk, and since he admitted the abuse, I decided there would be a better time to address this topic.
It was interesting to me, however, that he gathered that the interesting thing about jQuery, and why he wanted to call it a monad, was the chaining of calls in jQuery. I need to think of a good way to deal with this intuition when I get around to explaining monads.
Partial application and currying
Richard explained both partial application and currying, then he wondered when one would in practice write a curried function.
Richard recommended checking out the following libraries, especially Lo-Dash, which he judges to be faster than Underscore:
Questions and answers
There were many interesting questions raised after the presentation.
Introducing functional programming into a team environment
Someone asked Richard a good question about whether it’s appropriate to, say, start using Underscore at work and expecting other team members to learn how to use it. Is it fair to expect others to learn a new way of doing things that might seem weird?
My point of view is that there’s nothing specific about functional programming when it comes to questions like this. The same question comes up when evaluating a particular programming language or a particular MVC framework or a particular version control system, and the answer involve considering all the different realities of who is comfortable with what, whether it is worth learning something new, what the advantages and disadvantages are, how much the decision will affect delivery of business value.
Growing parameter list for a function vs. passing in a big object
One concern brought up is that if you’re programming in a functional style, you can end up being faced with ever-growing parameter lists for a function to pass in everything that it needs. One solution is to create a big object to stick everything into it that might be needed (for example, a configuration object instead of separate flag parameters). Is this overkill?
Also, Tony Lukasavage emphasized that his concern was in the context of the need to evolve an existing API without breaking customers' client code that uses it.
My first note would be that API design is tricky and checking out some resources on API design is very helpful.
Regarding parameter lists, Richard noted that you might have a design problem if you have a function that needs to know about so many things it depends on. You may want to refactor the function (of course, in the context of evolving a mature API that customers depend on, it may be too late, unfortunately) and the data structures passed in. For example, if you have a number of functions that all take as parameters a
lastName, then maybe you want a single
name parameter instead.
I suggested also that if certain flags are not always needed, then instead of a huge configuration object, how about simply having separate but related functions (in a language supporting overloading, you could provide overloaded functions, although the topic of overloading is another large one to discuss later), each of which takes only the flags the client cares about. Of course, as some people pointed out, then you might end up with annoyingly long and specific function names.
Nobody said designing an API is easy.
Do I actually do functional programming?