
Pain Points of Haskell - runeks
https://dixonary.co.uk/blog/haskell/pain
======
pdimitar
As a guy who casually (definitely not in-depth) reviewed working with Haskell
about 2 years ago I'd say the most relatable part of the article to me is:
complexity in tooling and unnecessary tension when working with libraries.
Also the String situation (several types of them).

A lot of the other points could be addressed but the community's seeming
unwillingness to tackle everyday productivity sends to me the message: "Keep
out!". :(

As a comparison, Elixir's Mix and Rust's Cargo are amazingly ergonomic build
systems and provide all the tooling you need to work with the languages and
your projects.

And hey, don't kill me. As I said, I reviewed it casually for several days
while comparing it with Rust and OCaml. They aren't the easiest to start with
either but Rust's tooling is excellent, OCaml's is also excellent but not
immediately obvious (their docs have improved a lot), and Haskell's is... kind
of there, as the article points out.

~~~
bjz_
> OCaml's is also excellent but not immediately obvious (their docs have
> improved a lot)

Interesting! The last time I tried OPAM, it actually seemed more frustrating
than Cabal! Maybe it's improved? Last time I tried OPAM it would install
packages globally by default, and avoiding that was a confusing process, when
that should be the default behavior.

Cargo (while not perfect) has really nice defaults out of the box - ie. it
generates a lockfile by default, scopes packages locally to individual
projects, and lets you use more than one package of the same version in the
same project.

Really hoping OCaml and Haskell can improve their package management story -
they are getting there, but it still holds me back from really using them on a
daily basis.

~~~
nextos
A bit off topic, but is Haskell viable for production? Or is it just really
intended as an experiment? Is it worthwhile to jump into Haskell now, or
perhaps one should look into Idris or Agda? Any companies using Haskell where
it has proven a distinct advantage?

Ocaml and SML (the former being sadly quite unpopular these days) are an order
of magnitude simpler than Haskell. They are easy to master. I have been quite
productive with Scala which, while being a member of the ML family, is much
more intricate due to OO + FP on top of great Java interop and many Haskell-
like features (laziness, monads, lenses...).

Haskell doesn't feel the same. I have learned some Haskell over the years.
Since I come from a pure FP education (started with SML) and I have lots of
type theory background, everything feels familiar, yet I've found it really
hard to be productive or to justify the mental overhead of laziness. I have
developed some small projects in Haskell, but I haven't found it a productive
alternative to the languages above.

~~~
tome
To answer your questions literally without much nuance:

Yes, Haskell is viable for production. There are caveats, as with any other
language. Haskell has _more_ caveats that most other languages you would be
familiar, but it's nonetheless perfectly suitable in the right environment.

No, don't use Agda or Idris in production, they're totally unfit. Agda is not
intended to be. Idris _may_ be but it will take 5 or 10 years.

The only members of the ML family that I'm aware of are Ocaml and the flavours
of Standard ML. Only Ocaml is fit for production.

~~~
_bxg1
There's ReasonML, which compiles to JS and seems intended for production

~~~
tomku
Reason is a syntax layer on top of OCaml, so it shares OCaml's production-
readiness. The tooling that Reason uses to compile to JS (Bucklescript) was
originally designed for OCaml, and Reason supports native targeting via the
plain OCaml compiler backend as well. It's a pretty pleasant ecosystem at the
moment.

------
rednum
As someone who stopped following Haskell world few years ago, this was quite
interesting read. I wonder what is the state of ecosystem (libraries) now: a
few years ago one of my friends complained that for most basic tasks there is
some library but usually half-working and abandoned.

However: I think the point about monads is fundamentally misguided. Monad is
an abstract concept and trying to explain it in non-technical way (as linked
from the post) will always fail. It makes as much sense as explaining time
signatures in non-musical terms, or a Banach space in non-mathematical terms.
I've seen this sentiment recurring every now and then; it feels like some
people really want to avoid formal definitions for the sake of it, even when
formal definitions make things easier. When it comes to monads, I think you
can't get any better than the semi-formal definition (typeclass with return
and join) plus some motivating examples. For me looking at Reader/Writer/State
helped get this; I agree with OP that List is kinda weird.

~~~
ashtonkem
I fully grok monads. I’ve done enough reading and usage of them to understand
them.

They are not a _useful_ abstraction for programming. They’re mathematically
correct, but that’s not the same as what an industrial engineer needs.

The big warning sign is monad transformers. Alone, monads are totally fine,
but the issue is that you rarely want _one_. So you end up with this unwieldy
tower of transformers that would make an enterprise Java engineer worried.

~~~
johnday
I firmly believe that monads (and monad transformers) are exactly what an
industrial engineer needs, for the following important reason.

A monad describes its scope in such a way that writing code outside of its
scope is a compile time error. If your code needs certain capabilities, it
must invoke the computational context of those capabilities (which is usually
a monad in Haskell). If it doesn't, then the context can (and should) be
omitted.

It's worth noting that monad transformers are themselves monads. So drawing a
distinction between "monads" and "monad transformers" to say that one is good
and one bad is not very meaningful. The composability of monads, as
exemplified in MTL, Transformers and similar, is a positive sign of their
power and not a red flag.

If your code ends up with an "unwieldy tower" of transformers then that's a
strong indication that the principle of separation of concerns has not been
adequately followed. The fact that Haskell makes that evident seems to me like
a benefit.

~~~
misja111
Even if you have separated concerns by having method 1 return Monad
transformer A and method 2 return transformer B, you will still have to
combine the results of both your methods at some point.

~~~
lmm
Sure. But if you're doing it right, that combination happens in a place whose
sole responsibility is doing that combination. The only case where you have to
carefully interleave A and B is if your business logic really does involve
carefully interleaving two disparate effects, and in that case the complexity
is genuinely there in the domain and it's good to surface it explicitly (or
else you're modelling it wrong).

------
spekcular
This is a great post. However, there are so many more things that should be on
here!

If I could suggest just one, it would be lazy evaluation by default, which
makes it exceedingly hard to reason about time and space complexity.

Also relevant is the author's article on problems using Haskell on Arch Linux:
[https://dixonary.co.uk/cabal-2020](https://dixonary.co.uk/cabal-2020)

~~~
dunefox
That's not a pain point, that's a core part of the language. If you don't want
that use Ocaml.

~~~
pdimitar
Then maybe an argument can be made that this should be front-and-center in the
docs? (Note that I am not claiming anything though, haven't checked Haskell's
docs in a while.)

It's also a surprise to me that you seem to say that OCaml is Haskell without
the lazy eval being the default, mind expanding on that? Quite interesting.

~~~
wk_end
They're similar but not really the same. The big differences are:

* Haskell is pure whereas Ocaml is not

* Haskell has a great story for parallelism whereas Ocaml does not

* Haskell has ad-hoc polymorphism (via typeclasses) whereas Ocaml does not

* Ocaml has an exceptionally powerful module system whereas Haskell does not

~~~
chewxy
I've heard it best as: both Ocaml and Haskell are pure functional languages.
Haskell is Extra Virgin.

That said, the notion of purity is a bit BS. At some point in your program you
are definitely going to invoke the world-at-large. Whether you do it through
IO(), tap your nose and call it "pure" or you do it other way is all down to
semantics (not in the PLT sense of the word - the PLT term to use would be
"pragmatics"). The core of both Ocaml and Haskell are pure functions.

Also, Ocaml doesn't really need typeclasses. Modules. Modules are everything

~~~
sweeneyrod
OCaml is a lot less pure in spirit as well. Not only can you read and write to
the outside world without having to jump through hoops, you can also write all
your code with pointers, for loops and (mutable) arrays if you so desire.

------
lambdasquirrel
This is nice, and yet, not much different from what the Haskell community was
dealing with 5 years ago. They are also mostly programming concerns, as
opposed to engineering concerns.

How do you deal with simple things like exceptions and string interpolation?
There are guides trying to explain monads, but no straightforward answer for
dealing with these sorts of things, with building separate libraries /
packages, setting up your own CI with your own stackage repo. And we haven’t
gotten into production yet. The ecosystem doesn’t have these things built out,
or, it’s just not documented/agreed upon so you’d effectively be starting from
scratch.

While it might be nice to use Haskell for math-y things, in the end, you’d
have to be able to instrument it, monitor/log it, etc. That doesn’t appear to
be in place either. When I worked with Haskell last, these were all blockers.

~~~
kccqzy
> How do you deal with simple things like exceptions and string interpolation?

While I agree string interpolation isn't convenient, exceptions in Haskell are
great. You can throw exceptions in pure code (throw upon evaluation), in IO
(throw when sequenced), catch them, inspect them by doing type down casting,
etc, all without pulling a dependency. It's even better than Rust. (In Rust I
had to use the anyhow crate to get back some basic features I thought should
be present by default.)

------
n3k5
Linked page just says “something went wrong” at the moment; cache:

[https://web.archive.org/web/20200608072453/https://dixonary....](https://web.archive.org/web/20200608072453/https://dixonary.co.uk/blog/haskell/pain)

~~~
sradman
Seems to be back with the following added to the top of the article:

> This is a temporary rehosted version while my website is hugged to death.

------
js8
I would say all this is improving, and while these are all pain points, they
are much less a pain point than they were just couple years ago.

My personal ergonomics improved when I ditched standard prelude for classy-
prelude. Regarding Strings, I just use Text type.

~~~
the_af
What is classy-prelude? Last time I coded something in Haskell (ages ago :() I
didn't hear of this.

~~~
tome
The first hit on Google will tell you :)

~~~
the_af
Hehe, such a classy way of telling me lmgtfy :P You're right of course, and
I've found out what it is.

------
bjz_
The new improvements to Cabal have been super nice of late, but one thing I
_really_ wish Cabal would do is allow for multiple versions of a library to be
used in the same project. Having to satisfy a single library version is
incredibly frustrating, and the solver errors are incredibly confusing. This
is especially confusing when it complains about a library deep in your
dependency graph being in conflict.

Ultimately the failure mode means that you can't build your package and are
blocked until a fix is made upstream, where as with a package manager like
Cargo you can still build your project. The downside is that you have a
duplicate in your dependency graph - but this can be fixed upstream in an
asynchronous fashion.

~~~
ulrikrasmussen
I agree that this is a pain point. I not familiar with Cargo and would like to
know how this actually works. Don't you risk binary incompatibility issues
because the same symbols are occupied by different versions of the same
package?

For example, what if my package depends on A(v1.0) and B, B depends on
A(v2.0), and furthermore B exposes a type from A(v2.0) in its API? Does the
package manager distinguish internal dependencies from dependencies that are
exposed in the API of the package?

~~~
bjz_
I think Rust gives the symbols unique hashes for each crate version to avoid
this. See this answer on Stack Overflow for more information:
[https://stackoverflow.com/a/51722134](https://stackoverflow.com/a/51722134)
\- not sure if there is a better reference document though.

You sometimes get weird errors like expected `A` but found `A` in the unusual
event that you actually run into this, but this is probably something that
could be fixed.

~~~
ulrikrasmussen
Thank you for the link, that clarified it for me. It seems that it requires
compiler support to do it in the same way as Rust does it, but I like that
approach better than complicating the package definitions with two types of
dependencies.

Reading that post also reminded me of the Unison language [1] which would even
allow the same type from different versions of a library to be identified as
the same if it wasn't altered. It does this by identifying every type and
function by the hash of their respective definitions.

[1] [https://www.unisonweb.org/](https://www.unisonweb.org/)

~~~
PhineasRex
What's interesting is that GHC absolutely supports this already. If you
manually link your projects you can use multiple versions of the same library
in your project. _Cabal_ doesn't support multiple versions of the same
library.

------
johnday
Thanks for hugging my site to death!

Note to admins: I've swapped it to Github Pages for the time being and the DNS
change should propagate soon.

Edit: Github Pages doesn't support adding HTTPS to a newly added domain for
some reason.

~~~
xrisk
You can put it behind Cloudflare and it should provision a certificate
instantly.

------
PhineasRex
The complaint about records is interesting because there aren't that many
major languages that actually do records well. For a language of its age, it
actually does records rather well. People sometimes forget that Haskell is
older than Java and like Java it has a ton of baggage from its early days.

The tooling is definitely an issue for bringing in beginners, but as far as
the build process goes if you're using docker and hosting your own package
mirror (as you should be for a professional deployment) it's perfectly
serviceable.

~~~
iso-8859-1
And records will be a lot friendlier with RecordDotSyntax! :D

[https://github.com/ghc-proposals/ghc-
proposals/blob/master/p...](https://github.com/ghc-proposals/ghc-
proposals/blob/master/proposals/0282-record-dot-syntax.rst#1-motivation)

------
wilde
One additional pain point for me is the number of symbolic operators. It’s
hard to search for what some of them do, and even harder to have a
conversation with a coworker when half your code is things like <$> or >>=.

~~~
yakshaving_jgt
Those two operators are very common, and they're typically called "fmap" and
"bind", respectively.

~~~
mcguire
Right, and when you have three or for libraries defining different symbolic
languages, your code begins to resemble J.

This is one of the reasons I gave up on Scala.

~~~
yakshaving_jgt
This hasn’t been my experience writing Haskell every day.

The language is a good tool, but it isn’t magic. You still need to apply good
taste.

------
junker123
I've been doing professional FP work for 20+ years at this point. Haskell has
community was the biggest killer for me. It was great when I made the jump
from Standard ML to Haskell about 15 years ago. About 5 years ago I just
couldn't stand what it had morphed into any longer - from the tension created
by certain companies to well known community members who were just trouble for
one reason or another. Instead of returning to Standard ML, I've been doing
Ocaml for the last 5 years or so and couldn't be happier.

------
gautamcgoel
Surprised that no one mentioned what a headache arrays are in Haskell. First,
there are several different libraries: Data.Array, Data.Vector, Repa, etc.
Second, the syntax is very clunky, especially if you are using
multidimensional arrays. It's a real shame, since most machine
learning/scientific computing programs are very heavy on arrays.

------
hopia
While there are lots of benefits for choosing Haskell it is certainly not
without its flaws as a development platform.

IDE has been my largest point of pain so far. I'm just used to a more
interactive development experience, and sadly the tooling is just not there
yet with Haskell. That said, there's great effort right now in that area so
hopefully it'll improve.

On top of the broken record system, another annoying part is that the runtime
monitoring and introspection story isn't so great compared to the likes of
Erlang.

~~~
diegoperini
Atom editor with its Haskell IDE plugin works like a charm. It has REPL and
all other inspection stuff you need out of box. What other feature you would
need is an unknown unknown to me.

~~~
Eliezer
An Atom plugin that actually worked after at most 3 days of effort trying to
get it to work would have been nice.

~~~
diegoperini
If you have ghc and stack installed, this plugin will automatically recognize
a project bootstrapped with stack and decorate Atom with IDE like widgets,
without requiring a configuration. It can take a few minutes to make it work
if your bash_profile doesn't do unexpected stuff on the PATH variable.

------
ghostwriter
> There is no featureful Haskell plugin for any major text editor which can be
> installed without command-line intervention

This is not true for at least a year already, as there's IntelliJ Haskell:

* [https://github.com/rikvdkleij/intellij-haskell](https://github.com/rikvdkleij/intellij-haskell)

* [https://plugins.jetbrains.com/plugin/8258-intellij-haskell](https://plugins.jetbrains.com/plugin/8258-intellij-haskell)

~~~
lol768
This one's a good start, but it's still painfully behind other IntelliJ
language plugins. E.g. I expected HSpec tests to work in the same way that I
can run JUnit tests within the IDE, but no such luck.

------
foldr
One thing that irks me about the current Haskell ecosystem is that it seems to
be going all-in on Nix. Nix is interesting, but I can't think of another
programming language where the only way to get a reasonable development
environment is to run a particular Linux distribution.

(I know that you can install nix as a package manager on OS X and other Linux
flavours, but at least on OS X, packages don't work all that reliably. The
enormous disk space requirements are also an issue when using a laptop.)

~~~
tome
Could you share what you mean by that, or how you got that impression?

I've used Nix once, four years ago, and I didn't really like it[1]. I've never
used it since. The only thing in the Haskell ecosystem that requires Nix is
GHCJS, I think, which is bleeding edge technology that few use. Cabal's new
package management style is _called_ "Nix-style" but otherwise has no
connection to Nix whatsoever (I wish they'd use a different name actually --
"persistent style", "immutable style"?).

[1] For various reasons to do with user experience. I'm sure it's improved a
lot by now.

~~~
foldr
I guess my experience is colored by my last gig as a Haskell developer, where
our entire development and deployment process was nix-based.

I understand that it is possible to use Haskell without using Nix. However, I
do get the impression that a significant section of the community see nix as
the best way to manage Haskell dependencies. The (insanely confusing) naming
of the new cabal features seems to support that.

~~~
tome
Well, I don't know the details of your personal experience, but I'm confident
to declare that the vast majority of the Haskell community has never used Nix
and has no particular intention to.

~~~
exdsq
Nix has managed to permeate almost every repo I work with. Only a minute ago I
realised a Make file had had nix-shell stuff added to stack build.

There is no escape, only Nix.

~~~
tome
Intriguing. Are these public repos? Care to share some examples?

~~~
exdsq
Sure! [https://github.com/input-output-hk/](https://github.com/input-output-
hk/)

------
amelius
I think Haskell would be the perfect language to write reference
implementations in. I.e., stuff that gets spec'd out by committees like RFCs
or Web technologies.

------
ivanbakel
I personally don't understand the hangup on the existence of an IDE.

Don't get me wrong - IDEs are great, especially for beginners. But "one-
editor-per-language" is an increasingly outdated mode of thinking. The culture
shock of having to download a whole new IDE for a new language is a distinct
negative. Beginners benefit from new languages slotting neatly into existing
tools, which is exactly what the language server efforts in Haskell have
yielded.

A (perhaps) valid criticism would be "the Haskell community has not done
enough to make sure the language server is easy usable" \- and I wouldn't even
say that _that_ was the case.

~~~
Supermancho
> I personally don't understand the hangup on the existence of an IDE.

I don't understand the lack of a hangup. It's obvious from using an IDE to
going back to a text editor. It hurts adoption, it hurts beginners, it hurts
the ecosystem...ie disparate tools grouped with known interactions are not
necessary to fully understand when creating a program, leave those details in
the IDE as a simplified interaction (eg checkbox to run a lint every save).
Lack of tooling (erlang and lua's lack of a comprehensive package manager
comes to mind) stunts language maturity. It's a pain point.

~~~
ivanbakel
My point is there's no "lack" of tooling. Nowadays, the Haskell IDE engine is
good enough for general use. It's pretty trivial to plug into every general-
purpose editor.

More and more beginners nowadays do not want to install a whole new IDE. They
don't want to have to configure it, learn it, and understand all of its
nuances and foibles. They prefer using their existing setup (VSCode, Atom,
vim, emacs, etc.) with a nice slot-in for the new language - which is exactly
what the Haskell tooling supports.

By comparison, a custom IDE is a massive undertaking, and a relatively
fruitless one. It takes a tremendous effort to do _well_ , and up until that
point it actually negatively impacts the learning experience: if you encourage
beginners to use a mediocre IDE, they will experience the language in a
mediocre way.

~~~
johnday
> Nowadays, the Haskell IDE engine is good enough for general use.

Sadly I am unconvinced of this. If it were true, there would not be an
immediate and significant push to make `haskell-language-server`.

Having tried many times to get `hie` working, I can say that it's a pain in
the bum.

Suppose I have 12 projects, one made every month for the last year. Each of
these 12 will be using a different stack resolver, possibly with different ghc
versions. As a result, I will need to compile and use up to _twelve_ different
versions of hie, and make sure that the correct one is on my $PATH depending
on which project I have open.

~~~
smichael
I don't think it's quite _that_ bad. Most likely you could easily use 1-2 ghc
versions for those, and you need one build of IDE tools per ghc version, not
per resolver, no ?

~~~
johnday
Sure, I'm outlining a worst-case. But realistically, even having two different
hie versions is enough to make switching environments a Hard Problem.

------
gridlockd
The compilation time is the dealbreaker for me right there. If your language
is slow to compile, it cannot be that good.

~~~
lmm
If the compiler finds problems that you'd otherwise need a test suite to
catch, then as long as the compiler is faster than that test suite you still
come out ahead.

~~~
gridlockd
If the compiler is slow, I _feel_ unproductive. I need to try out things
quickly, I need to iterate. The bugs don't matter at this stage.

~~~
lmm
You may not need a full compile for that though. Some combination of
incremental compilation, non-optimising compiles, or a presentation compiler /
typechecker only can be enough to iterate with.

------
mcguire
Does Cabal still have issues with multiple, conflicting versions of packages
installed globally?

Stack's main advantage was that packages ( _essentially everything_ ,
including the compiler if I remember correctly) are installed in project-
space, so that two different projects don't conflict, anyway.

~~~
tathougies
the new cabal v2 system does not encourage global package installation. It
adopts a stack-style approach and IMO is actually better than stack.

------
piinbinary
I would add a debugger to this list.

The one built into GHCi is the only working one I know of, and it has major
failings (such as telling me that variables aren't in scope when they clearly
are).

------
nickbauman
It strikes me that I need to be a mathematician to use Haskell. Especially
when someone like Rob Pike makes claims that "I cannot read the syntax of
Haskell and understand it."

~~~
whateveracct
That's because Rob Pike never learned Haskell. Expecting to just look at it
and know it because you know other languages is quite frankly silly!

You don't need to be a mathematician though. Many Haskellers I've worked with
either didn't go to university or went for something else. I only ever learned
C before Haskell, for instance.

~~~
irfanka
cough _Python_ cough

------
dustingetz
The community has different priorities than industry

~~~
brazzy
Which priorities are those, in your opinion?

------
barking
Warning possible flamebait: could Clojure be the solution?

~~~
johnday
I've tried to get into Clojure multiple times and it feels quite unergonomic
to program. I think my brain is not correctly shaped for its constructs,
whereas Haskell fits my mental models perfectly.

~~~
dgb23
Would you say you are a top-down thinker/programmer?

~~~
johnday
I think so, yes. I think Haskell helps with this. You can write the top level
code first, and be fairly confident that, if the types are sensible, there
will be a sensible implementation by the time you reach the bottom.

It also helps that there's so much code reuse that the "depth" reached is
quite shallow compared to what you might see in other languages.

