A response to “Ten reasons to not use a functional programming language”

If you haven’t read the top ten reasons to not use a functional programming language, I think you should. It’s a well written post and ironically debunks a lot of the major trepidations people have with functional languages.

But, I wanted to play devils advocate here. I read a lot of articles on functional and everyone touts a lot of the same reasons to use functional languages, and this post was no different. What these posts always lack, though, is acknowledgement that functional isn’t the end all be all of language solutions. It has plenty of problems itself, and it’s just as easy to critique it using the same ten reasons. That said, I wanted to address a few of my opinions regarding functional programming using the same format as the original article.

Reason 1: I don’t want to follow the latest fad

The authors point here is that people claim that functional is a fad, but he’s right in that it’s not. Functional has been around as long as imperative has, in fact Alonzo Church pioneered it with the concepts of lambda calculus.

That said, people don’t like functional because it’s hard to map to their mental model. I don’t know about you, but when I think of doing something 4 times I don’t think of a recursive loop with an accumulator, I think of a for loop. Making the mental jump to functional isn’t easy for everyone which is why it has been slow to adopt to the mainstream.

However, some aspects of functional are reasonably mainstream. Lambdas, options, first class functions and higher order functions are available in many languages such as Ruby, Scala, C#, JavaScript, Python, etc. Some even, like Scala, encourage immutable types! Functional isn’t a fad, but pure functional may be.

Reason 2: I get paid by the line

The point here is that functional languages are usually syntactically shorter. An example the author posts is this:

public static class SumOfSquaresHelper
{
   public static int Square(int i)
   {
      return i * i;
   }

   public static int SumOfSquares(int n)
   {
      int sum = 0;
      for (int i = 1; i <= n; i++)
      {
         sum += Square(i);
      }
      return sum;
   }
}

compared to

let square x = x * x
let sumOfSquares n = [1..n] |> List.map square |> List.sum

But that’s cheating. What if we did this instead:

public int SumOfSquares(int n)
{
    return Enumerable.Range(1, n).Select(i => i * i).Sum();
}

And

let square x = x * x
let sumOfSquares n = [1..n] 
                         |> List.map square 
                         |> List.sum

Now who has more lines? It’s all in how you see it. Granted, both are leveraging higher order functions, but most modern imperative languages support that. Comparing crappy code with good code is never a comparison. Terse code can be written in any (almost) language (sorry Java).

Reason 3: I love me some curly braces

Personally I don’t like space dependent languages since its easy to make scoping mistakes, but that notwithstanding, lets look at some Clojure:

(defn run-prep-tasks
  [{:keys [prep-tasks] :as project}]
  (doseq [task prep-tasks]
    (let [[task-name & task-args] (if (vector? task) task [task])
          task-name (main/lookup-alias task-name project)]
      (main/apply-task task-name (dissoc project :prep-tasks) task-args)))

While functional usually has less curly braces, many functional languages have a whole lot more parenthesis

Reason 4: I like to see explicit types

This is a common complaint from people who aren’t used to functional, and I can understand, because if someone asked you what the signature below does on first glance what would you say?

('State -> 'T1 -> 'T2 -> 'State) -> 'State -> 'T1 list -> 'T2 list -> 'State

Practiced functional programmers can tell its a function that takes a function (which takes a state, two items, and returns a new state) a seed state, and two lists, and returns a final state. This is the type signature of List.fold2 and its a mouthful!

Compare to the example the author gave:

public IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
    IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector
    )

Immediately at first glance, without caring about the types, you can tell it returns an enumerable, it takes an enumerable, and it takes a function. At the signature you can even see how the source and the selector map to each other. Reading the code you get a sense of how things work together. I won’t lie, the signature is nasty, and its verbose. Part of me wishes C# had inferred method signatures, but the other part really likes that I can glance at something and get a big picture overview of what is happening.

On top of that, its easy to make the mistake of passing a function instead of applying a function. Take this example:

apply one two

You might think that we are applying two to one, or maybe one to two, or maybe I am passing in a function called one and an argument called two, or maybe both one and two are functions and are being combined and returned as another function, or maybe I meant to curry the apply function by applying one to two like this:

apply (one two)

It’s very easy to make mistakes like this in functional, especially if the type arguments are generic enough. If the signature for apply is a 'a -> 'b -> 'c then you don’t know what you meant! Anyways, this is the complaint people have about implicit vs explicit typing.

Reason 5: I like to fix bugs

I like to fix type mismatches AND bugs.

Reason 6: I live in the debugger

I still live in the debugger. To say that a language makes it so that if your code compiles it probably works just boggles my mind. Code can compile fine but be logically completely wrong. This happens in every language! In fact, debugging in fsharp can be complex because of the pipe operator (see my other post on debugging the pipe operator).

Reason 7: I don’t want to think about every little detail

I didn’t really get this one. The author talks about how by having all the types matched up you suddenly think of all edge conditions. That’s just not true. Like I mentioned above, edge condtions are part of logical flow, not code semantics. You can have all the types match up and still have edge cases you didn’t consider.

Reason 8: I like to check for nulls

This one is fun because I’ve brought this up with a coworker to discuss before. I, personally, really like the option type, but you can still have nulls. What about

let foo = None;

Option.get foo

This results in:

System.ArgumentException was unhandled
  HResult=-2147024809
  Message=The option value was None
Parameter name: option
  Source=FSharp.Core
  ParamName=option
  StackTrace:
       at Microsoft.FSharp.Core.OptionModule.GetValue[T](FSharpOption`1 option)
       at <StartupCode$FSharpScratch>.$Print.main@() in C:\Projects\Program.fs:line 28
  InnerException: 

Oops! So, you still have to match on option discriminated unions for none which means you are still checking for some sort of empty thing. Instead of having

if(x != null){
}

You start having

match x with 
 | Some item ->
 | _ ->

I’m not saying matching is bad, just saying that its wrong to assume you get no exceptions since there are fewer nulls.

A safer design pattern is to use the maybe monad, which can easily be built into every object type in C# using extension methods. I also like the get and getOrElse pattern that Scala has, meaning that you can either get and it’s a None type or get and if it’s a None return a default. Much safer.

Reason 9: I like to use design patterns everywhere

Design patterns apply to any code of any language. To just write flat code with no organization or thought to structure is going to break large apps. Those patterns exist, I’m sure, even in functional applications. And if they don’t, I’d be skeptical of their extensiblity and robustness.

You always want to segment out 3rd party libraries behind proxies, you want to hide how things are created with factories, and you want to make sure that you interface with abstract classes and interfaces when necessary so you can inject different implementations of things. In fact, the F# team have videos showing how to do certain design patterns with F#!

Reason 10: It’s too mathematical

This is something I’ve never heard people mention when complaining about functional, but maybe it’s true. I don’t know. I can’t comment on this one.

Conclusion

I think the article is hilarious and well written, and I am a huge proponent of functional languages. But I find that some things about functional do annoy me. One of the reasons I really like f# is because you can do imperative work when you need to. That said, I think the big language winners will be languages like C# and Scala that embrace functional paradigms but also let you build with imperative.

5 comments

  1. Passerby

    Hi Anton,

    This is a very insightful article! I always enjoy perspectives that challenge my own. I had to do some research, but hopefully it resulted in some convincing counter-counter-arguments.

    Reason 2: I get paid by the line
    This reason is regarding those who prefer programming in the imperative style. Trust me, there are those who prefer the original code, for loop and all. To illustrate my point, here’s a comment I recently came across: “I don’t know about you, but when I think of doing something 4 times . . . I think of a for loop.” ;)

    Reason 3: I love me some curly braces
    The author specified that he was talking about the ML family of languages.

    Reason 4: I like to see explicit types
    What does the first half of your argument have to do with preferring explicit types? The second half is just a matter of becoming familiar with the order of operations. Most times if you mess up it will result in a type error right away. If you’re concerned about ambiguity you can always use parenthesis and explicitly declare the types.

    Reason 5: I like to fix bugs
    Type errors are that big of an issue? They can be handled and avoided fairly easily. Either way, isn’t it worth dealing with that part of a functional language if you get less bugs?

    Reason 6: I live in the debugger
    In functional languages it’s much easier to make illegal states unrepresentable, catching more errors at compile time.

    Reason 7: I don’t want to think about every little detail
    There are many instances where you are forced to handle edge cases. When pattern matching you have to handle all possible results. For your option example, you have to handle the case when the result is None.

    Reason 8: I like to check for nulls
    Option.get is unsafe, which is precisely why its use is discouraged. Fortunately, there are idiomatic ways to safely handle options. Sadly, this is not the case for nulls. Also, options are opt-in, meaning you only have to use them when necessary. Nulls, on the other hand, are attached to every reference type, whether you want them or not. The maybe monad could be helpful, but it’s still not optimal. Either you have to trust that people will no longer use null, or you still have to validate every object, even ones that you don’t expect to be Nothing. It’s possible to mess either up and not get an error at compile time. Isn’t Scala’s get just the same as Option.get? And getOrElse is defaultArg in F#.

    Reason 9: I like to use design patterns everywhere
    Design patterns are used to address design problems. One could argue that design patterns are less common in functional languages because you run into less design problems. For example, the strategy pattern requires creating a class for each strategy, while you could just use higher order functions.

    You’re grouping C# with Scala? Isn’t C# closer to Java? Granted, C# has added a lot of nice functional features, but it’s missing many crucial ones like immutability by default and non-nullability by default.

    • Anton Kropp

      Hey, thanks for the response! I agree with most of your responses, like I said, I was playing devils advocate :). When it comes down to it, I’m all for whatever gets to the code to be as short, readable, and bug free as possible. Functional is definitely a part of that end goal, but I don’t think its the only part.

      As far as the C# and Scala comment, that was mainly in reference to first class functions and lazy lists (and infinite sequences). I definitely agree about the non-nullability. What I wouldn’t give for an Option…

  2. Pingback: F# Weekly #17, 2013 | Sergey Tihon's Blog
    • Anton Kropp

      Kingsley, thanks for asking. Keep in mind I’m playing both sides of the field here. I guess the argument here is that by having everything be automatically general makes for confusing type signatures. Most of the time code isn’t general, and you have to specify some of the argument types of functions to be able to do work you want, so making it general just makes it needlessy complex to begin with. In general, the argument against making everything automatically generalized is readability and ambiguous type relationships (at first glance).

      That said, it kind of goes without saying that the goal is to make function signatures clean and easy to understand. It’s a smell when you can’t at a glance tell what it does by its signature. But, when everything is auto generalized that does promote you to write reusable and modular code. When done right its a huge bonus. When done wrong, deciphering type mismatches is really annoying.

Post a comment

You may use the following HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>