Haskell uses type inference, so explicit type declarations are rarely required. This:
import Control.Monad.Fix fibs = fix ((1:) . scanl (+) 1) main = print (take 20 fibs)
Is just as good as:
import Control.Monad.Fix fibs :: [Integer] fibs = fix ((1:) . scanl (+) 1) main :: IO () main = print (take 20 fibs)
$ runhaskell A.hs [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]
Now, once programs reach a decent size, the advantage of type declarations appears: it functions as machine-checkable documentation. So people new to your code can more quickly work out what the code is doing.
Contrast this (real world) Haskell code from a network client:
accessorMS decompose f = withMS $ s writer -> let (t,k) = decompose s in f t (writer . k)
And with type annotations:
accessorMS :: (s -> (t, t -> s)) -> (t -> (t -> LB ()) -> LB a) -> ModuleT s LB a accessorMS decompose f = withMS $ s writer -> let (t,k) = decompose s in f t (writer . k)
So at least you know have some idea of what that code does.
There’s an intuition here: type declarations are good, but they can be mechanically inferred. Let’s automate that then! Here’s a quick script I use every day. It just passes your top level declaration to ghci, and asks it to infer the type. The resulting type signature is spliced back in to your code:
#!/bin/sh # input is a top level .hs decls FILE=$* DECL=`cat` ID=`echo $DECL | sed 's/^([^ ]*).*/1/'` echo ":t $ID" | ghci -v0 -cpp -fglasgow-exts -w $FILE echo $DECL
Save this as an executable shell file in your path. Now you can call this from your editor. For example, from vim, you’d use the following .vimrc:
:map ty :.!typeOf %
Hitting :ty while the cursor is positioned on top of a top level declaration takes your code from:
writePS who x = withPS who (_ writer -> writer x)
writePS :: forall p g. String -> Maybe p -> ModuleT (GlobalPrivate g p) LB () writePS who x = withPS who (_ writer -> writer x)
I can’t emphasise enough how useful this is. So, steal this code and improve your productivity!