
A Bit of Heresy: Functional Languages Are Overrated (2010) - bertiewhykovich
http://www.benrady.com/2010/06/a-bit-of-heresy-functional-languages-are-overrated.html
======
bertiewhykovich
While the article is particularly focused on concurrency, I think that there's
a larger takeaway: namely, that language features are paraded around as,
essentially, features of the software that's written using them.

Functional languages may make concurrency easier. Does this mean that programs
written in functional languages necessarily do concurrency better? Absolutely
not.

Functional languages make many tasks easier. Does this mean that a piece of
software written in a functional language is necessarily better than a piece
of software written in any other language? Absolutely not.

And it's worth noting that functional languages, particularly pure functional
languages, are not without their drawbacks. Eliding the issue of the
fundamental mismatch between available computing hardware and functional
semantics, functional languages clearly have a serious usability problem.
Hundreds of Haskell projects are posted to HN every month, and virtually all
of them are either a) toys, b) highly academic, or c) attempts to evangelize
Haskell. The GHC is one of the largest Haskell codebases in existence, with
only a handful of competitors -- which says something grim.

Functional languages clearly have strong appeal to programmers. Functional
concepts are clearly useful. But what is the practical advantage of functional
programming? What meaningful project can I better accomplish in Haskell than I
can in Java, C, Python, or (heaven forbid) Javascript?

~~~
sgeisenh
The obvious example is a compiler.

Java, C, Python and Javascript all lack algebraic datatypes and pattern
matching, which make writing a compiler much easier. There are many additional
features of Haskell that make it especially good for implementing a compiler,
if you know what you're doing.

I guess that's cheating because a compiler is just a function from source
language to target language, so it is easily expressed in a functional
language. But it does answer the question nicely.

~~~
galacticpony
I believe you're mistaking "easy" with "convenient".

~~~
sgeisenh
How so?

Pattern matching makes for more readable code. And more readable code makes
writing projects easier.

It is also more convenient to write code using pattern matching, but that
isn't the primary benefit.

~~~
galacticpony
This is a superficial view of readability, focusing on terseness and perceived
syntactical elegance.

"True" readability is achieved through a consistent structuring of your
program, which has to conform to the expectations of the reader.

Programmers must first build this expectation from picking up patterns in the
code, which emerge no matter if you use pattern matching or an equivalent
structure using the available language constructs.

~~~
joesb
They all contribute to readability.

You can always write in assembly code, with all that consistent structuring
and conforming to expectation. But I'll takes writing in higher language with
"abstraction you can write yourself" any day.

------
cerrelio
The allure of easy concurrency is a trap. My functional programming experience
comes from Scala, and the building of systems usually ends up like this:

1\. Take an approachable problem and break it into composable functional units
- mostly monads and abstract syntax trees

2\. Try to write something useful to actually compose and execute these pieces

3\. Give up and use scalaz

4\. Forsake static typing

5\. Coup de grace - sprinkle in implicits for an extra "fuck you" to future
maintainers

6\. System is shitcanned - subsequent attempt uses Spark

The lesson learned is that most languages can be used to build a concurrent
system, but you're often better served by choosing the language with a
community that produces the most useful tools, frameworks and APIs for the
problem you're solving.

------
beala
I almost wrote a response arguing that this critique seemed shallow and
polemic. It doesn't show an understanding of the techniques it's criticizing--
it barely mentions them save for actors. Then I saw the date. This was written
in 2010. Given that, I can understand the author's frustration with learning
Haskell. Neither "Parallel and Concurrent Programming in Haskell" nor "Learn
You A Haskell" had been published yet [1].

I can't speak wrt the other languages, but I would encourage the author to
give Haskell another try. Both the tooling and learning resources have
improved dramatically in recent years [2].

[1] At least the print version of LYAH hadn't been published yet, not sure
about the online version.

[2] [https://haskell-lang.org/get-started](https://haskell-lang.org/get-
started)

------
kmicklas
This seems like less of an argument against functional languages and more like
an argument that functional languages aren't going to magically make your
program concurrent. Which is basically true. Despite crappy concurrency
infrastructure in most all languages, at scale the hard part is always making
the underlying algorithm parallelize well.

~~~
spion
The big win from FP languages isn't parallelisation, its the lack of shared
mutable state. Shared mutable state is the bane of all software: the more you
have of it, the less you are able to reason about your program's behaviour.
Its not an all or nothing thing (a little shared mutable state is better than
a lot), and the difficulty increase is probably not linear either; this is why
hybrid languages also help.

I don't know why the material for learning FP languages is so bad, but I would
guess its the math roots. Most math material is really bad in the sense that
it requires that you turn off parts of the brain that are normally useful and
helpful, but in the case of math are actually just harmful.

(e.g. the part of the brain that continuously looks for "real world" examples
of the patterns its seeing i.e. it tries to find X in "this looks like X".
Often times, there simply are no useful "real world" values for X and thats
okay)

My hopes are with Reason
([http://facebook.github.io/reason/](http://facebook.github.io/reason/)). It
takes all the best parts of OCaml:

    
    
      lightning fast runtime
      great realtime GC
      strict evaluation with predictable performance
      excellent module system
      algebraic datatypes
      objects and polymorphic variants
      compiles to JS cleanly via bucklescript
      not completely crazy about purity
      great type inference (there is *almost* no need to write down any types)
    

and puts a simple JS-like syntax on top of that.

With multicore support right behind the corner, I think it has a good chance
of taking over.

------
myrryr
Well, a lot of people say actors magically make your software concurrent, and
kinda they are right, and kinda they are not.

Each actor can be written as if the software is single threaded (just don't do
a blocking call!), and you do get concurrency out of it.

if your software can be modeled easily as actors then hey, that will work
well, and mostly you can.

Sometimes you can't and then it can turn to shit pretty quickly if you don't
know when that is the case.

Mostly though, I love functional programming because it makes mucking around
with data structures a lot easier.

~~~
bertiewhykovich
> Mostly though, I love functional programming because it makes mucking around
> with data structures a lot easier.

Can you talk more about that? My experience has actually been largely the
opposite: immutability actually makes working with data structures /harder/
for me. I'd be interested in hearing about any hidden advantages you've
discovered there.

~~~
deepGem
One such example is the Scala List data structure. It's fundamentally a linked
list but the built in routines for Lists such as zip, reduceLeft, map make it
easier to transform List elements. By easier I mean, you write less code to do
the same task as compared to an imperative program such as Java and without
side effects.

~~~
eudox
In that case, though, the immutability is a property of the operations rather
than the data. And you can have mutating versions of most of those operations
that alter the data they are working on, e.g. a mutating map.

------
Falkon1313
The article is all about concurrency, but to me that would just be a bonus if
it happens to be easier.

The main benefit that functional programming appears to claim is that you can
spend much less time resolving bugs like "I got home and my dog had turned
into a zebra for some reason!" Eventually tracking it down to the fact that if
a neighbor on the left changes a tire on a certain stretch of highway during
the week of a full moon, then their neighbor's dog will turn into a zebra. Not
for any rational reason, not because anyone intended that to happen, but
because no one happened to think to explicitly prevent it in the interactions
between shared mutable loosely-typed state and layers of indirection and
abstraction.

I don't know yet whether functional programming really helps with that, but it
sounds like FP would make it a lot more difficult to end up with situations
like that. It does however make me wonder what the annoying types of common
bugs are in functional programming that a future paradigm will try to
prevent...

------
hkjgkjy
Nothing overrated about it. Pure functions lack state. Without state, no state
bugs. State bugs make up a great share of bugs in this world. Thus your
program written in a functional language lacks those bugs.

Also just nice to have a = function, instead of having each class implement a
.equals() method (looking at you, java).

~~~
jeff_marshall
Yes, instead you have parameter passing bugs (what, it's not state if it's not
an implicit global variable?).

I suspect the bugs have more to do with what is considered an acceptable
design pattern (and resulting cognitive load) than the state-passing style.

~~~
nightski
Well not really in a language as strongly typed as Haskell. At least, it
provides you the type machinery to prevent a large source of these sort of
bugs. It's not the end of the story either, there are even much more powerful
languages than it which are still in infancy.

~~~
jeff_marshall
If you would have substituted coq (or even ACL2 with guards checking
enabled...) in the place of haskell I would agree.

hindley-milner style type checking is nice, but once you admit that things
like arrays exist (rather than just algebraic data types or cons cells), the
power of languages like haskell is in design patterns like map() and reduce()
rather than the type checker, IMO.

Having worked in functional languages with an imperative bent (esp. ACL2 in
the context of modeling microprocessors), you can get an awful lot of mileage
out of a good static proof strategy that can be instantiated over a known
design pattern (equivalence relations, map, reduce, etc) even when you pass a
single, huge, state object to all your top level functions.

~~~
codygman
> hindley-milner style type checking is nice, but once you admit that things
> like arrays exist

How are these related? Haskell has arrays. Arrays can be represented as
algebraic data types as well.

What is it you think Haskell has trouble representing?

------
spraak
So.. the author barely tried two functional programming languages and
concluded that they all aren't good for concurrency?

------
Jtsummers
Functional languages make concurrency easier (particularly the statically
typed ones, but also erlang thanks to its innate concurrency model) because
they generally encourage low coupling between components of your system.

OO languages have not done this as a language feature (it's touted as a good
thing, but is often ignored). It's too easy to call directly into another
class, or to add a method to a particular class rather than via an interface.
This means your system components know too much about each other and it's too
easy to get around problems by reaching into one area and directly managing
it.

OO languages or libraries implementing actor models allow you to design your
system in a more decoupled fashion. But, of course, this leads to issues with
performance.

That's to be expected. And there's a solution to this. Hide the
implementation. I have a database server, I need it to be fast but an actor
model implementation was too slow. Fine, I implement it in C on an exokernel
with direct access to the hard drive and the ethernet interface and it's
really freaking fast. But _everyone else sees it as an actor_. They don't need
to know the implementation details. They just need to know how to talk to it.
That that one component of your system wasn't implemented in a "pure" fashion
is irrelevant. The rest of your system can't reach into it directly and become
dependent on it (outside performance) in a way that makes your system fragile
by becoming too tightly coupled.

EDIT: Also, concurrency != parallelism.

Concurrency is more an element of system design, less an element of system
performance (though it lends itself to better performance). Parallelism is an
implementation detail and all about the performance (how many tasks can we do
at once).

Actors may or may not help with the latter. They certainly won't for
scientific programmers, on their own, because they're likely too heavyweight
in most implementations. Instead you want something like what OpenMP or MPI
offer. Some functional languages allow you access to similar parallel
computational models. But that, again, is separate from concurrency.

------
marcosdumay
Well, that's a bummer, but you can not really understand it until you actually
learn one of those languages. And yes, learning the first one is a pain.

Those languages will not save you from dealing with shared state and
synchronization bugs. They'll just give you tools you can use to reduce the
size of the problem to the minimum, and to organize what remains of it. It's
still you doing everything.

And there are those situations when you reduced the synchronization problem
into zero without noticing. Then you can simply tell the compiler to
parallelize it to you. It happens - not every time, but it isn't rare either,
mostly depends on your problem.

------
retrogradeorbit
It's not _just_ functional approaches that solve the concurrency problems, but
FP _combined with_ strong time modelling constructs. If you are just using FP,
but not any kind of STM, then you are still going to have difficulties.

Imperative languages have neither strong time constructs nor side-effect free
approaches. So you lose twice. But just getting rid of side effects is not
going to take you all the way to pain free concurrency. You need more.

------
rdtsc
> I haven't seen a cure yet that's better than the problem.

Ok, I have. Been using Erlang in production, full time. I see benefits every
day:

* State manipulation is clear and explicit. No giant class intances that somehow ended up in a funky state. No trying to figure out which inhereted class or which sequence of 50 method call got it into that funky state.

* Immutability by default for data. I have spent too many days tracking down Java and C++ share memory concurrency issues to want to do that again.

* Already built in high level concurrency primites -- message passing, lightweight processes, ability to send a message to a remote node, state machine modules, event distribution (listener-observer thing in other languages).

* Fault tolerance -- very well defined and first class error handling. Errors are confined and isolated to parts of the system and are guaranteed to not spread. With C++ you don't know if your crashed thread, hasn't scribbled over memory and restarting just that thread is safe. The only safe thing is to restart the whole service. Except there are 100k connected clients to it, now you have downtime and nowyou have long hard road ahead debugging the problem.

* Hot code replacement. Yeah seems like a gimmik and many users I understand don't use it to upgrade as regular mechanism. But it is really invaluable to quickly fix a patch on a running system for a particular customer with 0 downtime, or to hotpatch a function to get better logging.

These might seem like academic generalities, but I've seen in practice these
translate to a better uptime, lower ops load, less engineers salaries needed
to be paid, less code (Erlang is like a DSL for distributed system, and it
just happens that systems we use today are distributed). At the end of the day
all that means money saved and more bread on the table at the end of the year.

> I think the downfall of some of these languages is that they get wrapped up
> in being "functional" rather than focusing on solving a particular class of

Not true for Erlang. When Joe and Robert were writing Erlang, I understand
they never got wrapped up in academics papers and didn't even know about
"actors", didn't explicitly went for "functional first". They went for soft-
realtime and fault tolerant. "Functional" part with immutability just fell out
of "fault tolerant" requirement.

> You can have an actor model architecture without using a functional
> programming language. You can avoid shared state in any modern language
> (even JavaScript). These things are not synonymous with functional
> programming.

You can. You can even have live code replacement in Java. But it is not the
same. You can slap a thread safe queue + a thread and call your framework a
"an Erlang killer" but unfortunately concurrency and message passing is
secondary to fault tolarance. That shared state and that thread cand still use
a library which scribbled over memory of anothe thread.

> the cases where a functional programming language are really the best
> solution to the concurrency problem are, in fact, rather rare.

Maybe they are rare. But it is really the secret sauce when they match. Yeah,
you have to type . instead of ; at the end of statement and have to use
recursion in intead of for loops. If that is hard or insurmountable, don't use
it. Use something Java, C++ or Go. Those can get stuff done to.

Even better can give Elixir a try has the same VM as Erlang and a really
friendly community. So if typing . intead of ; is hard and seeing capitalized
variables looks irritating, give that a try.

------
knucklesandwich
I think the context of this post being 6 years old is important. While I don't
buy the argument that functional programming languages are more complex, I
think there's certainly been a long-standing problem of good pedagogy in the
functional programming community. I'll also add that I don't think the
languages themselves have been where pedagogy is lacking per se as much as
much as with regard to teaching functional design. Ask yourself how many
tutorials you've seen about pattern matching versus the number of monad
tutorials you've seen.

I think this has gotten somewhat better recently and there are better
resources available for understanding some of these functional design patterns
(more literature and likewise better guidance about how to write tutorials). I
also think lately there's been a couple of languages I'll call "bridge
languages" to purely functional programming languages (particularly scala,
rust, and swift) that have been particularly good at weening object-oriented
programmers into functional programming through a translation of functional
concepts into OO or otherwise imperative programming. Arguably these languages
are some of the most complex, but surprisingly I've found them to be pretty
good for learning functional programming (I'm definitely biased as someone who
came from scala to haskell, though).

There's still a pedagogy gap though, and I would say there's not a lot of
accessible literature on things like lenses, monad transformers, free monads,
etc. that ought to exist to make purely functional code understood by a wider
audience. In fact I'd argue that design patterns in purely functional
programming are still not totally settled. There's still a good amount of
discussion about the best way to achieve things like composition of effects,
testable code, module design, etc. Haskell in particular is still a test bed
for ideas about how to achieve some of these things, though languages with a
more lax view on explicit effects tend to be more settled on these things.

In any case, none of this should scare you away from functional programming.
If you're concerned that the benefits seem unclear, I'd advise you to take a
look at Simon Marlow's _excellent_ Parallel and Concurrent Programming in
Haskell [1] for a look at what purely functional programming brings to the
table WRT concurrency. Yes, you can do IPC in any language, but this only one
model of concurrent programming (and one that does not eliminate the potential
for deadlocks). Purely functional programming offers many tools in the
concurrent programmers toolbox such as data parallelism, software
transactional memory, message/channel concurrency, etc. that are all hard to
achieve without sharp corners in imperative languages.

[1]:
[http://chimera.labs.oreilly.com/books/1230000000929](http://chimera.labs.oreilly.com/books/1230000000929)

