100 comments

[ 3.0 ms ] story [ 160 ms ] thread
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).
And you can write good non-functional code as well.
(comment deleted)
(comment deleted)
Overall though it's a good article making a good point and doing so quite well IMHO.

It's like science research: The point of individual studies or here, articles about something, isn't to finish the topic once and for all in one stroke. The criticism of either (studies or articles) often seems to be based on attempts to find what a study/article does not provide instead of looking at what it does. Obviously, the stuff not in it far exceeds the stuff that is, so I see a lot of such criticism missing the mark.

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:"

    > 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".
Well, he really showed all those strawmen, didn't he.
I don't think that "FP as religion" is a total strawman. I've seen people who seemed to have pretty much a religious fervor for FP. On this site, even.

"Writing it in a functional language means it's FP" might be a bit more of a strawman, however.

It is a common conversational tactic to dismiss profession interest and enthusiasm as "religion."

I doubt Degoes would appreciate the phrase "religion" used as a disparagement to defend his work by the way. He's shown he's willing to take a lot of flack to defend religious people's dignity and needs.

Well then, it's a good thing that the author of the article did not simply dismiss it, but rather engaged with it and provided examples and argumentation.
Um, Degoes actually raised the topic. From his article:

> Some people have fallen so much in love with FP (it’s hard not to!) that their views are almost religious. Hence, what I refer to as the religion of FP. The problem with any religion is that dogmatism can be blinding.

I suspect that you didn't read the article, or at least merely skimmed it.

No. I was bring snide. Maybe it was too subtle to be a good joke. It was written a bit hastily, I guess.

Degoes fought bitterly for Lambdaconf to support people who hold unpopular and racist views, often citing the social ostracism of the religious members of the community as his reasoning.

But of course he casually dumps on them in this article and equates "religion" with a lack of logic. So really it was about supporting his DE chums and not his poor religious friends.

But that's not really the topic of this discussion.

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.

> 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.

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.

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.

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.

(comment deleted)
(comment deleted)
(comment deleted)
“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.
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-...

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.

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.
> If you don't use casts, then you should come pretty close to soundness, at least in Java.

If you forbid casts or reflection, then you're throwing away much of the Java ecosystem.

What about null?
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.
> 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.

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.
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.
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.
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...

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.

> 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.

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.
> 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.

That's technically correct. But it's like the joke about the mathematician who can only provide technically correct but otherwise totally useless answers.

> But that actually worries me a bit, soundness suddenly seems not such a useful concept.

Indeed, if you define soundness that way, it's a useless concept. That's why I refuse to accept a semantics that doesn't match how programmers actually think about their programs. I consider it undefined behavior to dereference a null pointer: the language implementor might trap the error to make debugging easier, purely out of goodness in his heart, but the program itself remains meaningless until it doesn't dereference null pointers anymore.

> Maybe because I am wrong with my understanding?

Nope, you're not wrong.

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.
While Haskell does have customizable infix operators which can be overused and abused, that line isn't a good example of the problem in my opinion. The three infix operators there ($, <$>, and <*>) are all extremely common in Haskell code and are built-in to the language.

Every competent Haskell programmer will be intimately familiar with those three operators.

The problem arises when new libraries introduce new infix operators. I find this highly annoying and almost always consider it a code-smell. So honestly we're in agreement in general, just not with that particular example. I consider those to be basically the "arithmetic operations" of function application. Considering the entire language is based on function composition and application, I don't think it's a bad thing for an economical notation to be used.

While they may be common, they're not actually built into the language. They're just regular functions.
(comment deleted)
Sure, they're library functions, but they're also specified in the haskell 2010 spec. That makes them built-in from a practical perspective.
The example you quoted is probably the very basic of operators and idioms being used. These operators have standard meaning in terms of fmap and applicatives.

But, I agree with you that there are obscure operators scattered around the code base and I have to keep jumping away from that point to lookup what the particular operator means in that context.

What makes an operator "obscure"? Can functions be obscure too? What makes a function "obscure"?

You don't like punctuation in your identifiers. It's a a fair objection, and a common one for people whose native language uses a small alphabet (as opposed to CJK and other languages that are ccustomed to using wide range of glyphs)

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.
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.

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.
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.
Maybe I'm weird, but I make sense of code as much as possible from static information, without actually running it in my head:

(0) Type signatures (obviously).

(1) Abstraction boundaries - which implementation details are not exposed outside of a module, class, whatever.

(2) Documented preconditions, postconditions, invariants and algebraic laws.

(3) In impure languages, the scope of mutable variables and effectful procedures.

> My point is that it's easier to evangelize for a language that is intuitive and comprehensible with only limited exposure to the language.

I have to admit I don't care much for technological evangelism. I prefer to let technical merits speak for themselves. To be more precise, I'm okay with others showing me technologies I might like (“good” advertisement), but I'm not okay with others trying to convince me to like whatever they like (“bad” advertisement).

> If you've ever done any programming, you can read go code and know what it does.

That hasn't been my experience, and I've honestly tried. If you show me a heavily concurrent Go program, I can't easily tell on a first read whether the program is correct, or, if it's incorrect, where the problem might be. Just because I can run a program (in the sense of myself acting as the interpreter), it doesn't mean that I actually understand its design. :-p

> I'd say Elixir, a functional programming language, is also good in this regard.

I haven't actually tried it, but the fact it's designed to be similar to Ruby doesn't inspire much confidence on me. I don't mind Ruby's syntax, but the impossibility to make sense of Ruby code other than by trial and error is incredibly frustrating.

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.

But your point is nothing more than a tautology. "You understand something you chose to learn and understand"

The other version, however, is more readable to anyone who isn't a software developer by profession, but has been exposed to at least some code or even pseudo-code. It's the difference between turning people off of programming versus keeping them interested.

> But your point is nothing more than a tautology. "You understand something you chose to learn and understand"

It's not a tautology. Especially in a language like Haskell, where it's very easy to write incomprehensible programs, even to experts, by following “common sense” principles.

> The other version, however, is more readable to anyone who isn't a software developer by profession, but has been exposed to at least some code or even pseudo-code. It's the difference between turning people off of programming versus keeping them interested.

I don't want to turn anyone off, but I also don't want to make an effort to accommodate people who aren't willing to learn. If other people like programming for what it is, that's great! If they don't, well, that's okay too. To each their own.

Just because something is unfamiliar doesn't make it worse. Operators, once learned, can greatly improve readability of code.

If you had never seen +,-,%,• before, simple math statements would be inscrutable. But, once you take the (short) time to learn them you can express things much better than if you had to write `add`, `subtract`, etc everywhere.

> If you had never seen +,-,%,• before, simple math statements would be inscrutable. But, once you take the (short) time to learn them you can express things much better than if you had to write `add`, `subtract`, etc everywhere.

A point that is particularly poignant to anyone who has ever dealt with code using custom numeric types in a language that doesn't let you define custom operators (or overload operators).

> Do you really think it would be clearer to write

Absolutely, anyone with programming experience can make sense of the longer (and still quite terse) version.

    whileM ((<=) <$> readLeftIdx <*> readRightIdx) $ do
is literally symbolic soup without knowing the context and language in which it is written. For example `(<=)` could be less than or equal, but then again, given all of the other operators, who knows, might pipe output into whatever's calling `whileM`.
My point is that a Haskell programmer would be able to make sense of either version equally easily. Chaining applicative actions using <$> and <*> is a very common idiom. If you don't know it, you don't know Haskell.
> Absolutely, anyone with programming experience can make sense of the longer (and still quite terse) version.

Is that true? I'm skeptical.

Why is the "<-" symbol clearer to you? In particular, why did the commenter above write

    li <- readLeftIdx
instead of

    let li = readLeftIdx
?

I don't think anyone with programming experience could correctly answer this question unless they also have some specific Haskell experience.

What exactly does "return" do? I don't think someone without Haskell experience has any chance of answering that correctly, because it does not have the same meaning that it does in most languages. You might be able to make a reasonable guess, but you're unlikely to actually get it right.

I think once you're at the point that you can completely understand and answer these questions, the odds are high that you'll appreciate a more economical syntax for these operations.

The repetion of `<-` for both li and ri seems to imply setting the variable on the left with the value on the right.

As for return, even if the semantics of return are different in Haskell the keyword itself is quite common in programming languages. Therefore, given that it's the last statement one can assume that it returns the result of `(li <= ri)`.

If you know Haskell then the one liner is likely obvious. If not, not at all, there's a lot of domain specific knowledge required to grok what the one liner actually does. The long form version at least provides some guide posts to work off of.

> The repetion of `<-` for both li and ri seems to imply setting the variable on the left with the value on the right. (...) one can assume that it returns the result of `(li <= ri)`.

No, that's not what it's doing. It's not imperative assignment. It's not even a `let` binding. Against popular belief, you actually need to learn a programming language to make sense of code written in it.

> If you know Haskell then the one liner is likely obvious. If not, not at all, there's a lot of domain specific knowledge required to grok what the one liner actually does.

s/Haskell/C/, s/one/hundred/g

Even if `<-` is not assigning a value, within the monadic context bind is doing just that, assigning result of the expression on the right to the variable on the left.

The point is that everyone can take a guess as to what's happening in the long form version.

> Even if `<-` is not assigning a value, within the monadic context bind is doing just that, assigning result of the expression on the right to the variable on the left.

What about monadic actions that may return less or more than one value? For instance, parsers for languages with non-deterministic grammars, that return all valid parse trees, rather than just the zeroth one.

> The point is that everyone can take a guess as to what's happening in the long form version.

And that guess would often be wrong.

I don't deny that there's value in having someone with no experience with the language be able to make some reasonable guesses about what the code does. It's hard to argue that this could ever be a bad thing.

At the same time, I think it's less valuable than it might seem. A vague, cursory understanding is enough in this case to get a gist of what's happening, but it's not enough to actually work with the code, except via luck. The intuition you describe works okay in this case, but it would fail somewhat spectacularly in other cases, for instance with the non-determinism of the list monad.

the operators $, <$>, <> in haskell are like the pointer related stuff in c. Everybody knows what the difference between foo and foo is and what the arrow operator (->) does. Its just really, really common so you pick it up pretty quickly. I know its a bit overwhelming at first, but thats not because it is difficult, but because the paradigm is really different and it needs these quick but obscure operators to not drive developers insane.
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.

Sorry for trolling, but sometimes I just have to.

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

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.

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.

> 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.

> 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".

> 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 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.

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.

Also empirically disproven, even on HN.

It seems there's a phase that some FP programmers go through where they have a zealotry that requires convincing the masses of the glorious wonders of FP. For the most part, they seem to either outgrow the "FP is the One True Way" zealotry, or at least learn to keep it to themselves for the most part.

In fairness, I'd say that I've seen it on HN less in the last year or so than I did before that.

> Also empirically disproven, even on HN.

Can you provide an example of someone who claims "FP guarantees good code"?

Word for word? No.

At least implying that? Probably not at the moment, given how many different ways it can be stated, and given that I've forgotten what the search engine for HN is. (There is one, but I failed to bookmark it when it was called to my attention.)

But I tell you that here on HN, I personally have argued against views that in my view are at least similar to that. Similar enough that you'll think they are the same? I can't answer that, partly because of my own forgetfulness of the exact details, partly because I don't know what you'd consider similar enough to count.

The search engine is on the homepage, at the bottom.
> You're missing the point.

I don't think I am. That code deliberately uses Haskell’s low-level features. It’s like saying, “hey, high-level languages don‘t even save you from segfaults! Just look at my purposely crafted low-level code in a high-level (but not dead, so it has low-level interface) language that access memory at 0.“

You are still missing the point.

The author argues that using FP does not guarantee good code.

All that is necessary to prove that is to demonstrate bad FP code, which he has done. Only a single example is required to disprove a universal assertion.

> It’s like saying, “hey, high-level languages don‘t even save you from segfaults! Just look at my purposely crafted low-level code in a high-level (but not dead, so it has low-level interface) language that access memory at 0.“

If someone argued that high level languages save you from segfaults, your statement would be a valid argument against that, just as the article has provided a valid argument against a particular belief about FP.

> A quick search of this site will turn up people who make that very argument

We should make an unemployable list and put these people on it.

If that had been his point, your reply would have been relevant. Instead, it's just trolling.

His point was that writing in an FP language doesn't mean that you're not writing trash. An FP language (or even an FP orientation/mindset) isn't enough. It may be helpful, but it's not enough. Haskell won't save you, and neither will using higher-order functions.

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.

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.

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.

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?

(comment deleted)
That rouge FP implementation would get negative credits in a good FP class. At least, that's what our TA used to do.
> 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

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.
To make your case, please don't set up one side as a "religion".
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.

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

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.

It's simple and effective. It's not design to answer everything.
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.

(comment deleted)