[Haskell-cafe] What is your favourite Haskell "aha" moment?
Neil Mayhew
neil_mayhew at users.sourceforge.net
Wed Jul 11 16:11:30 UTC 2018
I came to Haskell from C++, and I was used to the idea of parametric
types and functions from C++ templates.
However, what I really liked about Haskell's way of doing these was
that, due to type inference, not only is there a lot less ceremony
involved, but code is generic (parametric) *by default*, and thus much
more reusable, although you still have the option of tightening it up by
adding a type annotation (eg for performance reasons). For a while, I
was writing all my C++ code as templates, but this ended up being a pain.
Also, traditionally C++ has not allowed you to place any constraints on
template arguments (type parameters) whereas Haskell's type classes are
a very elegant way of doing it. (C++ now has concepts, but I haven't
taken the time yet to see how they compare with type classes.)
I was also used to function overloading, which is somewhat similar to
type inference, in that the compiler will pick an implementation based
on the types of a function's arguments. This is similar to Haskell
picking an implementation from among type class instances. However, what
blew me away is that Haskell can overload based on *return type* as well
as argument types. I haven't seen any other production-ready language
that can do this. A great example of how this is useful is the regex
library, where you can select from among widely varying styles of regex
matching result simply by type inference, ie without needing any type
annotation.
There were a *lot* of other things I found amazing, but others have
covered many of these already. Also, languages are borrowing from each
other at a rapid rate these days (eg Rust traits are equivalent to type
classes) so it's hard to find a "killer feature" in Haskell any more
(except laziness, perhaps). I think it's more the well-balanced
combination of all the features that makes Haskell so pleasant to work
in, and it's hard to demonstrate all of these in a single example.
My own favourite "gem" is this code for computing all primes, based on
code in a paper[1] by Doug McIlroy:
primes = sieve [2..] where sieve (p : ns) = p : sieve [n | n <- ns, n
`mod` p /= 0]
I think care must be exercised, when using examples like this one, to
avoid giving the impression that Haskell is a "toy" language. However,
what I find interesting about this example is that all other sieve
implementations I've seen work on a fixed size of sieve up front, and if
you later change your mind about how many primes you want, eg because
you're expanding a hash table and want a bigger prime for the size, you
typically have to start the sieve from scratch again.
[1]: http://www.cs.dartmouth.edu/~doug/sieve/sieve.pdf
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20180711/fb9b02cb/attachment.html>
More information about the Haskell-Cafe
mailing list