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.
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)
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.
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?
Some of the practical aspects that favour functional programming lies in parallelism, not concurrent programming. Parallelism as in multi threading. It's an order of magnitude easier to write parallel programs in Scala than in Java. I don't mean a simple hello world but real world applications which will involve transactions and potential deadlocks. Agree Yes, the parallel constructs in Scala work with Java threads underneath, but you write a lot less code.
I don't think Functional programming makes any task easier. In fact I'd argue the opposite, it's quite difficult to write functional code, to unlearn imperative programming. Once you gain mastery over FP, you do write less code. Reaching that level of mastery is an uphill task, it has been for me at least.
So there are some domains where that parallelism easing can really work out well, when dealing with jobs over thousands of cpus, or gpus etc. It can however get almost comical in the case of desktop/single server applications, where you have maybe 4 or 8 cpus available. Because you can write something nice in F# or Scala, and run it on 4 to 8 threads, get a nice speed up. But the same code, written in an obvious manner in C, ends up auto-vectorized and/or auto paralellized to some degree and runs ~8x faster on just one core. Could be more, of much less, depends on many factors, but it is something to consider.
This isn't an inherent problem, but most functional languages need a lot of compiler/JIT work to make the parallel features a net performance win more of the time.
Not to mention that there are much better libraries for data-parallel programming available for C/C++, plus there are languages designed for data-parallelism (OpenCL, ISPC) and they're not functional either.
Untold amounts of energy (and dollars) must have been wasted on the "convenience" of writing some program in Python that scales over cluster of MapReduce nodes, without investigating how an in-core solution in a straightforward native implementation would have performed.
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.
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.
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.
Algebraic data types and pattern matching are equivalent to having a class hierarchy and implementing the same method for each class.
The idea that Haskell or OCaml or SML are specially suited for compiler writing is a nineties truism that is thrown around a lot, never backed by any evidence whatsoever.
Not true: class hierarchies aren't closed, which means you can't check exhaustivity of cases (or you can, but you can't guarantee exhaustivity with dynamically loading code and you generally destroy your chances of having incremental compilation). That's a huge advantage of patterns and ADTs. Scala offers a sort of compromise to this with "sealed" traits/classes, but this is pretty uncommon (and doesn't provide any advantages over ADTs IMO). Also in practice, I think there's a huge advantage to languages with explicit pattern support in terms of how much boilerplate gets cut out.
Also I'm not aware of any research into why they're better (I'm not a PLT researcher, so I wouldn't be aware of papers like that), but a huge amount of languages are originally written in ML derived languages. Here's a list of ones just written in haskell: https://wiki.haskell.org/Applications_and_libraries/Compiler...
Apache Spark is one of the most popular analytics frameworks for large datasets, and would be substantially harder to implement in a non-functional language.
The appeal of Haskell for me isn't necessarily that it makes everything easier. It does make a lot of things easier, but also many things become more difficult. No, the appeal of a purely functional language with an expressive type system is that it helps me write correct programs. I'm not claiming that Haskell will magically make code free of bugs, but in my personal experience when I write Haskell I make far fewer mistakes.
I can't really make the same claim of other functional languages that I've used, especially those with dynamic type checking.
There's really not much point in arguing about programming languages, though. People like what they like. I knew a guy that thought Fortran was still the greatest programming language because it's the fastest. Then you have the embedded device guys that will be using C until they die despite a mountain of valid criticism. Then there are the Lisp folks who are smart enough to not need help from a compiler to write programs. Etc. The thing is, these people are usually really productive with the tool that they've mastered, so of course they're all going to have anecdotal evidence of their language's superiority, just like me.
This is my own thesis and not really researched in the sense that I can point to any other articles, but it's what I've synthesized from my time as a programmer and studying CS.
There are many camps of programming thought, but I'm going to distill them here to two.
Mechanical: This is, fundamentally, the Turing machine. Assembly, C, Fortran, others, sit in this camp. They're very much concerned with the mechanical act of computation. We don't have high level abstractions (ok, we do, but they're very limited). This doesn't bar us from performing incredible feats of computation or computational thinking. But it does make some things more difficult, particularly the task of viewing the code and understanding what it was intended to model. There's a strong disconnect, then, between our high level concept of the system being developed (specification, requirements) and the low level implementation.
Mathematical: This is lambda calculus. Haskell, erlang, lisp, others, sit in this camp. They're more concerned with mathematical or symbolic understandings of computation. They tend to be less concerned with low level performance (though may still achieve it quite well) and low level implementation details (I don't care if my custom type BTree is implemented as an array under the hood, I care that it behaves in a way that satisfies its definition). This makes translation from specification to implementation almost trivial. With some care, you can practical document both at once with languages like Prolog or Haskell in a single document. The drawback is primarily one of performance. But that can be handled via implementations (FFI, smarter compilers, access to low-level primitives or "unsafe" code).
Hybrid: I'm not sure where to place OO, so I'm going to place it here. In theory, most OO languages should be able to deal with both quite well. In practice, they have historically sat too close to the mechanical side. Or abused the mathematical side to create horribly inefficient models. They offer the, theoretical, abstraction levels of functional languages, but the procedural components (the methods defined in the classes) to have fine control over performance.
You can, technically, achieve any computational task in any language. But the levels of abstractions they provide allow certain domains of problems to be solved almost trivially.
I could make a functional model of a radio system. A functional language or an OO language makes this effort almost trivial. I don't care about the underlying performance, this is a model so I can demonstrate sending messages between two radios and between components within the radios. I need to know that the specification itself is consistent, and, as a bonus, this model can be turned into a simulator for situations where we want more radios than we physically have or can set up in the lab.
Implementing this in C, from a performance perspective for the latter part, makes some sense. But it makes demonstrating that the high level specification and requirements are satisfied and implemented almost impossibly difficult.
I would argue the same way from experience. Functional programming languages tend to provide an abstraction that you could have easily built yourself by focusing on the constraints that are relevant to you - not those constraints some language theoretician has decided for you.
It's a big ask to suggest reading through this, but reading http://blog.ploeh.dk starting from around about 2013 is illuminative.
Seemann basically wrote the book on dependency injection in .NET, and has been a pretty big evangelizer for clean object-oriented design. Some time during that year, though, he discovered F#, and from 2014 on his blog has been increasingly about functional rather than object-oriented design. By following the transition you get a pretty good luck at things (ML-style) functional programming does well, since it's basically a journal of a serious expert in object-oriented design learning switching over to the functional paradigm.
Yes, exactly. When I hear people brag about how functional languages don't allow side effects, I always wonder, what's to stop you from just writing your program without side effects in an imperative language? And occasionally it will be more natural for your program to have side effects than not.
I have a similar gripe with type systems. Types are restrictive metadata - they never reduce a program's complexity, and sometimes they increase it.
I work in embedded systems. Our primary programs operate on messages, binary data. Type less like you suggest would mean always grabbing contents by way of offsets. Then never knowing if they are ints, doubles or what.
Even the basic type infrastructure of C is useful here. We map the blobs to structs. We can then grab the contents and manipulate them as ints, doubles, or strings. Removing types would make this a horrific task.
I wrote a comment about this below, but I think its fair to say that pedagogy has been a long standing problem in the functional programming community. A lot of the criticisms leveled at haskell for producing "toy software" are better targeted at the poor pedagogy that has plagued haskell for so long.
I think there's lots of examples of production grade libraries in haskell (stm, async, lens, quickcheck, attoparsec, warp, etc.), some of which are effectively best in class among all languages. Likewise, I think there's a lack of acknowledgement of how many stellar compilers are written in haskell. Rust, Perl 6, Elm, Purescript, etc. were all either initially written in haskell (before they become self-hosting) or continue to be. This shouldn't be downplayed... a compiler is one of the most complicated kinds of software you can write, and writing a compiler in a language shows a lot about that language's versatility (which is why a self-hosted compiler is something of a benchmark).
Lack of good resources for learning haskell for so long have incubated haskell as an "experts" language, particularly experts interested in PLT considering haskell extensions have been used as a PLT research platform. I think this is why you see so much evangelism for the language and relatively little industry use: its generally been inaccessible for people without a background in theory for a long time (and its not per se that the language is inaccessible so much as some of the scary sounding, alien seeming design patterns that are conventionally used). Generally I believe this is getting better, though the situation isn't perfect.
As for evidence that this isn't due to deficiencies in the language, I think its worth considering how many ideas from haskell have percolated to the broader programming language community. Promises in javascript, for example, are effectively monadic (technically they're probably closer to bifunctors, but they're definitely very influenced by monads in haskell). Ad hoc polymorphism is now embraced by languages like C++ and rust. These things don't strictly need pure functions to work in the host language, but they're design ideas and language features that were originally developed to benefit a purely functional style.
Edit: As pointed out below, I'm wrong about rust, it was written in ocaml.
I'm pretty sure Rust was originally written in OCaml, not Haskell. Unfortunately, I'm unable to find any references for this, as googling mostly leads to other comments (on HN and reddit) saying the same thing, and Wikipedia's footnote for the sentence stating this seems to be a reference for other parts of the sentence.
That being said, Rust itself is heavily inspired by Haskell; most notably, Rust's traits are pretty much equivalent to Haskell's typeclasses in almost every imaginable way, up to and including the way they can be derived for types.
Really? The last Haskell project I saw on the front page was a c to rust transpiler. Seriously cool stuff even ignoring the Haskell aspect of it. It has its place, just like other languages--on a pragmatic level it is easily the best language for parsing I've ever seen in my life.
A transpiler is a perfect use-case for a functional language. You're literally mapping from an input domain to an output domain. That's what a function is, so it all fits very well. It's when you have things that go outside of domain-to-domain mapping that functional starts to fall apart.
You provide nothing to back up your assertion that it falls apart. I have written image capture and 3D reconstruction algorithms in Haskell which interacted with webcams, OpenGL, networking, etc.. and never felt like it was "falling apart".
> if your software can be modeled easily as actors then hey, that will work well, and mostly you can.
I feel like "if your software can be modeled easily as X then hey, that will work well, and mostly you can" applies to all languages/styles, and if only people who like different languages and styles could just accept that this then we'd all get along fine. It's just a pity when people (not saying you're saying this) go "X is the BEST WAY TO PROGRAM and all other ways are STUPID and are for STUPID PEOPLE" and it ruins whatever point they were trying to make.
> 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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
Equality, like copying, is often more complicated than that. You often run into a problem where you need a domain-specific equality function for your objects.
Which is not to say that automatic derivation of generic functions is not useful, but it's not really a game changer.
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...
> 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.
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.
52 comments
[ 2.8 ms ] story [ 105 ms ] threadI 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/). It takes all the best parts of OCaml:
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.
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?
I don't think Functional programming makes any task easier. In fact I'd argue the opposite, it's quite difficult to write functional code, to unlearn imperative programming. Once you gain mastery over FP, you do write less code. Reaching that level of mastery is an uphill task, it has been for me at least.
This isn't an inherent problem, but most functional languages need a lot of compiler/JIT work to make the parallel features a net performance win more of the time.
Untold amounts of energy (and dollars) must have been wasted on the "convenience" of writing some program in Python that scales over cluster of MapReduce nodes, without investigating how an in-core solution in a straightforward native implementation would have performed.
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.
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.
"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.
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.
The idea that Haskell or OCaml or SML are specially suited for compiler writing is a nineties truism that is thrown around a lot, never backed by any evidence whatsoever.
Also I'm not aware of any research into why they're better (I'm not a PLT researcher, so I wouldn't be aware of papers like that), but a huge amount of languages are originally written in ML derived languages. Here's a list of ones just written in haskell: https://wiki.haskell.org/Applications_and_libraries/Compiler...
I can't really make the same claim of other functional languages that I've used, especially those with dynamic type checking.
There's really not much point in arguing about programming languages, though. People like what they like. I knew a guy that thought Fortran was still the greatest programming language because it's the fastest. Then you have the embedded device guys that will be using C until they die despite a mountain of valid criticism. Then there are the Lisp folks who are smart enough to not need help from a compiler to write programs. Etc. The thing is, these people are usually really productive with the tool that they've mastered, so of course they're all going to have anecdotal evidence of their language's superiority, just like me.
There are many camps of programming thought, but I'm going to distill them here to two.
Mechanical: This is, fundamentally, the Turing machine. Assembly, C, Fortran, others, sit in this camp. They're very much concerned with the mechanical act of computation. We don't have high level abstractions (ok, we do, but they're very limited). This doesn't bar us from performing incredible feats of computation or computational thinking. But it does make some things more difficult, particularly the task of viewing the code and understanding what it was intended to model. There's a strong disconnect, then, between our high level concept of the system being developed (specification, requirements) and the low level implementation.
Mathematical: This is lambda calculus. Haskell, erlang, lisp, others, sit in this camp. They're more concerned with mathematical or symbolic understandings of computation. They tend to be less concerned with low level performance (though may still achieve it quite well) and low level implementation details (I don't care if my custom type BTree is implemented as an array under the hood, I care that it behaves in a way that satisfies its definition). This makes translation from specification to implementation almost trivial. With some care, you can practical document both at once with languages like Prolog or Haskell in a single document. The drawback is primarily one of performance. But that can be handled via implementations (FFI, smarter compilers, access to low-level primitives or "unsafe" code).
Hybrid: I'm not sure where to place OO, so I'm going to place it here. In theory, most OO languages should be able to deal with both quite well. In practice, they have historically sat too close to the mechanical side. Or abused the mathematical side to create horribly inefficient models. They offer the, theoretical, abstraction levels of functional languages, but the procedural components (the methods defined in the classes) to have fine control over performance.
You can, technically, achieve any computational task in any language. But the levels of abstractions they provide allow certain domains of problems to be solved almost trivially.
I could make a functional model of a radio system. A functional language or an OO language makes this effort almost trivial. I don't care about the underlying performance, this is a model so I can demonstrate sending messages between two radios and between components within the radios. I need to know that the specification itself is consistent, and, as a bonus, this model can be turned into a simulator for situations where we want more radios than we physically have or can set up in the lab.
Implementing this in C, from a performance perspective for the latter part, makes some sense. But it makes demonstrating that the high level specification and requirements are satisfied and implemented almost impossibly difficult.
For example I can expect a scala developer to be able to implement functional principals in say, PHP but not necessarily the other way around.
I could argue the opposite, FP hide away the details per design as it is declarative and not imperative being imperative per wikipedia
"Imperative programming focuses on describing how a program operates."
Therefore are probably bad working in constrained environments or low level programming.
Seemann basically wrote the book on dependency injection in .NET, and has been a pretty big evangelizer for clean object-oriented design. Some time during that year, though, he discovered F#, and from 2014 on his blog has been increasingly about functional rather than object-oriented design. By following the transition you get a pretty good luck at things (ML-style) functional programming does well, since it's basically a journal of a serious expert in object-oriented design learning switching over to the functional paradigm.
I have a similar gripe with type systems. Types are restrictive metadata - they never reduce a program's complexity, and sometimes they increase it.
I work in embedded systems. Our primary programs operate on messages, binary data. Type less like you suggest would mean always grabbing contents by way of offsets. Then never knowing if they are ints, doubles or what.
Even the basic type infrastructure of C is useful here. We map the blobs to structs. We can then grab the contents and manipulate them as ints, doubles, or strings. Removing types would make this a horrific task.
I think there's lots of examples of production grade libraries in haskell (stm, async, lens, quickcheck, attoparsec, warp, etc.), some of which are effectively best in class among all languages. Likewise, I think there's a lack of acknowledgement of how many stellar compilers are written in haskell. Rust, Perl 6, Elm, Purescript, etc. were all either initially written in haskell (before they become self-hosting) or continue to be. This shouldn't be downplayed... a compiler is one of the most complicated kinds of software you can write, and writing a compiler in a language shows a lot about that language's versatility (which is why a self-hosted compiler is something of a benchmark).
Lack of good resources for learning haskell for so long have incubated haskell as an "experts" language, particularly experts interested in PLT considering haskell extensions have been used as a PLT research platform. I think this is why you see so much evangelism for the language and relatively little industry use: its generally been inaccessible for people without a background in theory for a long time (and its not per se that the language is inaccessible so much as some of the scary sounding, alien seeming design patterns that are conventionally used). Generally I believe this is getting better, though the situation isn't perfect.
As for evidence that this isn't due to deficiencies in the language, I think its worth considering how many ideas from haskell have percolated to the broader programming language community. Promises in javascript, for example, are effectively monadic (technically they're probably closer to bifunctors, but they're definitely very influenced by monads in haskell). Ad hoc polymorphism is now embraced by languages like C++ and rust. These things don't strictly need pure functions to work in the host language, but they're design ideas and language features that were originally developed to benefit a purely functional style.
Edit: As pointed out below, I'm wrong about rust, it was written in ocaml.
That being said, Rust itself is heavily inspired by Haskell; most notably, Rust's traits are pretty much equivalent to Haskell's typeclasses in almost every imaginable way, up to and including the way they can be derived for types.
But it is absolutely fetishized.
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.
I feel like "if your software can be modeled easily as X then hey, that will work well, and mostly you can" applies to all languages/styles, and if only people who like different languages and styles could just accept that this then we'd all get along fine. It's just a pity when people (not saying you're saying this) go "X is the BEST WAY TO PROGRAM and all other ways are STUPID and are for STUPID PEOPLE" and it ruins whatever point they were trying to make.
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.
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.
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.
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.
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
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.
Also just nice to have a = function, instead of having each class implement a .equals() method (looking at you, java).
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.
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.
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?
Which is not to say that automatic derivation of generic functions is not useful, but it's not really a game changer.
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...
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.
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