New practioners of Haskell may find themselves guided by this page. Here are things I wish I knew when I started learning the language. This page may be slightly out of date; I need to update it.
XMonad
XMonad is a wonderful tiling window manager that also happens to
be written in Haskell. You can grab it with cabal install xmonad
. Plenty
of people have published their xmonad.hs
files, so I won't belabor the point,
but here are some useful tricks I've accumulated:
- If you use
.xsession
to start up XMonad from GDM without usinggnome-session
, you should also start upgnome-settings-daemon
,gnome-power-manager
andgnome-screensaver
. Among the things these will fix include your power button, locking your screen and your desktop background. - I hate
gnome-panel
with a passion, but without some sort of panel you can't run Network Manager. I use stalonetray, with the flags-bg '#000000' -i 8 -geometry 19x1+680+0
: in particular,-i 8
sets the icon size to something that fits snugly in a default xmobar. Once the tray is running, spin upnm-applet
. - Transparency is good. Windows that spawn on the same workspace that you requested are good.
Literature
I learned with Real World Haskell
and Brent Yorgey's Typeclassopedia.
Real World Haskell will give you a practical introduction to Haskell's
syntax and functional programming, and give you some cunningly written
examples of monadic code. Typeclassopedia will give you the
theoretical foundation to understand the progression from functor to
pointed to applicative to monad. Learn how to implement all of the
common monads from scratch. Be competent with both >>=
and the do
sugaring. return
does not
short-circuit execution.
Hoogle
I Hoogle. Do you
Hoogle? I have Hoogle setup as a keyword shortcut in
Firefox, under the letter h. I also have it
locally with cabal install hoogle
. Use it
to find what module functions are in, lookup those weird
operators like >>>
and <*>
,
and discover new functions from type signatures.
Types
So you got a type error. How do you go about fixing it?
- Do you have an explicit type signature for the top-level? Is it correct? What does Haskell infer if you leave it out?
- Replace parts of your expression with
undefined
until it typechecks, then figure out where the mismatch was. - Rewrite the function from scratch. It's short, right?
If you're running into type errors with monadic code, pay close attention to where the
m
is in the inputs and outputs of your functions.liftM (a -> b) -> m a -> m b ap m (a -> b) -> m a -> m b =<< (a -> m b) -> m a -> m b $ (m a -> m b) -> m a -> m b return a -> m a - Did you ask #haskell?
Exercises
I find working through mind-bending problems is a good way to test how well you've figured out a concept and also get intimately familiar with details. Here are some problems that should be done as you work your way through Typeclassopedia.
Functors
- Implement
fmap
forEither e
,((,) e)
and((->) e)
. - Write a function with type signature
Functor f => (a -> b) -> f (f a) -> f (f b)
. If you are stuck, try writing the function for a specific instance of functor. Can this type signature be generalized? - What is the type of
fmap fmap
? Specify both usingFunctor f =>
and explicitly. Remember that all functions in Haskell are automatically curried. - What is the type of
fmap fmap fmap
?
Pointed
- Implement
pure
for[]
and((->) e)
. - Assuming a well-behaved functor that follows the laws
fmap id = id
andfmap (g . h) = fmap g . fmap h
, prove thatfmap g . pure = pure . g
for all definitions ofpure
. (Nota bene: a rigorous treatment of this question may require Wadler's theorems for free, which is not for the lighthearted.)
Applicative
- What symbols are used for the infix version of
fmap
? - Implement
<*>
forMaybe
,[]
andZipList
. - Implement
<*>
for((->) e)
. This is tricky. - What is the value of
[(+1), (+2)] <*> [4, 5]
? - What is the type of
const <*> id
? Rewrite this expression in a form that makes this obvious from the applicative lawfmap g x = pure g <*> x
. - What is the value of
((+) <*> (+3)) 5?
- Write
fmap
withpure
and<*>
. - Write
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
with only<$>
and<*>
.
Errors
Errors can be broadly be divided into
two types: pure and impure. Pure errors utilize data types
like Maybe
, Either
or
MonadError
and are used monadically to short-circuit
execution. Impure errors mean that the function
returns "bottom" (a "value" that means the computation cannot
terminate) and possibly have additional semantics attached
to it, such as error
(prints string error message)
or asynchronous exceptions.
Pure errors are what you should turn to first when you find yourself in need of some sort of error reporting mechanism. Descriptive pure errors also fall into several categories: some errors are intended to be "caught" and then result in execution flow change, while others are unrecoverable and intended to be passed straight to the user.