
What I Wish I Knew When Learning Haskell - andars
http://dev.stephendiehl.com/hask/
======
skimpycompiler
What I still do not know and always wished to know when I learned Haskell was
how to write efficient code easily. I wrote huge projects with thousands of
lines knowing nothing about C++ execution model and had insanely fast code
(that could have been made even faster - but I was not that kind of expert)
but with Haskell I have to be an expert to really write code that is as
performant as something that would take me much less time to write in C/C++.

Just writing simple efficient matrix multiplication is a pain. It took me a
couple of days to write a working quicksort.

I couldn't find any resources that provide a very serious introduction to
optimizing Haskell code.

I found out way too late in my adventures with Haskell that Monad Transformers
and similar abstractions have a significant runtime overhead and aren't free,
I thought I was just playing with types that won't get in the way when the
code compiles.

No one seems to cover this aspect of Haskell.

~~~
coolsunglasses
Don't do something that generates thunks in a long-running loop which'll hold
references to them. (This applies to JS...virtually any language with GC and
first-class functions as well). This is pretty easy to fix and identify once
you know what you're doing.

Don't use String. Use Text for Text, ByteString for raw bytes.

Related to String: don't use lists for large datasets unless you're
intentionally modeling and reasoning about your data as an infinite space. Use
Vector if it's finite, fits in memory, and you're going to comprehend it all
at once. Vector gives you cache-friendliness as well.

As a safe default: lazy in the spine, strict in the leaves.

Use streaming libraries (Pipes, Conduit) for processing large datasets. I did
a test with a csv parsing library and compared the non-streaming (default)
interface and the streaming interface provided by pipes-csv for summing
columns in a ~6mb CSV.

No streaming used 30mb of heap.

Naive streaming used 10mb.

Pipes used 600kb.

monad-control and mtl (monad transformer libraries) are pretty fast and can be
used without much (any?) worry in 99.999% of circumstances.

Use attoparsec and be mindful of backtracking anytime you're doing perf
sensitive parsing. If you hit a limit, you may need to use a parser generator
but this is almost never the case. If you need to parse something huge...use a
streaming parser.

Sometimes CPS transformation can be a huge boon. Read what Bryan O'Sullivan
was written about this for the parsing side of it. Kmett has good examples of
CPS transformation for performance in his libraries as well.

Use async.

Default to using TVars (STM containers) for correctness until you know what
specific properties you need. If you get perf sensitive but still want
transactions, turn the scope of your transactions into cells of a data
structure. Cf. [http://hackage.haskell.org/package/stm-
containers](http://hackage.haskell.org/package/stm-containers) when you have
composable concurrency abstractions, it can become just yet another data
structures problem.

If you're writing a (very) hot loop, you'll probably end up looking to avoid
boxing (just like Java) and touching the heap (just like Java, C, C++). Don
Stewart has written good, thorough examples of this.

Resources:

Anything Don Stewart has ever written

Kmett's libraries and blog

Bryan O'Sullivan's libraries (particularly attoparsec and aeson) and blog

Johan Tibell is the strictness perf honcho. Check his libraries for ideas if
you're making something strict. Don Stewart has written along these lines too.

[http://book.realworldhaskell.org/](http://book.realworldhaskell.org/) is out
of date but the stuff on perf and debugging are some of the best in the book.

My own book [http://haskellbook.com/](http://haskellbook.com/) will explain
how to reason about performance and laziness, though it's primarily a
practical beginner's book. Focus WRT perf will be more on understanding the
foundations, how things evaluate, how the runtime works so you can absorb all
the other information as easily as possible.

Koalafications:

Every backend I work on in ad tech, including the public facing adserver, is
written in Haskell. Our adserver latencies range 12-17ms with some pretty
gentle 99th percentiles. The average DSP we talk to has response times in the
low hundreds (100-400).

~~~
agumonkey
How out of date is RWH ?

~~~
psibi
This answer[0] explains various parts of RWH which is outdated.

[0]
[http://stackoverflow.com/a/23733494/1651941](http://stackoverflow.com/a/23733494/1651941)

------
jack9
So before learning Haskell, you wished you had already known over 325 pages of
what? That seems like a motivation to not try to learn it at all.

~~~
BadassFractal
In Haskell you can get most work done without the advanced concepts, but you
can always choose to dig into the meaty stuff and get even more leverage from
the type system. Easy to start, hard to master, keeps things very interesting
for a a very long time. By the time you think you've mastered most of what it
has to offer, the next version of GHC comes out with new goodies, now you can
learn even more superpowers.

Btw, the title says "when", not "before", big difference.

------
greenyoda
Readers may be interested in the extensive comments from when this article was
posted in previous years:

[https://new-hn.algolia.com/?experimental&sort=byDate&prefix&...](https://new-
hn.algolia.com/?experimental&sort=byDate&prefix&page=0&dateRange=all&type=story&query=What%20I%20Wish%20I%20Knew%20When%20Learning%20Haskell)

~~~
agumonkey
Digression, nice 'experimental' flag.

------
theophrastus
syntax. of the dozen languages i've taught myself, (which is a super-set of
the half dozen i've used to write significant things), Haskell's syntax has
been, and remains, the largest barrier for me (and this includes perl!). For
example, just a clear equivalence, like this one given by the very helpful
linked article here, would've helped enormously ("sugared form" for monad):

    
    
        do { a <- f ; m } ≡ f >>= \a -> do { m }
    

Sometimes i think the authors of Haskell had a completely different glyph set.
I'm sure some of you folks can read this like a book, but this sort of thing
still throws me into the ditch:

    
    
        M2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r

~~~
evincarofautumn
Well, just for the sake of discussion, would you prefer something like this?

    
    
        def M2<T1, T2, Result, Action : Monad>(
          f: (T1, T2) => Result,
          a: Action<T1>,
          b: Action<T2>): Action<Result>
        {
          …
        }
    

The Haskell notation is a simple, concise representation of the actual
semantics. If you try to transpose those semantics to a more conventional
notation intended for different semantics, it gets complicated due to the
mismatch.

This notation I’ve suggested doesn’t even accurately express such details as
currying and typeclasses, which the original notation conveys neatly.

~~~
pekk
Just for the sake of discussion, what is actually wrong with the syntax you
just invented?

~~~
tel
Here are a few possible complaints:

    
    
        1. It's way longer
        2. Naming the monad "Action" may be misleading depending 
           on choice of Monad
        3. It's tied syntactically to the syntax of the implementation
        4. The <>s are superfluous
        5. The argument names are superfluous (to the type)
        6. The explicit type quantification is superfluous and verbose

~~~
masklinn
> It's way longer

Because of the much longer type names which have nothing to do with the
syntax.

By _just_ replacing all the type names by the original single-letter names, it
goes from 107 to 72 characters, that's almost 50% increase in size (49%) due
to longer type names, which incidentally is nearly double the increase in size
between the Haskell signature and this new signature (57 -> 72, or 26%)

    
    
        def M2<A1, A2, R, M: Monad>(f: (A1, A2) => R, a: M<A1>, b: M<A2>): M<R>
    

Funnily enough, this expansion is relatively worse if applied to the Haskell
signature (57 -> 87, +53%)

> Naming the monad "Action" may be misleading depending on choice of Monad

Nothing to do with syntax either.

> It's tied syntactically to the syntax of the implementation

?

> The argument names are superfluous (to the type)

That can trivially be removed when just displaying the function's type (`f`'s
arguments are not explicitly named after all), likewise for the unnecessary
type quantifications, so a type-view of this function (rather than a source
view) could be:

    
    
        def M2<M: Monad>((A1, A2) => R, M<A1>, M<A2>) => M<R>
    

Oh look, it's now shorter than the original signature (though somewhat more
noisy, I'll give you that)

~~~
evincarofautumn
Conventions are as important as syntax when it comes to legibility of a
language, so phrasing “M” as “Action” seemed a fair comparison. And if you
want to be true to the semantics, you might write something like this:

    
    
        def M2 ((A1 => A2 => R) => M<A1> => M<A2> => M<R>) where Monad<M>
    

At which point the Haskell syntax is shorter. However, concision is not the
goal—it’s merely a proxy for clarity.

~~~
masklinn
> Conventions are as important as syntax when it comes to legibility of a
> language, so phrasing “M” as “Action” seemed a fair comparison.

But the conventions are whatever we want for a resyntacting no?

> However, concision is not the goal

I was answering a comment putting concision forward as reasons to dislike the
alternate syntax.

------
Kenji
My desire to be productive is at odds with my appreciation for Haskell. Don't
get me wrong, I love the language, it's elegant, absolutely awesome (apart
from whitespaces with semantic meaning). But sometimes I feel like the
language, despite its expressiveness, is like a tight straitjacket. And then I
just write some JavaScript or Java or C++ or PHP and feel so much freer.

~~~
Gabriel439
I like to think of Haskell's types as traffic lights. They might get in the
way if you're the only driver on the road but the moment you have to deal with
other people they are a life saver.

That's why people favor Haskell in enterprise environments because types keep
everything sane when you have multiple people working on the same project or
you have large third-party dependency trees.

------
okasaki
I wish I hadn't bothered learning Haskell. I spent years thinking I was an
idiot because I struggled and failed to make any even moderately complex
programs. Now I'm using C++ and things are so much easier. I think leaving
Haskell feels like leaving an abusive partner who always puts you down.

------
mark_l_watson
The author covers cabal sandbox, which I used until recently when I converted
my few Haskell projects to use stack. I was really happy with stack until a
few days ago when I tried to add some old Haskell code to a new yesod web app.
Dependency hell that took a while to sort out.

This article discussed getting everything working and then doing a cabal
freeze which is something I hadn't seen before. I would like to be able to
simply update a project to new library versions but maybe I should drop that
desire. Any advice?

~~~
jumpwah
I'm not sure cabal can really be considered a package manager as the article
here states [1]. Stack helps, but I think just using a dedicated package
manager to manage haskell things is probably _the_ way to go.

Nix has a lot of haskell packages in it already, and guix has a hackage
'import' tool which helps you with writing a new package definition for a
project that hasn't already been packaged.

You may think that's overkill, and fair enough. But I've been through cabal
hell before, and I'd honestly rather not anymore, ever.

[1]: [https://ivanmiljenovic.wordpress.com/2010/03/15/repeat-
after...](https://ivanmiljenovic.wordpress.com/2010/03/15/repeat-after-me-
cabal-is-not-a-package-manager/)

------
chrisra
"All of Haskell"

------
erik14th
Would add stackage[0] as first item if it was my list.

[0][https://www.stackage.org/](https://www.stackage.org/)

------
mijoharas
What I wish I knew now that I know Haskell: Where I can find Haskell jobs that
aren't in banking! I have grown a love of the language through using it, but I
don't know how to find many job positions that aren't either in banking or
research.

~~~
scroy
Why is it popular in banking?

~~~
mijoharas
My understanding is that it's the expressiveness and the type safety along
with being functional and immutable (by default) in order to ensure correct
programs are being written (which is obviously a concern when lots of money is
moving about.)

------
harry8
pandoc, shellcheck and xmonad. is this still the list of things you can
install that were written in haskell that are used for something that isn't
writing haskell code?

~~~
zeckalpha
Stuff you can install: Hakyll, Gitit, Git-annex, Propellor, Pugs.

Haskell is an enterprise language, not an application language. The benefits
of Haskell have played out better there.

------
danellis
Is he kidding with that title-text-as-an-image? I was going to C&P it into an
IM, but nope.

~~~
iso8859-1
Used to be very common when you wanted a fancy font and had no way to bring it
to the user. No joke, just old-fashioned.

~~~
danellis
It might be an old fashioned thing to do, but that web site is not old
fashioned. It actually uses web fonts.

