Haskell

the mind can learn
the zen of
monads ∙ morphisms ∙ multicore

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 using gnome-session, you should also start up gnome-settings-daemon, gnome-power-manager and gnome-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 up nm-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
    apm (a -> b) -> m a -> m b
    =<<(a -> m b) -> m a -> m b
    $(m a -> m b) -> m a -> m b
    returna -> 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 for Either 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 using Functor 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 and fmap (g . h) = fmap g . fmap h, prove that fmap g . pure = pure . g for all definitions of pure. (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 <*> for Maybe, [] and ZipList.
  • 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 law fmap g x = pure g <*> x.
  • What is the value of ((+) <*> (+3)) 5?
  • Write fmap with pure 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.