Is Haskell really the language of geniuses and academia? (2019)
habr.com
habr.com
The whole avoid success at all costs looks more like a meme to me than actual strategy. There are functional languages (Clojure(Script), Elm) which made pragmatic decisions about the feature sets and have found commercial success. But with Haskell, it seems a reverse process was followed. It starts with some (questionable?) assumptions like lazy evaluation is good and then builds on top of it. For commercially viable software, the process is generally reverse. You start with specific problems and build a feature set from it.
The best way to understand this academic Vs. pragmatic approach is to look at Golang's relative success vs Haskell. No matter how much PL researcher snobs complain about golang, its creators started from real problems and chose some constraints carefully. The result is its widespread adoption in its niche (networked infrastructure code that can tolerate GC). Such cases are not documented for Haskell or are rare to find.
It is perfectly alright and better to say, look this was a research language to find out how far we can go with certain choices and assumptions. But constant evangelism about how it is elegant etc does not help.
From my (limited) experience with Haskell, my impression is that it's strongest in domains that map _very_ well to formal mathematical models. Mathematical computation is an obvious example, but things like programming language parsers are also really intuitive to build in Haskell.
My experience trying to use Haskell for other stuff — mainly, building web backends, my bread and butter back then — was painful. Simple CRUD against a database is actually pretty decent thanks to there being decent Postgres drivers and libraries, but the moment you need to handle arbitrarily-nested data structures (like a JSON request/response with a nontrivial schema), you run up against a lot of awkwardness, to the point that for the longest time this was known in Haskell as the "records problem."
Lenses and similar tools have tried to solve this problem over the years, and maybe they have, but I moved on from the language when I saw how deep that rabbit hole went.
None of this is to say Haskell isn't fun or rewarding, but it definitely has its strengths and weaknesses.
While I totally sympathize with this, I'm not sure it's a very good vantage point from which to judge a language.
Where are the practical Haskell tutorials. No preaching, no "you're holding it wrong", ONLY this: "you need to handle arbitrarily nested JSON? here's how".
- Simple Haskell Handbook[1]
- Haskell (Almost) Standard Libraries[2]
- [0]: https://lhbg-book.link
Learning an entirely new language is hard and requires patience and perseverance!
The records problem is basically a small syntactical quirk. Undesirable, yes, but not what will make or break production software development.
Lenses solve some of that and much more. Lenses strictly improve on fields/records/getters/setters/etc. in any other language you know. In other words, they accomplish what they do and then some.
If that was your only problem with Haskell, then it might be one of your favourite languages if you get to know it again.
I still think it's worth learning by anyone who's got the time for it.
There isn't much that only one programming language can do, so it's not fair to ask for this. The benefits of using Haskell over other programming languages are that it's easier to reason about code in it, and you can have more assurance that code is correct.
“In other communities people mostly discuss regular production problems and data structures, while in a Haskell chat people discuss monads, applicative functors, crazy types and things like that.”
This is why use cases and how only Haskell enabled solving it are rare.
The inofficial motto of 'avoid "success at all costs"' is not a joke. It's easy-ish to achieve success if you allow yourself to pander to the masses who don't always understand what they are missing, and there's always been an element in the Haskell community that tries to do what's right, notwithstanding what the large masses think.
This desire is usually expressed with sentiments like "let's make this easier to learn" and "Let's fix this so it's not a footgun", and not "success at all cost".
How about: the least-valued of all goals?
The lesson I derive from Haskell is that "If it compiles, it will work"
1. is inflated nonsense
2. has a lot less to do with Haskell-the-language than with Haskell-the-type-system
I have observed a similar phenomenon with Rust. That statement is not perfect, it's hyperbole. If it compiles, it doesn't mean it will run (otherwise, why bother writing code? We just write function signatures and call it a day).
But Rust's type system is incredibly sophisticated, just like Haskell's.
And guess what... Rust supports mutability. It doesn't just support it, it encourages it. It tells you "Step over that fence, it's okay, I got your back".
It's so liberating.
The secret here is not functional programming. It's not immutability.
It's a sound and powerful type system.
And everywhere else, unfortunately.
Go is an overreaction in the opposite direction. In 2010 I can't agree that there is a good reason to make a language without generics and reasonable error handling from the very start.
Kind of getting sick of hearing these same complaints, over and over, to be honest.
(my background is mostly python, R, javascript, clojure)
I don't completely buy the argument that a Google backed Haskell would have been as popular as Go. Readability (which is subjective), familiarity (often C is the reference), and the ease of which the language can be learned are still major factors in widespread acceptance.
Articles of that form are usually BS though, for any language. These are general purpose programming languages. They all have different benefits and drawbacks, but you can usually use any of them to do anything you want.
Haskell was created by researches who wanted to study properties of lazy functional programming. I’ve never heard anyone say they did it because lazy evaluation was “good”, it was their area of research. The co-creator has even said that if he had to do it over again it might be better if it were strictly evaluated.
I worked in Haskell for a few years before switching over to a very closely related but strictly evaluated language, PureScript.
The pureness and laziness of Haskell forced the creators to work out monads for IO which I find extremely useful. Haskell-like monads certainly can (and have) been implemented in strictly evaluated languages.
I don’t miss implicit laziness very often, but I’ll say with partially applied functions, it’s nice to know that values will be calculated as needed and results shared across invocations once those functions are fully applied. That’s something I wish we had in PureScript. Note that in addition to laziness this also requires a compiler optimization that moves let bindings out of inner functions, aka let-floating.
Laziness is also nice as it allows you to be very declarative, defining what things are without ever worrying about the calculations running when you don’t need the result. In practice it’s easy enough to create thunks manually and evaluate them when needed. Doing this can get tedious and the resulting code is less succinct.
I don't think it takes any special ability or particular intelligence to be drawn to a language like Haskell-- All you really need is real genuine curiosity, an inclination to learn something you don't already know, and that's something that "geniuses and academia" have a lot of.
There's nothing wrong with that, but going outside the comfort zone is how you maximize perspective. In other words, it's pretty much completely impossible to convince someone that it would be really worth their time to learn more maths (eg.: calculus, linear algebra, whatever)... The only way they'll see the practical value of doing so is if they actually do it.
I can think of several ways to motivate software engineers to learn more category theory (e.g. "did you know that MapReduce is basically an application of monoids?" or "did you know you can solve fast document layout updates with monoids?").
One thing I wonder about though. You say "curiosity" - can you explain what this means to you? Why are you curious about a certain concept? There are thousands of things to learn in the world - are you curious about all of them?
There are a number of subjects I used to really have zero interest in when I was younger (chemistry, accounting, law, finance, economics...), and in each instance I eventually realized how valuable time spent learning the subject was. After this happened a few times, I began paying closer attention to when I felt disinterested by some academic topic.
I don't think I've felt "uninterested" by any academic topic in years, so I suppose maybe I am curious about it all.
Focusing on practical things means avoiding digging yourself into an ivory tower, it's important to remember that programming languages are human artifacts and it's very easy to get lost in inventions we made up. Practicality is a measure of outwardness, not of curiosity. Many geniuses are very prone to get stuck in their own sky castles.
In fact that's funnily mirrored in Haskell itself. I/O in Haskell was only added after the fact, the language started out kind of solipsistic leading to monadic IO as a solution to the question of "wait, how do we actually talk to the outside world guys?"
I'm not advocating learning only one thing and spending your life on that-- on the contrary, I'm saying if there's a subject you're not interested in (eg.: chemistry, accounting, actuarial science, invertebrate biology, whatever), it's probably because you don't know enough about it.
The people who do know about topic X, do see its applications. There's almost always more to gain from learning a different thing than we tend to believe at the onset.
The principles driving Haskell and functional language paradigms are fine by developers and they don't need to learn it either, nearly every developer is familiar with these. Case and point: the success of Elixir, Swift, Scala, etc.
1) There is no easy way to learn the language. Try explaining that without looking ignorant.
2) The software written in this language is too hard to read and understand.
Do either of these problems manifest in Haskell? No idea. But I've seen them manifest in other systems and once I know about them suddenly there is an obvious and suspicious silence of people talking about clear problems. When a group of developers see something they don't understand they go quiet because it is hard to tell if the thing is inherently too hard or if the developer themselves just doesn't get it.
Happens a bit in the lisps, I think. There isn't any privileged syntax for loops and conditionals so there are no hints about what control flow constructs to use. People aren't about to stand up and say they don't really understand what control structures are should be used when. They'd just look silly. So the topic doesn't get a lot of discussion despite being important & people go use languages where loop statements get special syntax.
IMO thats really the only remaining problem of Haskell. Most of its documentation is just plain terrible. Rust is probably more difficult to learn as a language, yet the documentation culture is so awesome that one can power through.
In other words, journeyman users who are experienced in "following the types" will find plenty of really high-quality documentation. Total novices who need an introduction to the language will also find high-quality material (though this is more of a recent development).
However, once you're past the novice stage and at the apprentice level where you want to accomplish something specific using some libraries, there's nothing for you there. You have to ask someone at the next journeyman level to sit with you and guide you through following the types.
That is limiting, and under-recognised, I think.
Haskell code varies a ton. Some people write simple, straightforward stuff in Haskell with lots of pure functions and relatively simple types, straightforward definitions. Some people invent complicated type systems for their app. Some use weird styles, like points free, or continuation passing. Some will rely heavily on abstractions like monads, applicative functors, arrows, zippers, lenses, unfolding, etc. Some write straight imperative code.
Highly variable.
Data → Process → Process → Process
Is the above unclear?
Edit: Here's a Haskell wiki article:
https://wiki.haskell.org/Pointfree
> Point-free style can (clearly) lead to Obfuscation when used unwisely. As higher-order functions are chained together, it can become harder to mentally infer the types of expressions. The mental cues to an expression's type (explicit function arguments, and the number of arguments) go missing.
> Point-free style often times leads to code which is difficult to modify. A function written in a pointfree style may have to be radically changed to make minor changes in functionality. This is because the function becomes more complicated than a composition of lambdas and other functions, and compositions must be changed to application for a pointful function.
> Perhaps these are why pointfree style is sometimes (often?) referred to as pointless style.
I have observed some relatively extreme examples of pointfree in the wild, and that's what I'm referring to.
I think it is highly rewarding to learn to program in Haskell. It offers you another perspective of approaching a computational problem and has a great ecosystem for many applications. And if you ever want to get into theorem-proving with Coq or Agda, knowing Haskell is a huge bonus.
My suggested learning path is as follows: 1. Read Get Programming with Haskell and do the Haskell MOOC at https://haskell.mooc.fi/ at the same time. 2. Read sections in Haskell Programming from First Principles not covered in 1. 3. Read Haskell in Depth.
Many years ago, I tried to learn Haskell from Haskell Programming from First Principles but couldn't get to the point I could write meaningful applications. I gave up after about a year. Early this year, I completed the Haskell MOOC and read most of Haskell in Depth and now I have a much better command of the language. And doing exercises on CodeWars, Hackerrank, and Exercism really help.
I would advise ignoring both the glowing praises and scathing criticisms. Learn it well enough and form your own judgement, assuming of course that you have time to do so.
TXR Lisp:
1> [apply zip (gun (read))]
(1 2 3)
(a b c)
("foo" 3 :bar)
nil
[Ctrl-D][Enter]
((1 a "foo") (2 b 3) (3 c :bar))
CL: [2]> (apply #'mapcar #'list (loop for x = (read) while x collect x))
(1 2 3)
(a b c)
("foo" 3 :bar)
nil
((1 A "foo") (2 B 3) (3 C :BAR))Here, the parsing is done; the intermediate data structure is built in. For now, it's printed to the console and thrown away (becomes garbage), but if anything else is done with it, it will be in that same form.
It's possible to ask the Haskell compiler to not care, but this would be so far from idiomatic Haskell that it wouldn't help you if I wrote that code here, I don't think.
To @kazinator's point, it's not quite going to be just lists of random things, in that Haskell's list elements are all the same type, unlike what Common Lisp lets you do. I'd be more curious whether this could be lists of "Thing", where that Thing type is a typeclass that is inclusive of all the sorts of data you would expect to handle.
It has features you didn’t know you wanted or needed and eventually other languages will get there, and Haskell will seem average/harder to differentiate.
I’ve written a more practical post on why Haskell is worth using[0], excuse the clickbait title.
[0]: https://vadosware.io/post/how-and-why-haskell-is-better/
Most SWE's brains can't afford more though, sadly.
You don't need a Mercedes these days, and that's a result of other companies adopting things that and increase safety, etc. We should expect this of a healthy ecosystem!
1. Is functional programming really better than other approaches? (My opinion: Yes, for most applications, although it can sometimes feel more difficult.)
2. Is Haskell really better than other functional programming languages. (My opinion: Haskell has some powerful features that other FP languages lack, but it is often less practical.)
Bottom line: The most important thing is to grok functional programming as a paradigm. There are several good functional programming languages to choose from. Personally, I'm a big fan of F#.
Same thing seems to apply to monads. Haskell programmers use them a lot but other languages seem to work just fine without them.
So my guess is that many of the special features of Haskell are solutions to problems you will have if you insist that language is lazy and pure.
Monads are essential for "effectful" programming in a functional language. Imperative languages don't need monads, because everything they do is effectful (which leads to unnecessary side effects, and from there to a plethora of errors).
While both languages are functional, in contrast to Haskell, clojure doesn't have functors or monads. My understanding is that the same problem is essentially solved in a different way with macros.
In other words, you don't necessarily want laziness for its own sake (though sometimes you do) but you do want it for its second order benefits.
Could you get those benefits without laziness? Yes, you could. But in the history of programming languages that has happened very rarely, because under strictness it is very easy and tempting to sneak in a little mutability or uncontrolled effects. Under laziness you can't get away with that.
[1] http://augustss.blogspot.com/2011/05/more-points-for-lazy-ev...
[2] https://www.reddit.com/r/haskell/comments/l98v73/comment/glg...