This text demonstrates the difficulties people often face when learning Haskell. It is perhaps biased by my own experiences, so it may emphasize things that C and Scheme have taught me. Those would be paranoia and flexibility.
Table of Contents

 1.1 Confusing Prompt
 1.2 Interactive Context
 1.3 Shadowing Definitions
 1.4 Unloading Modules
 1.5 Specialized Functions
 1.6 Monad Type Class
 1.7 Language Extensions
 1.8 List Comprehensions

 2.1 Type Conversions
 2.2 No Instance for Show
 2.3 Monomorphism Restriction
 2.4 Type Defaulting
 2.5 Integer Overflows
 2.6 The Bottom Type

 3.3 Negative Numbers
 3.4 Special Characters
 3.5 Naming Conventions
1 System
The very first obstacle is GHC, the primary implementation of Haskell. This section concerns its tools and libraries.
1.1 Confusing Prompt
The prompt used in interactive sessions tends to grow quite long as more modules are imported, so it is typical to change it. Do not be confused by strange prefixes like λ>
or politeness.
Prelude Control.Monad Data.Functor> :set prompt "please " please :set prompt2 " " please let this = Just "an example" that = "good idea" please return this :: [Maybe String] [Just "an example"] please sequence it Just ["an example"]
1.2 Interactive Context
Definitions in source files are just like mathematical equations. They are statements of what is true, so it is not possible to change them, and they do not have effects, so it is not possible to observe them either. That makes them unfit for interactive use.
Interactive sessions get around this limitation by working in the IO
monad. That means interactive commands are akin to do
blocks, with the exception of some special cases like imports or data type definitions. The most visible consequence is that definitions need to be prefixed with let
.
please let x = 2 please x 2
1.3 Shadowing Definitions
Redefining things during interactive sessions can be confusing, because new names simply shadow the old ones. Both definitions coexist, but the other one is unreachable without indirection.
please let f x = 5 g = f please [f 2, g 2] [5, 5] please let f x = 2 please [f 2, g 2] [2, 5]
1.4 Unloading Modules
There is an alternative syntax for loading modules in interactive sessions.
please map negate [1 .. 3] [1, 2, 3] please :m + Data.List Data.Map
It is useful for loading multiple modules at the same time or unloading modules, both of which are normally impossible.
please map negate [1 .. 3] <interactive>:2:1: Ambiguous occurrence `map' It could refer to either `Data.Map.map', imported from `Data.Map' or `Prelude.map', imported from `Prelude' please :m  Data.Map please map negate [1 .. 3] [1, 2, 3]
1.5 Specialized Functions
Many functions are specialized versions of more general ones.
please :t map map :: (a > b) > [a] > [b] please :t (<$>) (<$>) :: Functor f => (a > b) > f a > f b
They exist to help convey intent and sometimes to allow for performance optimizations. It is up to the user to decide the level of abstraction they want to work at.
please :t (.) (.) :: (b > c) > (a > b) > a > c please :t (<<<) (<<<) :: Category cat => cat b c > cat a b > cat a c
1.6 Monad Type Class
A constraint that relates monads and applicative functors is missing in the standard library, because Monad
was created before Applicative
.
class Applicative a => Monad a where (>>=) :: m a > (a > m b) > m b (>>) :: m a > m b > m b return :: a > m a fail :: String > m a
It is also the reason why both pure
and return
exist.
1.7 Language Extensions
The best source for confusing features is language extensions. They are unofficial features that are not in the standard, but may one day be, if they survive their trial by fire (namely: users). Some of them are fun and some break on every update.
They have to be turned on explicitly.
please :set XGADTs please {# LANGUAGE GADTs #}
1.8 List Comprehensions
List comprehensions are essentially retarded monad comprehensions. They work like do
blocks or explicit binding,
please [x * y  x < [1 .. 3], y < [x .. 3]] [1, 2, 3, 4, 6, 9] please do x < [1 .. 3] y < [x .. 3] return $ x * y [1, 2, 3, 4, 6, 9] please [1 .. 3] >>= \ x > [x .. 3] >>= \ y > return $ x * y [1, 2, 3, 4, 6, 9]
but only with lists.
please Just 2 >>= \ x > Just x >>= \ y > return $ x * y Just 4 please do x < Just 2 y < Just x return $ x * y Just 4 please [x * y  x < Just 2, y < Just x] <interactive>:6:15: Couldn't match expected type `[b]' with actual type `Maybe a' In the return type of a call of `Just' In the expression: Just 2 In a stmt of a list comprehension: x < Just 2 <interactive>:6:28: Couldn't match expected type `[b]' with actual type `Maybe b' In the return type of a call of `Just' In the expression: Just x In a stmt of a list comprehension: y < Just x
Language extensions fix that.
please :set XMonadComprehensions please [x * y  x < Just 2, y < Just x] Just 4
Do not be afraid to use language extensions when it is appropriate.
1.9 Exceptions
There are exceptions that are kind of strange.
please :t undefined undefined :: a please undefined *** Exception: Prelude.undefined
2 Semantics
These issues arise from the formal system beneath.
2.1 Type Conversions
Some functions return concrete types.
please let f xs = sum xs / length xs <interactive>:1:19: No instance for (Fractional Int) arising from a use of `/' Possible fix: add an instance declaration for (Fractional Int) In the expression: sum xs / length xs In an equation for `f': f xs = sum xs / length xs
They need to be manually promoted to more generic types, since there are no implicit type conversions.
please let f xs = sum xs / fromIntegral (length xs) please import Data.List please let f xs = sum xs / genericLength xs
2.2 No Instance for Show
It is not possible to print arbitrary data types by default.
please data Type a = T a please T 2 <interactive>:2:1: No instance for (Show (Type a)) arising from a use of `print' Possible fix: add an instance declaration for (Show (Type a)) In a stmt of an interactive GHCi command: print it
Doing so requires defining the show
function from the Show
type class. It is a fairly obvious function. The only unexpected thing is that the result should be valid code that can be fed to read
. You can write it by hand
please instance Show a => Show (Type a) where show (T a) = "T " ++ show a please T 2 T 2
or derive it automatically.
please data Type a = T a deriving Show please T 2 T 2
Both have their uses, but the latter is more convenient.
2.3 Monomorphism Restriction
The Haskell 98 report mentions a strange type system restriction. It is called the monomorphism restriction and concerns making decisions about types during inference. It sounds like abstract nonsense, but makes sense in the context of category theory, where morphisms are a generalization of functions and monomorphisms in particular correspond to injective functions. Regardless of its cryptic name, it solves a practical problem: it prevents ambiguous types from appearing.
The restriction can cause seemingly nonsensical errors and is especially common in interactive sessions.
please let f x = return x please :t f f :: Monad m => a > m a please let f = return <interactive>:3:9: No instance for (Monad m) arising from a use of `return' The type variable `m' is ambiguous Possible fix: add a type signature that fixes these type variable(s) Note: there are several potential instances: instance Monad ((>) a)  Defined in `GHC.Base' instance Monad IO  Defined in `GHC.Base' instance Monad []  Defined in `GHC.Base' ...plus six others In the expression: return In an equation for `f': f = return
The correct way to work around it is to explicitly specify the types of all top level symbols.
please let f :: Monad m => a > m a f x = return x
The lazy way is to switch on a language extension that removes the restriction (and all of its benefits).
please :set XNoMonomorphismRestriction please let f = return please :t f f :: Monad m => a > m a
2.4 Type Defaulting
Another type inference problem is type defaulting. The most generic type is not always the one that is chosen, so the outcome can be unexpected and therefore troublesome.
please :t 2 2 :: Num a => a please let x = 2 please :t x x :: Integer
The solution is, again, to use type signatures or hope for the best.
please let x :: Num a => a x = 2 please :t x x :: Num a => a
2.5 Integer Overflows
Types defaulting to Integer
instead of Int
cause the illusion that integer overflows do not exist. Not having direct access to memory and lacking a special size type, like size_t
in C, also contribute to it.
Integer overflows are real and dangerous.
please 42 ^ 13 1265437718438866624512 please :t it it :: Integer please length [1 .. 42] ^ 13 7387622647092436992 please :t it it :: Int
2.6 The Bottom Type
The monomorphism restriction takes care of ambiguous types, but there are other special cases the type system chokes on. Functions that do not terminate and those that throw exceptions are among them.
please f :: a > b f x = f x
Their return types seem arbitrary, because a mathematical entity called the bottom type, often written ⊥
or __
, is not a visible part of the type system.
Total languages like Coq do not have this problem, but they are not Turing complete either.
3 Syntax
3.1 Indentation
Statements need to be indented correctly to avoid ambiguous parsing. The if
condition is especially confusing to beginners, because it works until it is placed in another construct that causes a conflict.
if p then c else a
Luckily there are many ways to do it right.
if p then c else a
if p then c else a
The right ways are more obvious with other constructs.
case p of True > c False > a
3.2 Spaces
Sometimes spaces do not make a difference and sometimes they do.
please import Foreign as F please (not.F.toBool) 1 False please (not . F . toBool) 1 <interactive>:3:8: Not in scope: data constructor `F'
It is best to be careful and establish a consistent style.
please (not . F.toBool) 1 False
3.3 Negative Numbers
The operator 
is difficult to apply partially as negative numbers may be written with a space between the sign and the magnitude.
please (/ 2) 5 2.5 please ( 2) 5 <interactive>:2:2: No instance for (Num (a > b)) arising from a use of syntactic negation Possible fix: add an instance declaration for (Num (a > b)) In the expression:  2 In the expression: ( 2) 5 In an equation for `it': it = ( 2) 5
The subtract
function is useful for working around the problem.
please (subtract 2) 5 3
3.4 Special Characters
Characters are reserved roughly so that
 lowercase letters are for values,
 uppercase letters are for types and
 special characters are for operators.
There are a few exceptions that are surprising. For example :
is used in operators for types
please let x + y = 5 in 2 + 2 5 please let x :+ y = 5 in 2 :+ 2 <interactive>:2:7: Not in scope: data constructor `:+' <interactive>:2:21: Not in scope: data constructor `:+'
please data Type a b = a :+ b please data Type a b = a + b <interactive>:4:17: Not a data constructor: `a'
and ~
is used for controlling pattern matching.
please let x ~~ y = 5 in 2 ~~ 2 5 please let x ~ y = 5 in 2 ~ 2 <interactive>:6:20: Pattern syntax in expression context: ~2 please let x ~y = 5 in x 2 5
3.5 Naming Conventions
Some names are inconsistent. For example Functor
is not called Mappable
while Traversable
is not named after abstract nonsense. It is best to focus on what things are instead of what they are called.