
Functional Programming Isn't the Answer (2014) - bertiewhykovich
http://degoes.net/articles/fp-is-not-the-answer
======
lelf
Sorry for trolling, but sometimes I just have to.

“Look at my intentionally non-thread-safe, spaghetti _imperative_
implementation. It sucks! So FP sucks.”

~~~
twblalock
You're missing the point.

The author used that example to demonstrate that just using FP languages is
not enough to ensure good code, because it is possible to write bad code in FP
languages.

Therefore, FP programming languages are not going to guarantee you good code
by themselves. They will only get you good code if you use them to write good
code. By the way, the author never said "FP sucks," and that's a very
uncharitable and unreasonable interpretation of the argument.

From the article:

> My view is that although FP makes it easier to write good code, just because
> something’s functional, or even “purely functional”, doesn’t necessarily
> mean it’s any good. Stated differently, as software engineers trying to
> improve our craft, we shouldn’t latch onto or defend something just because
> it’s “functional” or “purely functional”. Although it’s possible to write
> good code using techniques from functional programming, it’s also possible
> to write bad code. FP doesn’t protect us. We need a different measure for
> “good code” than just “functional”.

You may think that "FP guarantees good code" is a strawman argument, but it
isn't. A quick search of this site will turn up people who make that very
argument. It's useful to have a realistic corrective to the pseudo-religious
fervor of some FP proponents, and this article provides that.

~~~
the_af
But isn't that arguing against a strawman? Nobody is arguing that FP code is
automatically good by virtue of being written in an FP-oriented language.

For example, you can write a beautiful algorithm that solves the _wrong_
problem. No language will help you with that.

Or you can write a program in language X that ignores every convention or
practice -- you can write code that makes an extraordinary effort at
subverting the type system, for example -- but that's more of an argument
against your understanding of X.

I do agree with the author that FP, good as it is, is not _enough_. That's not
an argument against FP, it's an argument in favor of devising even better
tools, practices and the theoretical underpinnings for building correct
software.

~~~
twblalock
> Nobody is arguing that FP code is automatically good by virtue of being
> written in an FP-oriented language.

People do say that, so it's not a strawman.

> I do agree with the author that FP, good as it is, is not enough. That's not
> an argument against FP, it's an argument in favor of devising even better
> tools, practices and the theoretical underpinnings for building correct
> software.

The article was not an argument against FP. Why do you think it is? It is
simply an argument against a certain justification for using FP, namely that
it guarantees good code.

~~~
the_af
> _People do say that, so it 's not a strawman._

Who says this? No-one who is a serious practitioner of FP (as opposed to
someone who just reads about it in blogs) will tell you that FP automatically
guarantees good code. You'll hear that FP goes a long way at reducing some
common sources of errors, which is a pretty big deal, but automatically
correct code? No-one will claim that.

> _The article was not an argument against FP._

I didn't say the article was. Besides the clickbait title, and some dubious
assertions (and a poor example with QuickSort), I do agree with the overall
assertion that "FP is good, but not enough".

~~~
twblalock
> Who says this? No-one who is a serious practitioner of FP (as opposed to
> someone who just reads about it in blogs) will tell you that FP
> automatically guarantees good code.

No true scotsman fallacy.

~~~
the_af
The problem with the No True Scotsman fallacy is that it's impossible to apply
to the internet, where there is always someone out there who will claim
_anything_ about _anything_.

Therefore, in order to have a rational discussion about this, we must apply
some sort of filter.

I propose this filter: no-one who is an actual practitioner of FP (someone who
uses it to build software, not someone who just talks about it in the
internet) will not make the claim you mentioned, i.e. "FP guarantees good
code" (I'm quoting you verbatim).

I hope it stands to reason that it doesn't matter what people who've never
used FP claim about FP.

~~~
twblalock
It's still a fallacy. Being on the internet doesn't change that.

> I hope it stands to reason that it doesn't matter what people who've never
> used FP claim about FP.

That doesn't matter. An argument is not a strawman if it has been made by
anyone, anywhere, anytime, for any reason. You can't just filter out the
people you think don't count.

An argument is only a strawman if it is a false argument, e.g. one that has
been invented by the person who is arguing against it.

If you want to have a discussion about the merits of FP, feel free to filter
out whoever you like. But you said the argument mentioned in the article is a
strawman, and it isn't.

------
smt88
Title is a bit bait-y. A better title might be, "Functional Programming isn't
enough". tl;dr He says that FP is good, but you can still write bad functional
code (which is fairly obvious).

~~~
catnaroek
And you can write good non-functional code as well.

------
RcouF1uZ4gsC
I love how the author gives comprehension and composability as goals for code
over adherence to any dogma.

One interesting note is that the quicksort code in Haskel illustrates a very
important point about code evolution. Suppose you wanted to avoid the worst
case quadratic behavior. There is not a good evolution strategy that goes
through median of 3 pivot, median of 3 pivot with randomization, and
introsort. With the equivalent imperative code, the evolution strategy is
pretty easy by just adding a few if statements.

~~~
smt88
Comprehension and composability _are_ dogmatic.

There are people who criticize code that isn't concise enough, even if being
concise would make it harder to comprehend. Using lots of magic or overly-
terse languages are also valued by some people and not by others.

Composability is also something that is totally alien to large sections of the
OOP community. If you've ever dealt with a legacy PHP or Java framework,
you'll know what I mean. They heavily use inheritance over composability.

------
tracker1
tl;dr - You can write bad, hard to understand code in any paradigm (or
language).

That said, I tend to prefer functional patterns over OO... there are times
where OO/classes as a syntax expression is easier, I would say in most cases,
it isn't. It really depends on what you're trying to create, where and how.

------
LeanderK
While i could criticise some points and recognise others, i want to add the
following:

Function Programming is fun (mostly ;))

and i think even in corporate setting that is a valid argument. It may not the
the most important, but it is one nonetheless. Happy developers are more
productive in my experience.

------
wcrichton
I also find it strongly problematic that functional languages with
customizable infix operators (e.g. Haskell and OCaml) have lines of code like
this:

    
    
      whileM ((<=) <$> readLeftIdx <*> readRightIdx) $ do
    

And, from what I've seen in a few Haskell codebases, this isn't all that
uncommon. I refuse to believe that there's not a better way to express these
kinds of programming patterns. I recognize that applicatives, functors, etc.
have their value in creating well-engineered interfaces, but when they
manifest themselves in obscure operators, it feels self-defeating.

~~~
catnaroek
Do you really think it would be clearer to write

    
    
        cond = do
          li <- readLeftIdx
          ri <- readRightIdx
          return (li <= ri)
        
        -- ...
        whileM cond $ do ...
    

Personally, I don't think it adds much clarity. It just makes the code more
verbose.

~~~
timmytokyo
As someone who doesn't know Haskell, I can understand your version. I had no
idea what the other one does.

Maybe that's not a reason to avoid the more concise syntax, but a language
that is more comprehensible and intuitive to people unfamiliar with it is more
likely to capture mind-share.

~~~
catnaroek
Both versions are equivalent. The reason why you didn't understand the
original version is that, as you say, you don't know Haskell. But, if you
learnt Haskell, the original version would've been just as easily
comprehensible.

~~~
timmytokyo
I get that and agree with you. My point is that it's easier to evangelize for
a language that is intuitive and comprehensible with only limited exposure to
the language. Languages like go are a good example of this. If you've ever
done any programming, you can read go code and know what it does. even if
you've never written a single line of go code in your life. I'd say Elixir, a
functional programming language, is also good in this regard.

~~~
AnimalMuppet
Interesting. I wonder to what degree that influences language popularity.

I suggest the following plausible points:

1\. Nobody is born knowing a programming language. Languages have users to the
degree that they convert non-users into users.

2\. Sometimes people are forced to use a language for a job (which is, I
suspect, somewhat common, but less common than hiring someone who already
knows the desired language). Others are forced to learn it to pass a course.
Other than those cases, though, languages convert non-users into users in
proportion to how much a non-user gets rewarded for the first attempts to
write code in that language. This was one of the virtues of BASIC, for
example. I picked up BASIC from the TRS-80 manual in an hour, and was writing
code that afternoon. I got immediate feedback (we didn't call it a REPL on
BASIC, but it had one). You could just _read_ BASIC code.

But if I have to learn a mathematical notation, or a 1000-page book, in order
to be able to do anything that I want to do, I have to _want_ to learn that
language more before I'm going to _bother_ to learn that language.

Other than university people, Haskell seems to attract people who have been
around the block enough times to know why they might _want_ to learn FP, and
therefore who care enough to make the effort.

------
ivanjr0
Question: I totally agree that the second version is much cleaner. But, how
does the performance of the two compare in Haskell?

"Premature optimization is the root of all evil" and all that. But, a sorting
routine is something everyone would expect to be as efficient as possible,
right? (Maybe it was just for the sake of the example?)

I heard 1 or 2 things about Haskell being lazy, etc. Does it makes the second
version less inefficient than it looks?

------
threatofrain
I think it's a bit of a mismatch when people talk about maintainability and
comprehensibility, but only on very local levels, and the examples they bring
up are also very local.

System maintainability and comprehensibility owe more to architecture than to
local readability. It doesn't matter if local code is completely functional,
or if Scala secretly uses imperative algorithms underneath. What matters is if
the code module / division / unit is keeping its contract or API with respect
to the rest of the system, because if it is, then you can actually hope to
comprehend your system at a useful level of abstraction. Local vs system
comprehensibility are not in tension, but an unfair amount of focus has been
placed on local clarity.

So a question I'd have is what architectural solutions to some problem domain
are natural and special to the functional style. To that end, it's more
interesting to see a skeleton project tackling a generic problem, as an
inspiration for a generic architecture, than to talk about whether it's
possible to write locally bad code with an FP language.

------
ridiculous_fish
> well, filter should really be replaced by partition, because filter destroys
> information; use of filter requires manual negation of the boolean predicate
> < p, which represents duplicated information content

More seriously, `>=` is not the correct negation of `<` due to NaNs. The code
as written will return a list with all NaNs removed, because NaNs are neither
greater than nor less than any other number.

It's interesting that an in-place sort that uses only comparisons and swaps
cannot have bugs like duplicated or removed elements, regardless of any bugs
in the algorithm or comparison predicate. The "shuffle via sort by random"
approach [1] is a nicely insane test case.

[1]
[http://javascript.about.com/library/blsort2.htm](http://javascript.about.com/library/blsort2.htm)

~~~
wereHamster
You only have to deal with NaNs if they are valid values of the type the array
is made out of. In Haskell an 'Int' can not be NaN. Only types of the
'RealFloat' class can be NaN.

------
dhab
I agree with the author that blind religious devotion to a paradigm in not
healthy. I see FP and imperative as complimentary and we also see that
manifesting by FP idioms being baked into traditionally imperative languages.

Use the tool where it shines. Here is a doc that I have bookmarked for
Haskell: [https://github.com/Gabriel439/post-
rfc/blob/master/sotu.md](https://github.com/Gabriel439/post-
rfc/blob/master/sotu.md)

------
nilved
I don't understand this post. "With that said, brace yourself, and take a look
at my contrived, deliberately-obtuse strawman implementation of Quick Sort:"

~~~
Noseshine

        > I don't understand this post.
    

The point is, as the author states quite clearly, that it's not enough to say
"I wrote it in FP".

~~~
KirinDave
Which is of course a straw man. Find me a credible FP proponent that would
disagree with that point?

Yes, bad code is possible in any language or paradigm. We don't judge
languages by their worst examples. We never have expected FP to magically
solve all problems. Heck, this disclaimer exists in a slightly different form
even in the Backus paper!

I don't understand the point of this article either.

~~~
twblalock
> Find me a credible FP proponent that would disagree with that point?

What distinguishes a credible FP proponent from a non-credible one? This is
getting close to the territory of the no true scotsman fallacy.

If someone makes the argument, it's not a strawman. It doesn't matter if you
consider them credible or not. The author of the article responded to a real
argument made by real people.

~~~
KirinDave
Who? Who has made this argument? What actually is the argument? FP literature
is full of this admonition. FP papers constantly cite the primary benefits of
FP and it's not the functionality.

Who specifically is he arguing against? You cite "no true Scotsman" but we
lack even a false one to point to.

~~~
twblalock
You wrote:

> Find me a _credible_ FP proponent that would disagree with that point?

By including the word "credible," you are pre-emptively excluding some FP
proponents as not being "true" FP proponents. So you've already committed
yourself to applying the fallacy to evidence that anyone might present to you.

~~~
KirinDave
Let me actually commit a logical fallacy before I have to own up to one. Don't
worry, I'm sure it won't take very long.

But this pre-emptive strike thing is just a way to avoid providing any sort of
example, I suspect.

------
p4wnc6
I read the title and I think, "OK, FP's not the answer ... what's the problem
it's not the answer to?"

I think the problem is dysfunctional workplaces in general -- things like open
plan office dogma, "full stack" destroying the value held in specialization of
labor and compartmentalization, everyone trying to data mine "insights" that
are really serving political agendas instead of customers, users, clients, or
really even shareholders, and so on.

Writing good software systems inside a bureaucratic system constantly throwing
wrenches into the gears is _really_ hard, and just changing the language
paradigm, even if the paradigm comes with really great tools for correctness
and quality enforcement, generally isn't enough to overcome badly structured,
uncreative, un-turn-off-ably oversaturated with "collaboration" working
environments.

I love FP and hope I get to use it more in the future. I also love simpler OO
or even module-oriented programming with lots of mutation. You can write great
software in any of these paradigms, and you can have great teams.

But I think first we all have to unite and utterly refuse the horrible working
conditions pervading the industry. Put our debates about language or tool
superiority to the side and see if we can improve functionality and basic
humanity-affirming healthiness of workplaces and team structures.

------
gtf21
Despite the (somewhat heated) discussion below, this article is actually quite
pointless.

Having good tools does not make you a good craftsman, I'm not sure this is
contentious.

I don't think that anyone would argue that (good tools => good craftsman) is
valid. This isn't an argument against FP, just the statement that FP cannot
transmute a bad programmer into a good one.

------
catnaroek
“Strong types” (a term that hardly means anything, now that everyone and their
dog claims their favorite language is “strongly typed”) don't have anything to
do with functional programming. And the fact that algebraic data types were
originally used in functional languages is just a historical accident.

~~~
danbruc
Strongly typed was never really a useful description of any language. Eric
Lippert nicely points out [1] that descriptions like statically typed, type
safe and memory safe are a lot more useful when one wants to compare
programming languages than those fuzzy terms strongly and weakly typed.

[1] [https://ericlippert.com/2012/10/15/is-c-a-strongly-typed-
or-...](https://ericlippert.com/2012/10/15/is-c-a-strongly-typed-or-a-weakly-
typed-language/)

~~~
catnaroek
A more useful term is “sound”: does the type system protect the language's
basic abstractions?

(0) Standard ML: Yes.

(1) Java: No, but dynamic checks are used to patch the type soundness holes.

(2) C: No, and if you do anything wrong, may God help you.

~~~
danbruc
If you don't use casts, then you should come pretty close to soundness, at
least in Java. Array covariance is really the only exception I can ad hoc
think of. I am not really sure of C without casts.

~~~
kilink
What about null?

~~~
danbruc
You could say all Java reference types are sum types - either a real instance
of that type or a null reference. A function returning a string guarantees
that you either get a string or a null reference, I don't think that has
(necessarily) implications for soundness. But then again the theory of type
systems is not something I have a deep understanding of so I may actually be
wrong.

~~~
catnaroek
> You could say all Java reference types are sum types - either a real
> instance of that type or a null reference.

That's not true. There are two fundamental differences:

(0) Options can nest. Nullability doesn't nest.

(1) Options are manipulated by pattern matching (or direct application of
their induction principle, that is, Haskell's `maybe` function). In Java, you
just assume a nullable variable isn't null, and if it is, well, you get an
exception.

~~~
danbruc
That is certainly a limitation but I don't think that makes this view wrong. I
also can not have List<int> but that does not mean List<T> is not a generic
list type just because there are some constraints on what T can be.

~~~
catnaroek
In the vast majority of situations, when a Java programmer has a value of type
`Foo`, he or she expects it to be a non-`null` reference. Having a `null` one
is a possibility, but it's an error. Dictating that reference types are in
actuality option types is creating a gigantic mismatch between the
prescriptive semantics of the language and the actual model inside the heads
of programmers, which is the worst sin a language's semantics can commit.
Witness the flame wars about whether it would be a good idea to remove some
undefined behavior from C.

~~~
danbruc
That is certainly true, but the point was whether nullabilty has any bearing
on soundness. I just tried to argue that the fact that a reference may be a
null reference does not (necessarily) defy soundness - the compiler guarantees
that you get an instance or a null reference and that is exactly what happens
at runtime.

~~~
catnaroek
If you take the view that object references are options, then it's plain
unsound to manipulate object references as though they weren't options!
`NullPointerException` is just a dynamic plug on a static type safety hole.

This blog post (I'm not the author) explains the issue (item 3) very well:
[http://www.sebastiansylvan.com/post/language-design-deal-
bre...](http://www.sebastiansylvan.com/post/language-design-deal-breakers/)

~~~
danbruc
Maybe I (or you or both of us) have a wrong understanding of soundness. I
understand it as follows - the specification of a programming language (or its
implementation) is sound if the guarantees made a compile time hold at runtime
for all inputs. The guarantee for calling a method on a reference in Java is
that you either invoke that method or a NullPointerException gets thrown in
case the reference is a null reference.

After thinking about it again, unless you made an error in the specification
or implementation, every programming language should actually be sound. You
can always weaken your guarantees until they (trivially) hold at runtime. But
that actually worries me a bit, soundness suddenly seems not such a useful
concept. Maybe because I am wrong with my understanding?

Note that this also possibly contradicts what I wrote previously, I may not
have thought carefully enough about some of the things.

~~~
catnaroek
> Maybe I (or you or both of us) have a wrong understanding of soundness.

I'm using my own interpretation of “soundness = progress + preservation”,
originally due to Wright and Felleisen. “Progress” means “not getting stuck”
(a term is either in normal form or further reduces). “Preservation” means
that, if a term `t` has type `T` and reduces to a term `u`, then `u` also has
type `T`.

From a purely technical point of view, raising a NPE is a well-defined
execution step, different from getting stuck. But I contend that this is just
a technicality, and not really how a programmer thinks about his code. For
practical purposes, in most situations, throwing a NPE is reaching a stuck
state, and thus a violation of type safety.

~~~
danbruc
Which then brings us all the way back to the first comments - without agreed
and accepted definitions statements about programming languages may be way
more up for interpretation then one hopes.

------
abc_lisper
That rouge FP implementation would get negative credits in a good FP class. At
least, that's what our TA used to do.

------
hughw
To make your case, please don't set up one side as a "religion".

------
icalc
It's simple and effective. It's not design to answer everything.

