diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -37,6 +37,8 @@
   transformers
   unordered-containers
   utf8-string
+  split
+  boxes
 
 base LICENSE file:
 
@@ -620,3 +622,63 @@
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+split LICENSE file:
+
+  Copyright (c) 2008 Brent Yorgey, Louis Wasserman
+
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the author nor the names of other contributors
+     may be used to endorse or promote products derived from this software
+     without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  SUCH DAMAGE.
+
+boxes LICENSE file:
+
+  Copyright (c) Brent Yorgey 2008
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the author nor the names of other contributors
+     may be used to endorse or promote products derived from this software
+     without specific prior written permission.
+
+  All other rights are reserved.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  SUCH DAMAGE.
diff --git a/examples/passing/InstanceBeforeClass.purs b/examples/passing/InstanceBeforeClass.purs
new file mode 100644
--- /dev/null
+++ b/examples/passing/InstanceBeforeClass.purs
@@ -0,0 +1,9 @@
+module Main where
+
+instance fooNumber :: Foo Number where
+  foo = 0
+
+class Foo a where
+  foo :: a
+
+main = Debug.Trace.trace "Done"
diff --git a/examples/passing/OperatorAssociativity.purs b/examples/passing/OperatorAssociativity.purs
--- a/examples/passing/OperatorAssociativity.purs
+++ b/examples/passing/OperatorAssociativity.purs
@@ -40,3 +40,19 @@
 
     assert (4 % (9 / 3) == 1)   "4 % (9 / 3) == 1"
     assert ((4 % 9) / 2 == 2)   "(4 % 9) / 2 == 2"
+
+    assert (not (1 < 0) == true) "not (1 < 0) == true"
+    assert (not (complement (1 + 10) == 8) == true) "not (complement (1 + 10) == 8) == true"
+    assert (not ((negate 1) < 0) == false) "not ((negate 1) < 0) == false"
+
+    assert (negate (1 + 10) == -11) "negate (1 + 10) == -11"
+
+    assert (2 * 3 / 4 == 1.5) "2 * 3 / 4 == 1.5"
+    assert (1 * 2 * 3 * 4 * 5 / 6 == 20) "1 * 2 * 3 * 4 * 5 / 6 == 20"
+    assert (complement (2 / 3 * 4) == -3) "complement (2 / 3 * 4) == -3"
+
+    assert (1 + 10 - 5 == 6) "1 + 10 - 5 == 6"
+    assert (1 + 10 * 5 == 51) "1 + 10 * 5 == 51"
+    assert (10 * 5 - 1 == 49) "10 * 5 - 1 == 49"
+
+    trace "Success!"
diff --git a/examples/passing/SequenceDesugared.purs b/examples/passing/SequenceDesugared.purs
new file mode 100644
--- /dev/null
+++ b/examples/passing/SequenceDesugared.purs
@@ -0,0 +1,34 @@
+module Main where
+
+import Control.Monad.Eff
+
+data Sequence t = Sequence (forall m a. (Monad m) => t (m a) -> m (t a))
+
+sequence :: forall t. Sequence t -> (forall m a. (Monad m) => t (m a) -> m (t a))
+sequence (Sequence s) = s
+
+sequenceArraySeq :: forall m a. (Monad m) => Array (m a) -> m (Array a)
+sequenceArraySeq [] = pure []
+sequenceArraySeq (x:xs) = (:) <$> x <*> sequenceArraySeq xs
+
+sequenceArray :: Sequence []
+sequenceArray = Sequence (sequenceArraySeq)
+
+sequenceArray' :: Sequence []
+sequenceArray' = Sequence ((\val -> case val of
+  [] -> pure []
+  (x:xs) -> (:) <$> x <*> sequence sequenceArray' xs))
+
+sequenceArray'' :: Sequence []
+sequenceArray'' = Sequence (sequenceArraySeq :: forall m a. (Monad m) => Array (m a) -> m (Array a))
+
+sequenceArray''' :: Sequence []
+sequenceArray''' = Sequence ((\val -> case val of
+  [] -> pure []
+  (x:xs) -> (:) <$> x <*> sequence sequenceArray''' xs) :: forall m a. (Monad m) => Array (m a) -> m (Array a))
+
+main = do
+  sequence sequenceArray $ [Debug.Trace.trace "Done"]
+  sequence sequenceArray' $ [Debug.Trace.trace "Done"]
+  sequence sequenceArray'' $ [Debug.Trace.trace "Done"]
+  sequence sequenceArray''' $ [Debug.Trace.trace "Done"]
diff --git a/hierarchy/Main.hs b/hierarchy/Main.hs
--- a/hierarchy/Main.hs
+++ b/hierarchy/Main.hs
@@ -64,7 +64,7 @@
   case modules of
     Left err -> hPutStr stderr (show err) >> exitFailure
     Right ms -> do
-      for_ ms $ \(P.Module moduleName decls _) ->
+      for_ ms $ \(P.Module _ moduleName decls _) ->
         let name = runModuleName moduleName
             tcs = filter P.isTypeClassDeclaration decls
             supers = sort . nub . filter (not . null) $ fmap superClasses tcs
diff --git a/prelude/prelude.purs b/prelude/prelude.purs
--- a/prelude/prelude.purs
+++ b/prelude/prelude.purs
@@ -20,7 +20,7 @@
   , negate
   , DivisionRing
   , Num
-  , Eq, (==), (/=), refEq, refIneq
+  , Eq, (==), (/=)
   , Ord, Ordering(..), compare, (<), (>), (<=), (>=)
   , Bits, (.&.), (.|.), (.^.), shl, shr, zshr, complement
   , BoolLike, (&&), (||)
@@ -31,27 +31,38 @@
 
   -- | An alias for `true`, which can be useful in guard clauses:
   -- |
-  -- | E.g.
+  -- | ```purescript
+  -- | max x y | x >= y = x
+  -- |         | otherwise = y
+  -- | ```
   -- |
-  -- |     max x y | x >= y = x
-  -- |             | otherwise = y
   otherwise :: Boolean
   otherwise = true
 
   -- | Flips the order of the arguments to a function of two arguments.
+  -- |
+  -- | ```purescript
+  -- | flip const 1 2 = const 2 1 = 2
+  -- | ```
+  -- |
   flip :: forall a b c. (a -> b -> c) -> b -> a -> c
   flip f b a = f a b
 
   -- | Returns its first argument and ignores its second.
+  -- |
+  -- | ```purescript
+  -- | const 1 "hello" = 1
+  -- | ```
+  -- |
   const :: forall a b. a -> b -> a
   const a _ = a
 
   -- | This function returns its first argument, and can be used to assert type equalities.
   -- | This can be useful when types are otherwise ambiguous.
   -- |
-  -- | E.g.
-  -- |
-  -- |     main = print $ [] `asTypeOf` [0]
+  -- | ```purescript
+  -- | main = print $ [] `asTypeOf` [0]
+  -- | ```
   -- |
   -- | If instead, we had written `main = print []`, the type of the argument `[]` would have
   -- | been ambiguous, resulting in a compile-time error.
@@ -61,15 +72,33 @@
   infixr 9 >>>
   infixr 9 <<<
 
+  -- | A `Semigroupoid` is similar to a [`Category`](#category) but does not require an identity
+  -- | element `id`, just composable morphisms.
+  -- |
+  -- | `Semigroupoid`s should obey the following rule:
+  -- |
+  -- | - Associativity: `p <<< (q <<< r) = (p <<< q) <<< r`
+  -- |
+  -- | One example of a `Semigroupoid` is the function type constructor `(->)`, with `(<<<)` defined
+  -- | as function composition.
   class Semigroupoid a where
     (<<<) :: forall b c d. a c d -> a b c -> a b d
 
   instance semigroupoidArr :: Semigroupoid (->) where
     (<<<) f g x = f (g x)
 
+  -- | Forwards composition, or `(<<<)` with its arguments reversed.
   (>>>) :: forall a b c d. (Semigroupoid a) => a b c -> a c d -> a b d
   (>>>) f g = g <<< f
 
+  -- | `Category`s consist of objects and composable morphisms between them, and as such are
+  -- | [`Semigroupoids`](#semigroupoid), but unlike `semigroupoids` must have an identity element.
+  -- |
+  -- | `Category`s should obey the following rules.
+  -- |
+  -- | - Left Identity: `id <<< p = p`
+  -- | - Right Identity: `p <<< id = p`
+  -- |
   class (Semigroupoid a) <= Category a where
     id :: forall t. a t t
 
@@ -79,17 +108,57 @@
   infixr 0 $
   infixl 0 #
 
+  -- | Applies a function to its argument
+  -- |
+  -- | ```purescript
+  -- | length $ groupBy productCategory $ filter isInStock products
+  -- | ```
+  -- |
+  -- | is equivalent to
+  -- |
+  -- | ```purescript
+  -- | length (groupBy productCategory (filter isInStock (products)))
+  -- | ```
+  -- |
+  -- | `($)` is different from [`(#)`](#-2) because it is right-infix instead of left, so
+  -- | `a $ b $ c $ d x` = `a $ (b $ (c $ (d $ x)))` = `a (b (c (d x)))`
+  -- |
   ($) :: forall a b. (a -> b) -> a -> b
   ($) f x = f x
 
+  -- | Applies a function to its argument
+  -- |
+  -- | ```purescript
+  -- | products # groupBy productCategory # filter isInStock # length
+  -- | ```
+  -- |
+  -- | is equivalent to
+  -- |
+  -- | ```purescript
+  -- | length (groupBy productCategory (filter isInStock (products)))
+  -- | ```
+  -- |
+  -- | `(#)` is different from [`($)`](#-1) because it is left-infix instead of right, so
+  -- | `x # a # b # c # d` = `(((x # a) # b) # c) # d` = `d (c (b (a x)))`
+  -- |
   (#) :: forall a b. a -> (a -> b) -> b
   (#) x f = f x
 
   infixr 6 :
 
+  -- | An infix alias for `cons`.
+  -- |
+  -- | Note, the running time of this function is `O(n)`.
   (:) :: forall a. a -> [a] -> [a]
   (:) = cons
 
+  -- | Attaches an element to the front of an array, creating a new array.
+  -- |
+  -- | ```purescript
+  -- | cons 1 [2, 3, 4] = [1, 2, 3, 4]
+  -- | ```
+  -- |
+  -- | Note, the running time of this function is `O(n)`.
   foreign import cons
     """
     function cons(e) {
@@ -99,6 +168,10 @@
     }
     """ :: forall a. a -> [a] -> [a]
 
+  -- | The `Show` type class represents those types which can be converted into a human-readable `String` representation.
+  -- |
+  -- | While not required, it is recommended that for any expression `x`, the string `show x` be executable PureScript code
+  -- | which evaluates to the same value as the expression `x`.
   class Show a where
     show :: a -> String
 
@@ -148,41 +221,166 @@
   infixl 4 <$>
   infixl 1 <#>
 
+  -- | A `Functor` is a type constructor which supports a mapping operation `(<$>)`.
+  -- |
+  -- | `(<$>)` can be used to turn functions `a -> b` into functions `f a -> f b` whose argument and return
+  -- | types use the type constructor `f` to represent some computational context.
+  -- |
+  -- | `Functor` instances should satisfy the following laws:
+  -- |
+  -- | - Identity: `(<$>) id = id`
+  -- | - Composition: `(<$>) (f <<< g) = (f <$>) <<< (g <$>)`
+  -- |
   class Functor f where
     (<$>) :: forall a b. (a -> b) -> f a -> f b
 
+  -- | `(<#>)` is `(<$>)` with its arguments reversed. For example:
+  -- |
+  -- | ```purescript
+  -- | [1, 2, 3] <#> \n -> n * n
+  -- | ```
   (<#>) :: forall f a b. (Functor f) => f a -> (a -> b) -> f b
   (<#>) fa f = f <$> fa
 
+  -- | The `void` function is used to ignore the type wrapped by a [`Functor`](#functor), replacing it with `Unit` and
+  -- | keeping only the type information provided by the type constructor itself.
+  -- |
+  -- | `void` is often useful when using `do` notation to change the return type of a monadic computation:
+  -- |
+  -- | ```purescript
+  -- | main = forE 1 10 \n -> void do
+  -- |   print n
+  -- |   print (n * n)
+  -- | ```
   void :: forall f a. (Functor f) => f a -> f Unit
   void fa = const unit <$> fa
 
   infixl 4 <*>
 
+  -- | The `Apply` class provides the `(<*>)` which is used to apply a function to an argument under a type constructor.
+  -- |
+  -- | `Apply` can be used to lift functions of two or more arguments to work on values wrapped with the type constructor `f`.
+  -- | It might also be understood in terms of the `lift2` function:
+  -- |
+  -- | ```purescript
+  -- | lift2 :: forall f a b c. (Apply f) => (a -> b -> c) -> f a -> f b -> f c
+  -- | lift2 f a b = f <$> a <*> b
+  -- | ```
+  -- |
+  -- | `(<*>)` is recovered from `lift2` as `lift2 ($)`. That is, `(<*>)` lifts the function application operator `($)` to arguments
+  -- | wrapped with the type constructor `f`.
+  -- |
+  -- | `Apply` instances should satisfy the following law:
+  -- |
+  -- | - Associative Composition: `(<<<) <$> f <*> g <*> h = f <*> (g <*> h)`
+  -- |
+  -- | Formally, `Apply` represents a strong lax semi-monoidal endofunctor.
   class (Functor f) <= Apply f where
     (<*>) :: forall a b. f (a -> b) -> f a -> f b
 
+  -- | The `Applicative` type class extends the [`Apply`](#apply) type class with a `pure` function, which can be used to
+  -- | create values of type `f a` from values of type `a`.
+  -- |
+  -- | Where [`Apply`](#apply) provides the ability to lift functions of two or more arguments to functions whose arguments are wrapped using `f`,
+  -- | and [`Functor`](#functor) provides the ability to lift functions of one argument, `pure` can be seen as the function which lifts functions of
+  -- | _zero_ arguments. That is, `Applicative` functors support a lifting operation for any number of function arguments.
+  -- |
+  -- | `Applicative` instances should satisfy the following laws:
+  -- |
+  -- | - Identity: `(pure id) <*> v = v`
+  -- | - Composition: `(pure <<<) <*> f <*> g <*> h = f <*> (g <*> h)`
+  -- | - Homomorphism: `(pure f) <*> (pure x) = pure (f x)`
+  -- | - Interchange: `u <*> (pure y) = (pure ($ y)) <*> u`
+  -- |
   class (Apply f) <= Applicative f where
     pure :: forall a. a -> f a
 
+  -- | `liftA1` provides a default implementation of `(<$>)` for any [`Applicative`](#applicative) functor,
+  -- | without using `(<$>)` as provided by the [`Functor`](#functor)-[`Applicative`](#applicative) superclass relationship.
+  -- |
+  -- | `liftA1` can therefore be used to write [`Functor`](#functor) instances as follows:
+  -- |
+  -- | ```purescript
+  -- | instance functorF :: Functor F where
+  -- |   (<$>) = liftA1
+  -- | ```
   liftA1 :: forall f a b. (Applicative f) => (a -> b) -> f a -> f b
   liftA1 f a = pure f <*> a
 
   infixl 1 >>=
 
+  -- | The `Bind` type class extends the [`Apply`](#apply) type class with a "bind" operation `(>>=)` which composes computations
+  -- | in sequence, using the return value of one computation to determine the next computation.
+  -- |
+  -- | The `>>=` operator can also be expressed using `do` notation, as follows:
+  -- |
+  -- | ```purescript
+  -- | x >>= f = do y <- x
+  -- |              f y
+  -- | ```
+  -- |
+  -- | where the function argument of `f` is given the name `y`.
+  -- |
+  -- | `Bind` instances should satisfy the following law:
+  -- |
+  -- | - Associativity: `(x >>= f) >>= g = x >>= (\k => f k >>= g)`
+  -- |
+  -- | Or, expressed using `do` notation:
+  -- |
+  -- | - Associativity: `do { z <- do { y <- x ; f y } ; g z } = do { k <- x ; do { y <- f k ; g y } }`
+  -- |
+  -- | Associativity tells us that we can regroup operations which use do-notation, so that we can unambiguously write, for example:
+  -- |
+  -- | ```purescript
+  -- | do x <- m1
+  -- |    y <- m2 x
+  -- |    m3 x y
+  -- | ```
   class (Apply m) <= Bind m where
     (>>=) :: forall a b. m a -> (a -> m b) -> m b
 
+  -- | The `Monad` type class combines the operations of the `Bind` and `Applicative` type classes. Therefore, `Monad` instances
+  -- | represent type constructors which support sequential composition, and also lifting of functions of arbitrary arity.
+  -- |
+  -- | `Monad` instances should satisfy the following laws:
+  -- |
+  -- | - Left Identity: `pure x >>= f = f x`
+  -- | - Right Identity: `x >>= pure = x`
+  -- |
+  -- | Or, expressed using `do` notation:
+  -- |
+  -- | - Left Identity: `do { y <- pure x ; f y } = f x`
+  -- | - Right Identity: `do { y <- x ; pure y } = x`
+  -- |
   class (Applicative m, Bind m) <= Monad m
 
+  -- | `return` is an alias for `pure`.
   return :: forall m a. (Monad m) => a -> m a
   return = pure
 
+  -- | `liftM1` provides a default implementation of `(<$>)` for any [`Monad`](#monad),
+  -- | without using `(<$>)` as provided by the [`Functor`](#functor)-[`Monad`](#monad) superclass relationship.
+  -- |
+  -- | `liftM1` can therefore be used to write [`Functor`](#functor) instances as follows:
+  -- |
+  -- | ```purescript
+  -- | instance functorF :: Functor F where
+  -- |   (<$>) = liftM1
+  -- | ```
   liftM1 :: forall m a b. (Monad m) => (a -> b) -> m a -> m b
   liftM1 f a = do
     a' <- a
     return (f a')
 
+  -- | `ap` provides a default implementation of `(<*>)` for any [`Monad`](#monad),
+  -- | without using `(<*>)` as provided by the [`Apply`](#apply)-[`Monad`](#monad) superclass relationship.
+  -- |
+  -- | `ap` can therefore be used to write [`Apply`](#apply) instances as follows:
+  -- |
+  -- | ```purescript
+  -- | instance applyF :: Apply F where
+  -- |   (<*>) = ap
+  -- | ```
   ap :: forall m a b. (Monad m) => m (a -> b) -> m a -> m b
   ap f a = do
     f' <- f
@@ -210,28 +408,43 @@
   infixl 6 -
   infixl 6 +
 
-  -- | Addition and multiplication
+  -- | Addition and multiplication, satisfying the following laws:
+  -- |
+  -- | - `a` is a commutative monoid under addition
+  -- | - `a` is a monoid under multiplication
+  -- | - multiplication distributes over addition
+  -- | - multiplication by `zero` annihilates `a`
+  -- |
   class Semiring a where
     (+)  :: a -> a -> a
     zero :: a
     (*)  :: a -> a -> a
     one  :: a
 
-  -- | Semiring with modulo operation and division where
-  -- | ```a / b * b + (a `mod` b) = a```
+  -- | Addition, multiplication, modulo operation and division, satisfying:
+  -- |
+  -- | - ```a / b * b + (a `mod` b) = a```
+  -- |
   class (Semiring a) <= ModuloSemiring a where
     (/) :: a -> a -> a
     mod :: a -> a -> a
 
-  -- | Addition, multiplication, and subtraction
+  -- | Addition, multiplication, and subtraction.
+  -- |
+  -- | Has the same laws as `Semiring` but additionally satisfying:
+  -- |
+  -- | - `a` is an abelian group under addition
+  -- |
   class (Semiring a) <= Ring a where
     (-) :: a -> a -> a
 
   negate :: forall a. (Ring a) => a -> a
   negate a = zero - a
 
-  -- | Ring where every nonzero element has a multiplicative inverse (possibly
-  -- | a non-commutative field) so that ```a `mod` b = zero```
+  -- | Ring where every nonzero element has a multiplicative inverse so that:
+  -- |
+  -- | - ```a `mod` b = zero```
+  -- |
   class (Ring a, ModuloSemiring a) <= DivisionRing a
 
   -- | A commutative field
@@ -301,14 +514,29 @@
 
   instance numNumber :: Num Number
 
+  -- | The `Unit` type has a single inhabitant, called `unit`. It represents values with no computational content.
+  -- |
+  -- | `Unit` is often used, wrapped in a monadic type constructor, as the return type of a computation where only
+  -- | the _effects_ are important.
   newtype Unit = Unit {}
 
+  -- | `unit` is the sole inhabitant of the `Unit` type.
   unit :: Unit
   unit = Unit {}
 
   infix 4 ==
   infix 4 /=
 
+  -- | The `Eq` type class represents types which support decidable equality.
+  -- |
+  -- | `Eq` instances should satisfy the following laws:
+  -- |
+  -- | - Reflexivity: `x == x = true`
+  -- | - Symmetry: `x == y = y == x`
+  -- | - Transitivity: if `x == y` and `y == z` then `x == z`
+  -- | - Negation: `x /= y = not (x == y)`
+  -- |
+  -- | `(/=)` may be implemented in terms of `(==)`, but it might give a performance improvement to implement it separately.
   class Eq a where
     (==) :: a -> a -> Boolean
     (/=) :: a -> a -> Boolean
@@ -366,6 +594,11 @@
     (==) xs ys = eqArrayImpl (==) xs ys
     (/=) xs ys = not (xs == ys)
 
+  -- | The `Ordering` data type represents the three possible outcomes of comparing two values:
+  -- |
+  -- | `LT` - The first value is _less than_ the second.
+  -- | `GT` - The first value is _greater than_ the second.
+  -- | `EQ` - The first value is _equal to_ or _incomparable to_ the second.
   data Ordering = LT | GT | EQ
 
   instance eqOrdering :: Eq Ordering where
@@ -380,11 +613,25 @@
     show GT = "GT"
     show EQ = "EQ"
 
+  instance semigroupOrdering :: Semigroup Ordering where
+    (<>) LT _ = LT
+    (<>) GT _ = GT
+    (<>) EQ y = y
+
+  -- | The `Ord` type class represents types which support comparisons.
+  -- |
+  -- | `Ord` instances should satisfy the laws of _partially orderings_:
+  -- |
+  -- | - Reflexivity: `a <= a`
+  -- | - Antisymmetry: if `a <= b` and `b <= a` then `a = b`
+  -- | - Transitivity: if `a <= b` and `b <= c` then `a <= c`
+  -- |
   class (Eq a) <= Ord a where
     compare :: a -> a -> Ordering
 
   infixl 4 <
 
+  -- | Test whether one value is _strictly less than_ another.
   (<) :: forall a. (Ord a) => a -> a -> Boolean
   (<) a1 a2 = case a1 `compare` a2 of
     LT -> true
@@ -392,6 +639,7 @@
 
   infixl 4 >
 
+  -- | Test whether one value is _strictly greater than_ another.
   (>) :: forall a. (Ord a) => a -> a -> Boolean
   (>) a1 a2 = case a1 `compare` a2 of
     GT -> true
@@ -399,6 +647,7 @@
 
   infixl 4 <=
 
+  -- | Test whether one value is _non-strictly less than_ another.
   (<=) :: forall a. (Ord a) => a -> a -> Boolean
   (<=) a1 a2 = case a1 `compare` a2 of
     GT -> false
@@ -406,6 +655,7 @@
 
   infixl 4 >=
 
+  -- | Test whether one value is _non-strictly greater than_ another.
   (>=) :: forall a. (Ord a) => a -> a -> Boolean
   (>=) a1 a2 = case a1 `compare` a2 of
     LT -> false
@@ -456,6 +706,7 @@
   infixl 10 .|.
   infixl 10 .^.
 
+  -- | The `Bits` type class identifies types which support bitwise operations.
   class Bits b where
     (.&.) :: b -> b -> b
     (.|.) :: b -> b -> b
@@ -538,6 +789,10 @@
   infixr 2 ||
   infixr 3 &&
 
+  -- | The `BoolLike` type class identifies types which support Boolean operations.
+  -- |
+  -- | `BoolLike` instances are required to satisfy the laws of a _Boolean algebra_.
+  -- |
   class BoolLike b where
     (&&) :: b -> b -> b
     (||) :: b -> b -> b
@@ -575,6 +830,13 @@
 
   infixr 5 <>
 
+  -- | The `Semigroup` type class identifies an associative operation on a type.
+  -- |
+  -- | `Semigroup` instances are required to satisfy the following law:
+  -- |
+  -- | - Associativity: `(x <> y) <> z = x <> (y <> z)`
+  -- |
+  -- | For example, the `String` type is an instance of `Semigroup`, where `(<>)` is defined to be string concatenation.
   class Semigroup a where
     (<>) :: a -> a -> a
 
@@ -598,26 +860,57 @@
 
   infixr 5 ++
 
+  -- | `(++)` is an alias for `(<>)`.
   (++) :: forall s. (Semigroup s) => s -> s -> s
   (++) = (<>)
 
 module Data.Function where
 
+  -- | The `on` function is used to change the domain of a binary operator.
+  -- |
+  -- | For example, we can create a function which compares two records based on the values of their `x` properties:
+  -- |
+  -- | ```purescript
+  -- | compareX :: forall r. { x :: Number | r } -> { x :: Number | r } -> Ordering
+  -- | compareX = compare `on` _.x
+  -- | ```
   on :: forall a b c. (b -> b -> c) -> (a -> b) -> a -> a -> c
   on f g x y = g x `f` g y
 
+  -- | A function of zero arguments
   foreign import data Fn0 :: * -> *
+
+  -- | A function of one argument
   foreign import data Fn1 :: * -> * -> *
+
+  -- | A function of two arguments
   foreign import data Fn2 :: * -> * -> * -> *
+
+  -- | A function of three arguments
   foreign import data Fn3 :: * -> * -> * -> * -> *
+
+  -- | A function of four arguments
   foreign import data Fn4 :: * -> * -> * -> * -> * -> *
+
+  -- | A function of five arguments
   foreign import data Fn5 :: * -> * -> * -> * -> * -> * -> *
+
+  -- | A function of six arguments
   foreign import data Fn6 :: * -> * -> * -> * -> * -> * -> * -> *
+
+  -- | A function of seven arguments
   foreign import data Fn7 :: * -> * -> * -> * -> * -> * -> * -> * -> *
+
+  -- | A function of eight arguments
   foreign import data Fn8 :: * -> * -> * -> * -> * -> * -> * -> * -> * -> *
+
+  -- | A function of nine arguments
   foreign import data Fn9 :: * -> * -> * -> * -> * -> * -> * -> * -> * -> * -> *
+
+  -- | A function of ten arguments
   foreign import data Fn10 :: * -> * -> * -> * -> * -> * -> * -> * -> * -> * -> * -> *
 
+  -- | Create a function of no arguments
   foreign import mkFn0
     """
     function mkFn0(fn) {
@@ -627,6 +920,7 @@
     }
     """ :: forall a. (Unit -> a) -> Fn0 a
 
+  -- | Create a function of one argument
   foreign import mkFn1
     """
     function mkFn1(fn) {
@@ -636,6 +930,7 @@
     }
     """ :: forall a b. (a -> b) -> Fn1 a b
 
+  -- | Create a function of two arguments from a curried function
   foreign import mkFn2
     """
     function mkFn2(fn) {
@@ -645,6 +940,7 @@
     }
     """ :: forall a b c. (a -> b -> c) -> Fn2 a b c
 
+  -- | Create a function of three arguments from a curried function
   foreign import mkFn3
     """
     function mkFn3(fn) {
@@ -654,6 +950,7 @@
     }
     """ :: forall a b c d. (a -> b -> c -> d) -> Fn3 a b c d
 
+  -- | Create a function of four arguments from a curried function
   foreign import mkFn4
     """
     function mkFn4(fn) {
@@ -663,6 +960,7 @@
     }
     """ :: forall a b c d e. (a -> b -> c -> d -> e) -> Fn4 a b c d e
 
+  -- | Create a function of five arguments from a curried function
   foreign import mkFn5
     """
     function mkFn5(fn) {
@@ -672,6 +970,7 @@
     }
     """ :: forall a b c d e f. (a -> b -> c -> d -> e -> f) -> Fn5 a b c d e f
 
+  -- | Create a function of six arguments from a curried function
   foreign import mkFn6
     """
     function mkFn6(fn) {
@@ -681,6 +980,7 @@
     }
     """ :: forall a b c d e f g. (a -> b -> c -> d -> e -> f -> g) -> Fn6 a b c d e f g
 
+  -- | Create a function of seven arguments from a curried function
   foreign import mkFn7
     """
     function mkFn7(fn) {
@@ -690,6 +990,7 @@
     }
     """ :: forall a b c d e f g h. (a -> b -> c -> d -> e -> f -> g -> h) -> Fn7 a b c d e f g h
 
+  -- | Create a function of eight arguments from a curried function
   foreign import mkFn8
     """
     function mkFn8(fn) {
@@ -699,6 +1000,7 @@
     }
     """ :: forall a b c d e f g h i. (a -> b -> c -> d -> e -> f -> g -> h -> i) -> Fn8 a b c d e f g h i
 
+  -- | Create a function of nine arguments from a curried function
   foreign import mkFn9
     """
     function mkFn9(fn) {
@@ -708,6 +1010,7 @@
     }
     """ :: forall a b c d e f g h i j. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j) -> Fn9 a b c d e f g h i j
 
+  -- | Create a function of ten arguments from a curried function
   foreign import mkFn10
     """
     function mkFn10(fn) {
@@ -717,6 +1020,7 @@
     }
     """ :: forall a b c d e f g h i j k. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k) -> Fn10 a b c d e f g h i j k
 
+  -- | Apply a function of no arguments
   foreign import runFn0
     """
     function runFn0(fn) {
@@ -724,6 +1028,7 @@
     }
     """ :: forall a. Fn0 a -> a
 
+  -- | Apply a function of one argument
   foreign import runFn1
     """
     function runFn1(fn) {
@@ -733,6 +1038,7 @@
     }
     """ :: forall a b. Fn1 a b -> a -> b
 
+  -- | Apply a function of two arguments
   foreign import runFn2
     """
     function runFn2(fn) {
@@ -744,6 +1050,7 @@
     }
     """ :: forall a b c. Fn2 a b c -> a -> b -> c
 
+  -- | Apply a function of three arguments
   foreign import runFn3
     """
     function runFn3(fn) {
@@ -757,6 +1064,7 @@
     }
     """ :: forall a b c d. Fn3 a b c d -> a -> b -> c -> d
 
+  -- | Apply a function of four arguments
   foreign import runFn4
     """
     function runFn4(fn) {
@@ -772,6 +1080,7 @@
     }
     """ :: forall a b c d e. Fn4 a b c d e -> a -> b -> c -> d -> e
 
+  -- | Apply a function of five arguments
   foreign import runFn5
     """
     function runFn5(fn) {
@@ -789,6 +1098,7 @@
     }
     """ :: forall a b c d e f. Fn5 a b c d e f -> a -> b -> c -> d -> e -> f
 
+  -- | Apply a function of six arguments
   foreign import runFn6
     """
     function runFn6(fn) {
@@ -808,6 +1118,7 @@
     }
     """ :: forall a b c d e f g. Fn6 a b c d e f g -> a -> b -> c -> d -> e -> f -> g
 
+  -- | Apply a function of seven arguments
   foreign import runFn7
     """
     function runFn7(fn) {
@@ -829,6 +1140,7 @@
     }
     """ :: forall a b c d e f g h. Fn7 a b c d e f g h -> a -> b -> c -> d -> e -> f -> g -> h
 
+  -- | Apply a function of eight arguments
   foreign import runFn8
     """
     function runFn8(fn) {
@@ -852,6 +1164,7 @@
     }
     """ :: forall a b c d e f g h i. Fn8 a b c d e f g h i -> a -> b -> c -> d -> e -> f -> g -> h -> i
 
+  -- | Apply a function of nine arguments
   foreign import runFn9
     """
     function runFn9(fn) {
@@ -877,6 +1190,7 @@
     }
     """ :: forall a b c d e f g h i j. Fn9 a b c d e f g h i j -> a -> b -> c -> d -> e -> f -> g -> h -> i -> j
 
+  -- | Apply a function of ten arguments
   foreign import runFn10
     """
     function runFn10(fn) {
@@ -906,6 +1220,9 @@
 
 module Prelude.Unsafe where
 
+  -- | Find the element of an array at the specified index.
+  -- |
+  -- | Note: this function can cause unpredictable failure at runtime if the index is out-of-bounds.
   foreign import unsafeIndex
     """
     function unsafeIndex(xs) {
@@ -915,8 +1232,18 @@
     }
     """ :: forall a. [a] -> Number -> a
 
-module Control.Monad.Eff where
+module Control.Monad.Eff
+  ( Eff()
+  , Pure()
+  , runPure
+  , untilE, whileE, forE, foreachE
+  ) where
 
+  -- | The `Eff` type constructor is used to represent _native_ effects.
+  -- |
+  -- | See [Handling Native Effects with the Eff Monad](https://github.com/purescript/purescript/wiki/Handling-Native-Effects-with-the-Eff-Monad) for more details.
+  -- |
+  -- | The first type parameter is a row of effects which represents the contexts in which a computation can be run, and the second type parameter is the return type.
   foreign import data Eff :: # ! -> * -> *
 
   foreign import returnE
@@ -939,8 +1266,15 @@
     }
     """ :: forall e a b. Eff e a -> (a -> Eff e b) -> Eff e b
 
+  -- | The `Pure` type synonym represents _pure_ computations, i.e. ones in which all effects have been handled.
+  -- |
+  -- | The `runPure` function can be used to run pure computations and obtain their result.
   type Pure a = forall e. Eff e a
 
+  -- | Run a pure computation and return its result.
+  -- |
+  -- | Note: since this function has a rank-2 type, it may cause problems to apply this function using the `$` operator. The recommended approach
+  -- | is to use parentheses instead.
   foreign import runPure
     """
     function runPure(f) {
@@ -962,6 +1296,10 @@
 
   instance monadEff :: Monad (Eff e)
 
+  -- | Loop until a condition becomes `true`.
+  -- |
+  -- | `untilE b` is an effectful computation which repeatedly runs the effectful computation `b`,
+  -- | until its return value is `true`.
   foreign import untilE
     """
     function untilE(f) {
@@ -972,6 +1310,10 @@
     }
     """ :: forall e. Eff e Boolean -> Eff e Unit
 
+  -- | Loop while a condition is `true`.
+  -- |
+  -- | `whileE b m` is effectful computation which runs the effectful computation `b`. If its result is
+  -- | `true`, it runs the effectful computation `m` and loops. If not, the computation ends.
   foreign import whileE
     """
     function whileE(f) {
@@ -986,6 +1328,10 @@
     }
     """ :: forall e a. Eff e Boolean -> Eff e a -> Eff e Unit
 
+  -- | Loop over a consecutive collection of numbers.
+  -- |
+  -- | `forE lo hi f` runs the computation returned by the function `f` for each of the inputs
+  -- | between `lo` (inclusive) and `hi` (exclusive).
   foreign import forE
     """
     function forE(lo) {
@@ -1001,7 +1347,9 @@
     }
     """ :: forall e. Number -> Number -> (Number -> Eff e Unit) -> Eff e Unit
 
-
+  -- | Loop over an array of values.
+  -- |
+  -- | `foreach xs f` runs the computation returned by the function `f` for each of the inputs `xs`.
   foreign import foreachE
     """
     function foreachE(as) {
@@ -1019,6 +1367,9 @@
 
   import Control.Monad.Eff
 
+  -- | Change the type of an effectful computation, allowing it to be run in another context.
+  -- |
+  -- | Note: use of this function can result in arbitrary side-effects.
   foreign import unsafeInterleaveEff
     """
     function unsafeInterleaveEff(f) {
@@ -1030,8 +1381,10 @@
 
   import Control.Monad.Eff
 
+  -- | The `Trace` effect represents those computations which write to the console.
   foreign import data Trace :: !
 
+  -- | Write a `String` to the console.
   foreign import trace
     """
     function trace(s) {
@@ -1042,6 +1395,7 @@
     }
     """ :: forall r. String -> Eff (trace :: Trace | r) Unit
 
+  -- | Write a value to the console, using its `Show` instance to produce a `String`.
   print :: forall a r. (Show a) => a -> Eff (trace :: Trace | r) Unit
   print o = trace (show o)
 
@@ -1049,10 +1403,17 @@
 
   import Control.Monad.Eff
 
+  -- | The `ST` effect represents _local mutation_, i.e. mutation which does not "escape" into the surrounding computation.
+  -- |
+  -- | An `ST` computation is parameterized by a phantom type which is used to restrict the set of reference cells it is allowed to access.
+  -- |
+  -- | The `runST` function can be used to handle the `ST` effect.
   foreign import data ST :: * -> !
 
+  -- | The type `STRef s a` represents a mutable reference holding a value of type `a`, which can be used with the `ST s` effect.
   foreign import data STRef :: * -> * -> *
 
+  -- | Create a new mutable reference.
   foreign import newSTRef
     """
     function newSTRef(val) {
@@ -1062,6 +1423,7 @@
     }
     """ :: forall a h r. a -> Eff (st :: ST h | r) (STRef h a)
 
+  -- | Read the current value of a mutable reference.
   foreign import readSTRef
     """
     function readSTRef(ref) {
@@ -1071,6 +1433,7 @@
     }
     """ :: forall a h r. STRef h a -> Eff (st :: ST h | r) a
 
+  -- | Modify the value of a mutable reference by applying a function to the current value.
   foreign import modifySTRef
     """
     function modifySTRef(ref) {
@@ -1082,6 +1445,7 @@
     }
     """ :: forall a h r. STRef h a -> (a -> a) -> Eff (st :: ST h | r) a
 
+  -- | Set the value of a mutable reference.
   foreign import writeSTRef
     """
     function writeSTRef(ref) {
@@ -1093,6 +1457,12 @@
     }
     """ :: forall a h r. STRef h a -> a -> Eff (st :: ST h | r) a
 
+  -- | Run an `ST` computation.
+  -- |
+  -- | Note: the type of `runST` uses a rank-2 type to constrain the phantom type `s`, such that the computation must not leak any mutable references
+  -- | to the surrounding computation.
+  -- |
+  -- | It may cause problems to apply this function using the `$` operator. The recommended approach is to use parentheses instead.
   foreign import runST
     """
     function runST(f) {
@@ -1100,5 +1470,9 @@
     }
     """ :: forall a r. (forall h. Eff (st :: ST h | r) a) -> Eff r a
 
+  -- | A convenience function which combines `runST` with `runPure`, which can be used when the only required effect is `ST`.
+  -- |
+  -- | Note: since this function has a rank-2 type, it may cause problems to apply this function using the `$` operator. The recommended approach
+  -- | is to use parentheses instead.
   pureST :: forall a. (forall h r. Eff (st :: ST h | r) a) -> a
   pureST st = runPure (runST st)
diff --git a/psc-docs/Main.hs b/psc-docs/Main.hs
--- a/psc-docs/Main.hs
+++ b/psc-docs/Main.hs
@@ -30,21 +30,37 @@
 import System.Exit (exitSuccess, exitFailure)
 import System.IO (hPutStrLn, stderr)
 
+import Etags
+import Ctags
+
+-- Available output formats
+data Format = Markdown -- Output documentation in Markdown format
+               | Ctags -- Output ctags symbol index suitable for use with vi
+               | Etags -- Output etags symbol index suitable for use with emacs
+
 data PSCDocsOptions = PSCDocsOptions
-  { pscdInputFiles  :: [FilePath]
+  { pscdFormat :: Format
+  , pscdInputFiles  :: [FilePath]
   }
 
 docgen :: PSCDocsOptions -> IO ()
-docgen (PSCDocsOptions input) = do
+docgen (PSCDocsOptions fmt input) = do
   e <- P.parseModulesFromFiles (fromMaybe "") <$> mapM (fmap (first Just) . parseFile) (nub input)
   case e of
     Left err -> do
       hPutStrLn stderr $ show err
       exitFailure
     Right ms -> do
-      putStrLn . runDocs $ renderModules (map snd ms)
+      case fmt of
+       Markdown -> putStrLn . runDocs $ renderModules (map snd ms)
+       Etags -> ldump $ dumpEtags $ pairs ms
+       Ctags -> ldump $ dumpCtags $ pairs ms
       exitSuccess
-
+  where pairs :: [(Maybe String, m)] -> [(String, m)]
+        pairs = map (\(k,m) -> (fromMaybe "" k,m))
+        ldump :: [String] -> IO ()
+        ldump = mapM_ putStrLn
+    
 parseFile :: FilePath -> IO (FilePath, String)
 parseFile input = (,) input <$> readFile input
 
@@ -86,9 +102,12 @@
   mapM_ renderModule ms
 
 renderModule :: P.Module -> Docs
-renderModule mdl@(P.Module moduleName _ exps) = do
+renderModule mdl@(P.Module coms moduleName _ exps) = do
     headerLevel 2 $ "Module " ++ P.runModuleName moduleName
     spacer
+    unless (null coms) $ do
+      renderComments coms
+      spacer
     renderTopLevel exps (P.exportedDeclarations mdl)
     spacer
 
@@ -219,8 +238,20 @@
      metavar "FILE"
   <> help "The input .purs file(s)"
 
+instance Read Format where
+    readsPrec _ "etags" = [(Etags, "")]
+    readsPrec _ "ctags" = [(Ctags, "")]
+    readsPrec _ "markdown" = [(Markdown, "")]
+    readsPrec _ _ = []    
+
+format :: Parser Format
+format = option auto $ value Markdown
+         <> long "format"
+         <> metavar "FORMAT"
+         <> help "Set output FORMAT (markdown | etags | ctags)"  
+
 pscDocsOptions :: Parser PSCDocsOptions
-pscDocsOptions = PSCDocsOptions <$> many inputFile
+pscDocsOptions = PSCDocsOptions <$> format <*> many inputFile
 
 main :: IO ()
 main = execParser opts >>= docgen
diff --git a/psc-make/Main.hs b/psc-make/Main.hs
--- a/psc-make/Main.hs
+++ b/psc-make/Main.hs
@@ -155,7 +155,7 @@
                     <*> noMagicDo
                     <*> pure Nothing
                     <*> noOpts
-                    <*> comments
+                    <*> (not <$> comments)
                     <*> verboseErrors
                     <*> pure P.MakeOptions
 
diff --git a/psc/Main.hs b/psc/Main.hs
--- a/psc/Main.hs
+++ b/psc/Main.hs
@@ -180,7 +180,7 @@
                     <*> runMain
                     <*> noOpts
                     <*> verboseErrors
-                    <*> comments
+                    <*> (not <$> comments)
                     <*> additionalOptions
   where
   additionalOptions =
diff --git a/psci/Commands.hs b/psci/Commands.hs
--- a/psci/Commands.hs
+++ b/psci/Commands.hs
@@ -32,7 +32,7 @@
   -- |
   -- Import a module from a loaded file
   --
-  | Import ModuleName
+  | Import ImportedModule
   -- |
   -- Browse a module
   --
@@ -50,9 +50,9 @@
   --
   | Reset
   -- |
-  -- Binds a value to a name
+  -- Add some declarations to the current evaluation context.
   --
-  | Let [Declaration]
+  | Decls [Declaration]
   -- |
   -- Find the type of an expression
   --
@@ -66,22 +66,13 @@
   --
   | Show String
 
--- |
--- The help menu.
+-- | All of the data that is contained by an ImportDeclaration in the AST.
+-- That is:
 --
-help :: [(String, String, String)]
-help =
-  [ (":?", "",         "Show this help menu")
-  , (":i", "<module>", "Import <module> for use in PSCI")
-  , (":b", "<module>", "Browse <module>")
-  , (":m", "<file>",   "Load <file> for importing")
-  , (":q", "",         "Quit PSCi")
-  , (":r", "",         "Reset")
-  , (":s", "import",   "Show imported modules")
-  , (":s", "loaded",   "Show loaded modules")
-  , (":t", "<expr>",   "Show the type of <expr>")
-  , (":k", "<type>",   "Show the kind of <type>")
-  ]
-
-commands :: [String]
-commands = map (\ (a, _, _) -> a) help
+-- * A module name, the name of the module which is being imported
+-- * An ImportDeclarationType which specifies whether there is an explicit
+--   import list, a hiding list, or neither.
+-- * If the module is imported qualified, its qualified name in the importing
+--   module. Otherwise, Nothing.
+--
+type ImportedModule = (ModuleName, ImportDeclarationType, Maybe ModuleName)
diff --git a/psci/Main.hs b/psci/Main.hs
--- a/psci/Main.hs
+++ b/psci/Main.hs
@@ -54,12 +54,14 @@
 import qualified Language.PureScript.Names as N
 import qualified Paths_purescript as Paths
 
-import Commands as C
+import qualified Commands as C
+import qualified Directive as D
 import Parser
 
 data PSCiOptions = PSCiOptions
-  { psciMultiLineMode :: Bool
+  { psciMultiLineMode  :: Bool
   , psciInputFile      :: [FilePath]
+  , psciInputNodeFlags :: [String]
   }
 
 -- |
@@ -70,11 +72,16 @@
 --
 data PSCiState = PSCiState
   { psciImportedFilenames   :: [FilePath]
-  , psciImportedModuleNames :: [P.ModuleName]
+  , psciImportedModules     :: [C.ImportedModule]
   , psciLoadedModules       :: [(Either P.RebuildPolicy FilePath, P.Module)]
   , psciLetBindings         :: [P.Declaration]
+  , psciNodeFlags           :: [String]
   }
 
+psciImportedModuleNames :: PSCiState -> [P.ModuleName]
+psciImportedModuleNames (PSCiState{psciImportedModules = is}) =
+  map (\(mn, _, _) -> mn) is
+
 -- State helpers
 
 -- |
@@ -86,8 +93,8 @@
 -- |
 -- Updates the state to have more imported modules.
 --
-updateImports :: P.ModuleName -> PSCiState -> PSCiState
-updateImports name st = st { psciImportedModuleNames = name : psciImportedModuleNames st }
+updateImportedModules :: C.ImportedModule -> PSCiState -> PSCiState
+updateImportedModules im st = st { psciImportedModules = im : psciImportedModules st }
 
 -- |
 -- Updates the state to have more loaded files.
@@ -105,8 +112,8 @@
 -- |
 -- Load the necessary modules.
 --
-defaultImports :: [P.ModuleName]
-defaultImports = [P.ModuleName [P.ProperName "Prelude"]]
+defaultImports :: [C.ImportedModule]
+defaultImports = [(P.ModuleName [P.ProperName "Prelude"], D.Implicit, Nothing)]
 
 -- |
 -- Locates the node executable.
@@ -168,9 +175,17 @@
 --
 helpMessage :: String
 helpMessage = "The following commands are available:\n\n    " ++
-  intercalate "\n    " (map line C.help)
-  where line :: (String, String, String) -> String
-        line (cmd, arg, desc) = intercalate " " [cmd, arg, replicate (11 - length arg) ' ', desc]
+  intercalate "\n    " (map line D.help)
+  where
+    line :: (D.Directive, String, String) -> String
+    line (dir, arg, desc) = intercalate " "
+          [ cmd
+          , replicate (11 - length cmd) ' '
+          , arg
+          , replicate (11 - length arg) ' '
+          , desc
+          ]
+      where cmd = ":" ++ head (D.commands dir)
 
 -- |
 -- The welcome prologue.
@@ -203,19 +218,23 @@
 -- Decide what kind of completion we need based on input.
 completionContext :: String -> String -> Maybe CompletionContext
 completionContext cmd@"" _ = Just $ Multiple [Command cmd, Identifier]
-completionContext cmd@(':' : _ ) _
-  | cmd `elem` C.commands || cmd == ":" = Just $ Command cmd
-completionContext (':' : c : _) word = case c of
-  'i' -> Just Module
-  'b' -> Just Module
-  'm' -> Just $ FilePath word
-  'q' -> Nothing
-  'r' -> Nothing
-  '?' -> Nothing
-  's' -> Just $ Fixed ["import", "loaded"]
-  't' -> Just Identifier
-  'k' -> Just Type
-  _   -> Nothing
+completionContext (':' : cmd) word =
+  case D.parseDirective dstr of
+    Just directive | dstr `elem` D.commands directive -> context directive
+    _ -> Just $ Command cmd
+  where
+  dstr :: String
+  dstr = takeWhile (not . isSpace) cmd
+
+  context :: D.Directive -> Maybe CompletionContext
+  context D.Browse = Just Module
+  context D.Load = Just $ FilePath word
+  context D.Quit = Nothing
+  context D.Reset = Nothing
+  context D.Help = Nothing
+  context D.Show = Just $ Fixed ["import", "loaded"]
+  context D.Type = Just Identifier
+  context D.Kind = Just Type
 completionContext _ _ = Just Identifier
 
 -- |
@@ -236,13 +255,16 @@
     return $ sortBy sorter completions
 
   getCompletion :: CompletionContext -> StateT PSCiState IO [Either String Completion]
-  getCompletion (Command s) = return $ (map Left) $ nub $ filter (isPrefixOf s) C.commands
   getCompletion (FilePath f) = (map Right) <$> listFiles f
   getCompletion Module = (map Left) <$> getModuleNames
   getCompletion Identifier = (map Left) <$> ((++) <$> getIdentNames <*> getDctorNames)
   getCompletion Type = (map Left) <$> getTypeNames
   getCompletion (Fixed list) = return $ (map Left) list
   getCompletion (Multiple contexts) = concat <$> mapM getCompletion contexts
+  getCompletion (Command cmd) = return . map (Left . (":" ++)) . nub $ matching
+    where
+    matching :: [String]
+    matching = filter (isPrefixOf cmd) . concatMap (D.commands) $ D.directives
 
   getLoadedModules :: StateT PSCiState IO [P.Module]
   getLoadedModules = map snd . psciLoadedModules <$> get
@@ -284,7 +306,7 @@
             getTypeName _ = Nothing
 
   identNames :: P.Module -> [N.Ident]
-  identNames (P.Module _ ds exports) = nub [ ident | ident <- mapMaybe (getDeclName exports) (D.flattenDecls ds) ]
+  identNames (P.Module _ _ ds exports) = nub [ ident | ident <- mapMaybe (getDeclName exports) (D.flattenDecls ds) ]
     where getDeclName :: Maybe [P.DeclarationRef] -> P.Declaration -> Maybe P.Ident
           getDeclName exts decl@(P.ValueDeclaration ident _ _ _)  | P.isExported exts decl = Just ident
           getDeclName exts decl@(P.ExternDeclaration _ ident _ _) | P.isExported exts decl = Just ident
@@ -305,7 +327,7 @@
           onlyDataDecls = (filter P.isDataDecl (P.exportedDeclarations m))
 
   moduleNames :: [P.Module] -> [String]
-  moduleNames ms = nub [show moduleName | P.Module moduleName _ _ <- ms]
+  moduleNames ms = nub [show moduleName | P.Module _ moduleName _ _ <- ms]
 
   sorter :: Completion -> Completion -> Ordering
   sorter (Completion _ d1 _) (Completion _ d2 _) = if ":" `isPrefixOf` d1 then LT else compare d1 d2
@@ -351,10 +373,9 @@
 -- Makes a volatile module to execute the current expression.
 --
 createTemporaryModule :: Bool -> PSCiState -> P.Expr -> P.Module
-createTemporaryModule exec PSCiState{psciImportedModuleNames = imports, psciLetBindings = lets} val =
+createTemporaryModule exec PSCiState{psciImportedModules = imports, psciLetBindings = lets} val =
   let
     moduleName = P.ModuleName [P.ProperName "$PSCI"]
-    importDecl m = P.ImportDeclaration m P.Unqualified Nothing
     traceModule = P.ModuleName [P.ProperName "Debug", P.ProperName "Trace"]
     trace = P.Var (P.Qualified (Just traceModule) (P.Ident "print"))
     mainValue = P.App trace (P.Var (P.Qualified Nothing (P.Ident "it")))
@@ -362,32 +383,33 @@
     mainDecl = P.ValueDeclaration (P.Ident "main") P.Value [] $ Right mainValue
     decls = if exec then [itDecl, mainDecl] else [itDecl]
   in
-    P.Module moduleName ((importDecl `map` imports) ++ lets ++ decls) Nothing
+    P.Module [] moduleName ((importDecl `map` imports) ++ lets ++ decls) Nothing
 
 
 -- |
 -- Makes a volatile module to hold a non-qualified type synonym for a fully-qualified data type declaration.
 --
 createTemporaryModuleForKind :: PSCiState -> P.Type -> P.Module
-createTemporaryModuleForKind PSCiState{psciImportedModuleNames = imports} typ =
+createTemporaryModuleForKind PSCiState{psciImportedModules = imports} typ =
   let
     moduleName = P.ModuleName [P.ProperName "$PSCI"]
-    importDecl m = P.ImportDeclaration m P.Unqualified Nothing
     itDecl = P.TypeSynonymDeclaration (P.ProperName "IT") [] typ
   in
-    P.Module moduleName ((importDecl `map` imports) ++ [itDecl]) Nothing
+    P.Module [] moduleName ((importDecl `map` imports) ++ [itDecl]) Nothing
 
 -- |
 -- Makes a volatile module to execute the current imports.
 --
 createTemporaryModuleForImports :: PSCiState -> P.Module
-createTemporaryModuleForImports PSCiState{psciImportedModuleNames = imports} =
+createTemporaryModuleForImports PSCiState{psciImportedModules = imports} =
   let
     moduleName = P.ModuleName [P.ProperName "$PSCI"]
-    importDecl m = P.ImportDeclaration m P.Unqualified Nothing
   in
-    P.Module moduleName (importDecl `map` imports) Nothing
+    P.Module [] moduleName (importDecl `map` imports) Nothing
 
+importDecl :: C.ImportedModule -> P.Declaration
+importDecl (mn, declType, asQ) = P.ImportDeclaration mn declType asQ
+
 modulesDir :: FilePath
 modulesDir = ".psci_modules" ++ pathSeparator : "node_modules"
 
@@ -401,24 +423,25 @@
 handleDeclaration val = do
   st <- PSCI $ lift get
   let m = createTemporaryModule True st val
+  let nodeArgs = psciNodeFlags st ++ [indexFile]
   e <- psciIO . runMake $ P.make modulesDir (psciLoadedModules st ++ [(Left P.RebuildAlways, m)]) []
   case e of
     Left err -> PSCI $ outputStrLn err
     Right _ -> do
       psciIO $ writeFile indexFile "require('$PSCI').main();"
       process <- psciIO findNodeProcess
-      result  <- psciIO $ traverse (\node -> readProcessWithExitCode node [indexFile] "") process
+      result  <- psciIO $ traverse (\node -> readProcessWithExitCode node nodeArgs "") process
       case result of
         Just (ExitSuccess,   out, _)   -> PSCI $ outputStrLn out
         Just (ExitFailure _, _,   err) -> PSCI $ outputStrLn err
         Nothing                        -> PSCI $ outputStrLn "Couldn't find node.js"
 
 -- |
--- Takes a let declaration and updates the environment, then run a make. If the declaration fails,
--- restore the pre-let environment.
+-- Takes a list of declarations and updates the environment, then run a make. If the declaration fails,
+-- restore the original environment.
 --
-handleLet :: [P.Declaration] -> PSCI ()
-handleLet ds = do
+handleDecls :: [P.Declaration] -> PSCI ()
+handleDecls ds = do
   st <- PSCI $ lift get
   let st' = updateLets ds st
   let m = createTemporaryModule False st' (P.ObjectLiteral [])
@@ -436,24 +459,44 @@
   psciIO $ readModules loadedModules >>= putStrLn
   return ()
   where readModules = return . unlines . sort . nub . map toModuleName
-        toModuleName =  N.runModuleName . (\ (D.Module mdName _ _) -> mdName) . snd
+        toModuleName =  N.runModuleName . (\ (D.Module _ mdName _ _) -> mdName) . snd
 
 -- |
 -- Show the imported modules in psci.
 --
 handleShowImportedModules :: PSCI ()
 handleShowImportedModules = do
-  PSCiState { psciImportedModuleNames = importedModuleNames } <- PSCI $ lift get
-  psciIO $ readModules importedModuleNames >>= putStrLn
+  PSCiState { psciImportedModules = importedModules } <- PSCI $ lift get
+  psciIO $ showModules importedModules >>= putStrLn
   return ()
-  where readModules = return . unlines . sort . map N.runModuleName
+  where
+  showModules = return . unlines . sort . map showModule
+  showModule (mn, declType, asQ) =
+    "import " ++ case asQ of
+      Just mn' -> "qualified " ++ N.runModuleName mn ++ " as " ++ N.runModuleName mn'
+      Nothing  -> N.runModuleName mn ++ " " ++ showDeclType declType
 
+  showDeclType D.Implicit = ""
+  showDeclType (D.Explicit refs) = refsList refs
+  showDeclType (D.Hiding refs) = "hiding " ++ refsList refs
+  refsList refs = "(" ++ commaList (map showRef refs) ++ ")"
+
+  showRef :: P.DeclarationRef -> String
+  showRef (D.TypeRef pn dctors) = show pn ++ "(" ++ maybe ".." (commaList . map N.runProperName) dctors ++ ")"
+  showRef (D.ValueRef ident) = show ident
+  showRef (D.TypeClassRef pn) = show pn
+  showRef (D.TypeInstanceRef ident) = show ident
+  showRef (D.PositionedDeclarationRef _ _ ref) = showRef ref
+
+  commaList :: [String] -> String
+  commaList = intercalate ", "
+
 -- |
 -- Imports a module, preserving the initial state on failure.
 --
-handleImport :: P.ModuleName -> PSCI ()
-handleImport moduleName = do
-   st <- updateImports moduleName <$> PSCI (lift get)
+handleImport :: C.ImportedModule -> PSCI ()
+handleImport im = do
+   st <- updateImportedModules im <$> PSCI (lift get)
    let m = createTemporaryModuleForImports st
    e <- psciIO . runMake $ P.make modulesDir (psciLoadedModules st ++ [(Left P.RebuildAlways, m)]) []
    case e of
@@ -507,7 +550,7 @@
   case env of
     Left err -> PSCI $ outputStrLn err
     Right env' ->
-      if moduleName `notElem` (nub . map ((\ (P.Module modName _ _ ) -> modName) . snd)) loadedModules
+      if moduleName `notElem` (nub . map ((\ (P.Module _ modName _ _ ) -> modName) . snd)) loadedModules
         then PSCI $ outputStrLn $ "Module '" ++ N.runModuleName moduleName ++ "' is not valid."
         else printModuleSignatures moduleName env'
 
@@ -528,7 +571,7 @@
           let chk = P.CheckState env' 0 0 (Just mName)
               k   = L.runStateT (P.unCheck (P.kindOf mName typ')) chk
           case k of
-            Left errStack   -> PSCI . outputStrLn . P.prettyPrintErrorStack False $ errStack
+            Left errStack   -> PSCI . outputStrLn . P.prettyPrintMultipleErrors False $ errStack
             Right (kind, _) -> PSCI . outputStrLn . P.prettyPrintKind $ kind
         Nothing -> PSCI $ outputStrLn "Could not find kind"
 
@@ -537,7 +580,7 @@
 -- |
 -- Parses the input and returns either a Metacommand or an expression.
 --
-getCommand :: Bool -> InputT (StateT PSCiState IO) (Either String (Maybe Command))
+getCommand :: Bool -> InputT (StateT PSCiState IO) (Either String (Maybe C.Command))
 getCommand singleLineMode = do
   firstLine <- getInputLine "> "
   case firstLine of
@@ -552,12 +595,12 @@
 -- |
 -- Performs an action for each meta-command given, and also for expressions..
 --
-handleCommand :: Command -> PSCI ()
-handleCommand (Expression val) = handleDeclaration val
-handleCommand Help = PSCI $ outputStrLn helpMessage
-handleCommand (Import moduleName) = handleImport moduleName
-handleCommand (Let l) = handleLet l
-handleCommand (LoadFile filePath) = do
+handleCommand :: C.Command -> PSCI ()
+handleCommand (C.Expression val) = handleDeclaration val
+handleCommand C.Help = PSCI $ outputStrLn helpMessage
+handleCommand (C.Import im) = handleImport im
+handleCommand (C.Decls l) = handleDecls l
+handleCommand (C.LoadFile filePath) = do
   absPath <- psciIO $ expandTilde filePath
   exists <- psciIO $ doesFileExist absPath
   if exists then do
@@ -568,22 +611,22 @@
       Right mods -> PSCI . lift $ modify (updateModules (map ((,) (Right absPath)) mods))
   else
     PSCI . outputStrLn $ "Couldn't locate: " ++ filePath
-handleCommand Reset = do
+handleCommand C.Reset = do
   files <- psciImportedFilenames <$> PSCI (lift get)
   PSCI . lift . modify $ \st -> st
     { psciImportedFilenames   = files
-    , psciImportedModuleNames = defaultImports
+    , psciImportedModules     = defaultImports
     , psciLetBindings         = []
     }
   loadAllImportedModules
-handleCommand (TypeOf val) = handleTypeOf val
-handleCommand (KindOf typ) = handleKindOf typ
-handleCommand (Browse moduleName) = handleBrowse moduleName
-handleCommand (Show "loaded") = handleShowLoadedModules
-handleCommand (Show "import") = handleShowImportedModules
+handleCommand (C.TypeOf val) = handleTypeOf val
+handleCommand (C.KindOf typ) = handleKindOf typ
+handleCommand (C.Browse moduleName) = handleBrowse moduleName
+handleCommand (C.Show "loaded") = handleShowLoadedModules
+handleCommand (C.Show "import") = handleShowImportedModules
 handleCommand _ = PSCI $ outputStrLn "Unknown command"
 
-loadUserConfig :: IO (Maybe [Command])
+loadUserConfig :: IO (Maybe [C.Command])
 loadUserConfig = do
   configFile <- (</> ".psci") <$> getCurrentDirectory
   exists <- doesFileExist configFile
@@ -608,7 +651,7 @@
     Right modules -> do
       historyFilename <- getHistoryFilename
       let settings = defaultSettings { historyFile = Just historyFilename }
-      flip evalStateT (PSCiState psciInputFile defaultImports modules []) . runInputT (setComplete completion settings) $ do
+      flip evalStateT (PSCiState psciInputFile defaultImports modules [] psciInputNodeFlags) . runInputT (setComplete completion settings) $ do
         outputStrLn prologueMessage
         traverse_ (mapM_ (runPSCI . handleCommand)) config
         go
@@ -619,7 +662,7 @@
           case c of
             Left err -> outputStrLn err >> go
             Right Nothing -> go
-            Right (Just Quit) -> outputStrLn quitMessage
+            Right (Just C.Quit) -> outputStrLn quitMessage
             Right (Just c') -> runPSCI (loadAllImportedModules >> handleCommand c') >> go
 
 multiLineMode :: Parser Bool
@@ -633,9 +676,19 @@
      metavar "FILE"
   <> Opts.help "Optional .purs files to load on start"
 
+nodeFlagsFlag :: Parser [String]
+nodeFlagsFlag = option parser $
+     long "node-opts"
+  <> metavar "NODE_OPTS"
+  <> value []
+  <> Opts.help "Flags to pass to node, separated by spaces"
+  where
+    parser = words <$> str
+
 psciOptions :: Parser PSCiOptions
 psciOptions = PSCiOptions <$> multiLineMode
                           <*> many inputFile
+                          <*> nodeFlagsFlag
 
 main :: IO ()
 main = execParser opts >>= loop
diff --git a/psci/Parser.hs b/psci/Parser.hs
--- a/psci/Parser.hs
+++ b/psci/Parser.hs
@@ -13,13 +13,14 @@
 --
 -----------------------------------------------------------------------------
 
-module Parser (
-    parseCommand
+module Parser
+  ( parseCommand
   ) where
 
 import Prelude hiding (lex)
 
-import Commands
+import qualified Commands as C
+import qualified Directive as D
 
 import Data.Char (isSpace)
 
@@ -33,50 +34,92 @@
 -- |
 -- Parses PSCI metacommands or expressions input from the user.
 --
-parseCommand :: String -> Either String Command
+parseCommand :: String -> Either String C.Command
 parseCommand cmdString =
-  case splitCommand cmdString of
-    Just ('?', _) -> return Help
-    Just ('q', _) -> return Quit
-    Just ('r', _) -> return Reset
-    Just ('i', moduleName) -> Import <$> parseRest P.moduleName moduleName
-    Just ('b', moduleName) -> Browse <$> parseRest P.moduleName moduleName
-    Just ('m', filename) -> return $ LoadFile (trimEnd filename)
-    Just ('s', command) -> return $ Show (trimEnd command)
-    Just ('t', expr) -> TypeOf <$> parseRest P.parseValue expr
-    Just ('k', ty) -> KindOf <$> parseRest P.parseType ty
-    Just _ -> Left $ "Unrecognized command. Type :? for help."
-    Nothing -> parseRest (psciLet <|> psciExpression) cmdString
+  case cmdString of
+    (':' : cmd) -> parseDirective cmd
+    _ -> parseRest psciCommand cmdString
+
+parseRest :: P.TokenParser a -> String -> Either String a
+parseRest p s = either (Left . show) Right $ do
+  ts <- P.lex "" s
+  P.runTokenParser "" (p <* eof) ts
+
+psciCommand :: P.TokenParser C.Command
+psciCommand = choice (map try parsers)
   where
-  parseRest :: P.TokenParser a -> String -> Either String a
-  parseRest p s = either (Left . show) Right $ do
-    ts <- P.lex "" s
-    P.runTokenParser "" (p <* eof) ts
+  parsers =
+    [ psciLet
+    , psciImport
+    , psciOtherDeclaration
+    , psciExpression
+    ]
 
-  trimEnd :: String -> String
-  trimEnd = reverse . dropWhile isSpace . reverse
+trim :: String -> String
+trim = trimEnd . trimStart
 
-  -- |
-  -- Split a command into a command char and the trailing string
-  --
-  splitCommand :: String -> Maybe (Char, String)
-  splitCommand (':' : c : s) = Just (c, dropWhile isSpace s)
-  splitCommand _ = Nothing
+trimStart :: String -> String
+trimStart = dropWhile isSpace
 
-  -- |
-  -- Parses expressions entered at the PSCI repl.
-  --
-  psciExpression :: P.TokenParser Command
-  psciExpression = Expression <$> P.parseValue
+trimEnd :: String -> String
+trimEnd = reverse . trimStart . reverse
 
-  -- |
-  -- PSCI version of @let@.
-  -- This is essentially let from do-notation.
-  -- However, since we don't support the @Eff@ monad,
-  -- we actually want the normal @let@.
-  --
-  psciLet :: P.TokenParser Command
-  psciLet = Let <$> (P.reserved "let" *> P.indented *> manyDecls)
-    where
-    manyDecls :: P.TokenParser [P.Declaration]
-    manyDecls = C.mark (many1 (C.same *> P.parseDeclaration))
+parseDirective :: String -> Either String C.Command
+parseDirective cmd =
+  case D.parseDirective dstr of
+    Just D.Help -> return C.Help
+    Just D.Quit -> return C.Quit
+    Just D.Reset -> return C.Reset
+    Just D.Browse -> C.Browse <$> parseRest P.moduleName arg
+    Just D.Load -> return $ C.LoadFile (trim arg)
+    Just D.Show -> return $ C.Show (trim arg)
+    Just D.Type -> C.TypeOf <$> parseRest P.parseValue arg
+    Just D.Kind -> C.KindOf <$> parseRest P.parseType arg
+    Nothing -> Left $ "Unrecognized command. Type :? for help."
+  where (dstr, arg) = break isSpace cmd
+
+-- |
+-- Parses expressions entered at the PSCI repl.
+--
+psciExpression :: P.TokenParser C.Command
+psciExpression = C.Expression <$> P.parseValue
+
+-- |
+-- PSCI version of @let@.
+-- This is essentially let from do-notation.
+-- However, since we don't support the @Eff@ monad,
+-- we actually want the normal @let@.
+--
+psciLet :: P.TokenParser C.Command
+psciLet = C.Decls <$> (P.reserved "let" *> P.indented *> manyDecls)
+  where
+  manyDecls :: P.TokenParser [P.Declaration]
+  manyDecls = C.mark (many1 (C.same *> P.parseLocalDeclaration))
+
+-- | Imports must be handled separately from other declarations, so that
+-- :show import works, for example.
+psciImport :: P.TokenParser C.Command
+psciImport = C.Import <$> P.parseImportDeclaration'
+
+-- | Any other declaration that we don't need a 'special case' parser for
+-- (like let or import declarations).
+psciOtherDeclaration :: P.TokenParser C.Command
+psciOtherDeclaration = C.Decls . (:[]) <$> do
+  decl <- discardPositionInfo <$> P.parseDeclaration
+  if acceptable decl
+    then return decl
+    else fail "this kind of declaration is not supported in psci"
+
+discardPositionInfo :: P.Declaration -> P.Declaration
+discardPositionInfo (P.PositionedDeclaration _ _ d) = d
+discardPositionInfo d = d
+
+acceptable :: P.Declaration -> Bool
+acceptable (P.DataDeclaration _ _ _ _) = True
+acceptable (P.TypeSynonymDeclaration _ _ _) = True
+acceptable (P.ExternDeclaration _ _ _ _) = True
+acceptable (P.ExternDataDeclaration _ _) = True
+acceptable (P.ExternInstanceDeclaration _ _ _ _) = True
+acceptable (P.TypeClassDeclaration _ _ _ _) = True
+acceptable (P.TypeInstanceDeclaration _ _ _ _ _) = True
+acceptable _ = False
diff --git a/purescript.cabal b/purescript.cabal
--- a/purescript.cabal
+++ b/purescript.cabal
@@ -1,5 +1,5 @@
 name: purescript
-version: 0.6.8
+version: 0.6.9
 cabal-version: >=1.8
 build-type: Simple
 license: MIT
@@ -35,7 +35,8 @@
                    utf8-string >= 1 && < 2,
                    pattern-arrows >= 0.0.2 && < 0.1,
                    file-embed >= 0.0.7 && < 0.0.8,
-                   time -any
+                   time -any,
+                   boxes >= 0.1.4 && < 0.2.0
     exposed-modules: Language.PureScript
                      Language.PureScript.AST
                      Language.PureScript.AST.Binders
@@ -96,7 +97,6 @@
                      Language.PureScript.Sugar.Operators
                      Language.PureScript.Sugar.TypeClasses
                      Language.PureScript.Sugar.TypeDeclarations
-                     Language.PureScript.Supply
                      Language.PureScript.Traversals
                      Language.PureScript.TypeChecker
                      Language.PureScript.TypeChecker.Entailment
@@ -112,6 +112,8 @@
                      Language.PureScript.Types
 
                      Control.Monad.Unify
+                     Control.Monad.Supply
+                     Control.Monad.Supply.Class
     exposed: True
     buildable: True
     hs-source-dirs: src
diff --git a/src/Control/Monad/Supply.hs b/src/Control/Monad/Supply.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Supply.hs
@@ -0,0 +1,43 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  Control.Monad.Supply
+-- Copyright   :  (c) Phil Freeman 2014
+-- License     :  MIT
+--
+-- Maintainer  :  Phil Freeman <paf31@cantab.net>
+-- Stability   :  experimental
+-- Portability :
+--
+-- |
+-- Fresh variable supply
+--
+-----------------------------------------------------------------------------
+
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module Control.Monad.Supply where
+
+import Data.Functor.Identity
+
+import Control.Applicative
+import Control.Monad.State
+import Control.Monad.Except
+import Control.Monad.Reader
+import Control.Monad.Writer
+
+newtype SupplyT m a = SupplyT { unSupplyT :: StateT Integer m a } 
+  deriving (Functor, Applicative, Monad, MonadTrans, MonadError e, MonadWriter w, MonadReader r)
+
+runSupplyT :: Integer -> SupplyT m a -> m (a, Integer)
+runSupplyT n = flip runStateT n . unSupplyT
+
+evalSupplyT :: (Functor m) => Integer -> SupplyT m a -> m a
+evalSupplyT n = fmap fst . runSupplyT n
+
+type Supply = SupplyT Identity
+
+runSupply :: Integer -> Supply a -> (a, Integer)
+runSupply n = runIdentity . runSupplyT n
+
+evalSupply :: Integer -> Supply a -> a
+evalSupply n = runIdentity . evalSupplyT n
diff --git a/src/Control/Monad/Supply/Class.hs b/src/Control/Monad/Supply/Class.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Monad/Supply/Class.hs
@@ -0,0 +1,36 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  Control.Monad.Supply.Class
+-- Copyright   :  (c) PureScript 2015
+-- License     :  MIT
+--
+-- Maintainer  :  Phil Freeman <paf31@cantab.net>
+-- Stability   :  experimental
+-- Portability :
+--
+-- |
+-- A class for monads supporting a supply of fresh names
+--
+-----------------------------------------------------------------------------
+
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+module Control.Monad.Supply.Class where
+
+import Control.Monad.Supply
+import Control.Monad.State
+
+class (Monad m) => MonadSupply m where
+  fresh :: m Integer
+  
+instance (Monad m) => MonadSupply (SupplyT m) where
+  fresh = SupplyT $ do
+    n <- get
+    put (n + 1)
+    return n
+  
+instance (MonadSupply m) => MonadSupply (StateT s m) where
+  fresh = lift fresh
+
+freshName :: (MonadSupply m) => m String
+freshName = liftM (('_' :) . show) fresh
diff --git a/src/Control/Monad/Unify.hs b/src/Control/Monad/Unify.hs
--- a/src/Control/Monad/Unify.hs
+++ b/src/Control/Monad/Unify.hs
@@ -20,11 +20,9 @@
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE UndecidableInstances #-}
-{-# LANGUAGE OverloadedStrings #-}
 
 module Control.Monad.Unify where
 
-import Data.String (IsString)
 import Data.Monoid
 
 import Control.Applicative
@@ -85,9 +83,15 @@
 defaultUnifyState = UnifyState 0 mempty
 
 -- |
+-- A class for errors which support unification errors
+--
+class UnificationError t e where
+  occursCheckFailed :: t -> e
+
+-- |
 -- The type checking monad, which provides the state of the type checker, and error reporting capabilities
 --
-newtype UnifyT t m a = UnifyT { unUnify :: (StateT (UnifyState t) m) a }
+newtype UnifyT t m a = UnifyT { unUnify :: StateT (UnifyState t) m a }
   deriving (Functor, Monad, Applicative, Alternative, MonadPlus)
 
 instance (MonadState s m) => MonadState s (UnifyT t m) where
@@ -113,7 +117,7 @@
 -- |
 -- Replace a unification variable with the specified value in the current substitution
 --
-(=:=) :: (IsString e, Monad m, MonadError e m, Unifiable m t) => Unknown -> t -> UnifyT t m ()
+(=:=) :: (UnificationError t e, Monad m, MonadError e m, Unifiable m t) => Unknown -> t -> UnifyT t m ()
 (=:=) u t' = do
   st <- UnifyT get
   let sub = unifyCurrentSubstitution st
@@ -128,10 +132,10 @@
 -- |
 -- Perform the occurs check, to make sure a unification variable does not occur inside a value
 --
-occursCheck :: (IsString e, Monad m, MonadError e m, Partial t) => Unknown -> t -> UnifyT t m ()
+occursCheck :: (UnificationError t e, Monad m, MonadError e m, Partial t) => Unknown -> t -> UnifyT t m ()
 occursCheck u t =
   case isUnknown t of
-    Nothing -> when (u `elem` unknowns t) $ UnifyT . lift . throwError $ "Occurs check fails"
+    Nothing -> when (u `elem` unknowns t) $ UnifyT . lift . throwError $ occursCheckFailed t
     _ -> return ()
 
 -- |
diff --git a/src/Language/PureScript.hs b/src/Language/PureScript.hs
--- a/src/Language/PureScript.hs
+++ b/src/Language/PureScript.hs
@@ -13,15 +13,29 @@
 --
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE DataKinds, QuasiQuotes, TemplateHaskell, FlexibleContexts #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
-module Language.PureScript (module P, compile, compile', RebuildPolicy(..), MonadMake(..), make, prelude) where
+module Language.PureScript
+  ( module P
+  , compile
+  , compile'
+  , RebuildPolicy(..)
+  , MonadMake(..)
+  , make
+  , prelude
+  , version
+  ) where
 
 import Data.FileEmbed (embedFile)
 import Data.Function (on)
 import Data.List (sortBy, groupBy, intercalate)
 import Data.Maybe (fromMaybe)
 import Data.Time.Clock
+import Data.Version (Version)
 import qualified Data.Traversable as T (traverse)
 import qualified Data.ByteString.UTF8 as BU
 import qualified Data.Map as M
@@ -48,12 +62,14 @@
 import Language.PureScript.Pretty as P
 import Language.PureScript.Renamer as P
 import Language.PureScript.Sugar as P
-import Language.PureScript.Supply as P
+import Control.Monad.Supply as P
 import Language.PureScript.TypeChecker as P
 import Language.PureScript.Types as P
 import qualified Language.PureScript.CoreFn as CoreFn
 import qualified Language.PureScript.Constants as C
 
+import qualified Paths_purescript as Paths
+
 -- |
 -- Compile a collection of modules
 --
@@ -84,15 +100,15 @@
   additional <- asks optionsAdditional
   mainModuleIdent <- asks (fmap moduleNameFromString . optionsMain)
   (sorted, _) <- sortModules $ map importPrim $ if noPrelude then ms else map importPrelude ms
-  (desugared, nextVar) <- stringifyErrorStack True $ runSupplyT 0 $ desugar sorted
+  (desugared, nextVar) <- interpretMultipleErrors True $ runSupplyT 0 $ desugar sorted
   (elaborated, env') <- runCheck' env $ forM desugared $ typeCheckModule mainModuleIdent
-  regrouped <- stringifyErrorStack True $ createBindingGroupsModule . collapseBindingGroupsModule $ elaborated
+  regrouped <- interpretMultipleErrors True $ createBindingGroupsModule . collapseBindingGroupsModule $ elaborated
   let corefn = map (CoreFn.moduleToCoreFn env') regrouped
   let entryPoints = moduleNameFromString `map` entryPointModules additional
   let elim = if null entryPoints then corefn else eliminateDeadCode entryPoints corefn
   let renamed = renameInModules elim
   let codeGenModuleNames = moduleNameFromString `map` codeGenModules additional
-  let modulesToCodeGen = if null codeGenModuleNames then renamed else filter (\(CoreFn.Module mn _ _ _ _) -> mn `elem` codeGenModuleNames) renamed
+  let modulesToCodeGen = if null codeGenModuleNames then renamed else filter (\(CoreFn.Module _ mn _ _ _ _) -> mn `elem` codeGenModuleNames) renamed
   js <- concat <$> (evalSupplyT nextVar $ T.traverse moduleToJs modulesToCodeGen)
   let exts = intercalate "\n" . map (`moduleToPs` env') $ regrouped
   js' <- generateMain env' js
@@ -154,15 +170,15 @@
 -- If timestamps have not changed, the externs file can be used to provide the module's types without
 -- having to typecheck the module again.
 --
-make :: (Functor m, Applicative m, Monad m, MonadMake m)
+make :: forall m. (Functor m, Applicative m, Monad m, MonadMake m)
      => FilePath -> [(Either RebuildPolicy FilePath, Module)] -> [String] -> m Environment
 make outputDir ms prefix = do
   noPrelude <- asks optionsNoPrelude
-  let filePathMap = M.fromList (map (\(fp, Module mn _ _) -> (mn, fp)) ms)
+  let filePathMap = M.fromList (map (\(fp, Module _ mn _ _) -> (mn, fp)) ms)
 
   (sorted, graph) <- sortModules $ map importPrim $ if noPrelude then map snd ms else map (importPrelude . snd) ms
 
-  toRebuild <- foldM (\s (Module moduleName' _ _) -> do
+  toRebuild <- foldM (\s (Module _ moduleName' _ _) -> do
     let filePath = runModuleName moduleName'
 
         jsFile = outputDir </> filePath </> "index.js"
@@ -180,30 +196,29 @@
 
   marked <- rebuildIfNecessary (reverseDependencies graph) toRebuild sorted
 
-  (desugared, nextVar) <- stringifyErrorStack True $ runSupplyT 0 $ zip (map fst marked) <$> desugar (map snd marked)
+  (desugared, nextVar) <- interpretMultipleErrors True $ runSupplyT 0 $ zip (map fst marked) <$> desugar (map snd marked)
 
   evalSupplyT nextVar $ go initEnvironment desugared
 
   where
-  go :: (Functor m, Applicative m, Monad m, MonadMake m)
-     => Environment -> [(Bool, Module)] -> SupplyT m Environment
+  go :: Environment -> [(Bool, Module)] -> SupplyT m Environment
   go env [] = return env
   go env ((False, m) : ms') = do
     (_, env') <- lift . runCheck' env $ typeCheckModule Nothing m
 
     go env' ms'
-  go env ((True, m@(Module moduleName' _ exps)) : ms') = do
+  go env ((True, m@(Module coms moduleName' _ exps)) : ms') = do
     let filePath = runModuleName moduleName'
         jsFile = outputDir </> filePath </> "index.js"
         externsFile = outputDir </> filePath </> "externs.purs"
 
     lift . progress $ "Compiling " ++ runModuleName moduleName'
 
-    (Module _ elaborated _, env') <- lift . runCheck' env $ typeCheckModule Nothing m
+    (Module _ _ elaborated _, env') <- lift . runCheck' env $ typeCheckModule Nothing m
 
-    regrouped <- stringifyErrorStack True . createBindingGroups moduleName' . collapseBindingGroups $ elaborated
+    regrouped <- interpretMultipleErrors True . createBindingGroups moduleName' . collapseBindingGroups $ elaborated
 
-    let mod' = Module moduleName' regrouped exps
+    let mod' = Module coms moduleName' regrouped exps
     let corefn = CoreFn.moduleToCoreFn env' mod'
     let [renamed] = renameInModules [corefn]
 
@@ -216,18 +231,18 @@
 
     go env' ms'
 
-  rebuildIfNecessary :: (Functor m, Monad m, MonadMake m) => M.Map ModuleName [ModuleName] -> S.Set ModuleName -> [Module] -> m [(Bool, Module)]
+  rebuildIfNecessary :: M.Map ModuleName [ModuleName] -> S.Set ModuleName -> [Module] -> m [(Bool, Module)]
   rebuildIfNecessary _ _ [] = return []
-  rebuildIfNecessary graph toRebuild (m@(Module moduleName' _ _) : ms') | moduleName' `S.member` toRebuild = do
+  rebuildIfNecessary graph toRebuild (m@(Module _ moduleName' _ _) : ms') | moduleName' `S.member` toRebuild = do
     let deps = fromMaybe [] $ moduleName' `M.lookup` graph
         toRebuild' = toRebuild `S.union` S.fromList deps
     (:) (True, m) <$> rebuildIfNecessary graph toRebuild' ms'
-  rebuildIfNecessary graph toRebuild (Module moduleName' _ _ : ms') = do
+  rebuildIfNecessary graph toRebuild (Module _ moduleName' _ _ : ms') = do
     let externsFile = outputDir </> runModuleName moduleName' </> "externs.purs"
     externs <- readTextFile externsFile
     externsModules <- fmap (map snd) . either (throwError . show) return $ P.parseModulesFromFiles id [(externsFile, externs)]
     case externsModules of
-      [m'@(Module moduleName'' _ _)] | moduleName'' == moduleName' -> (:) (False, m') <$> rebuildIfNecessary graph toRebuild ms'
+      [m'@(Module _ moduleName'' _ _)] | moduleName'' == moduleName' -> (:) (False, m') <$> rebuildIfNecessary graph toRebuild ms'
       _ -> throwError $ "Externs file " ++ externsFile ++ " was invalid"
 
 reverseDependencies :: ModuleGraph -> M.Map ModuleName [ModuleName]
@@ -240,9 +255,9 @@
 -- Add an import declaration for a module if it does not already explicitly import it.
 --
 addDefaultImport :: ModuleName -> Module -> Module
-addDefaultImport toImport m@(Module mn decls exps)  =
+addDefaultImport toImport m@(Module coms mn decls exps)  =
   if isExistingImport `any` decls || mn == toImport then m
-  else Module mn (ImportDeclaration toImport Unqualified Nothing : decls) exps
+  else Module coms mn (ImportDeclaration toImport Implicit Nothing : decls) exps
   where
   isExistingImport (ImportDeclaration mn' _ _) | mn' == toImport = True
   isExistingImport (PositionedDeclaration _ _ d) = isExistingImport d
@@ -256,3 +271,6 @@
 
 prelude :: String
 prelude = BU.toString $(embedFile "prelude/prelude.purs")
+
+version :: Version
+version = Paths.version
diff --git a/src/Language/PureScript/AST/Declarations.hs b/src/Language/PureScript/AST/Declarations.hs
--- a/src/Language/PureScript/AST/Declarations.hs
+++ b/src/Language/PureScript/AST/Declarations.hs
@@ -30,14 +30,15 @@
 import Language.PureScript.Environment
 
 -- |
--- A module declaration, consisting of a module name, a list of declarations, and a list of the
--- declarations that are explicitly exported. If the export list is Nothing, everything is exported.
+-- A module declaration, consisting of comments about the module, a module name,
+-- a list of declarations, and a list of the declarations that are
+-- explicitly exported. If the export list is Nothing, everything is exported.
 --
-data Module = Module ModuleName [Declaration] (Maybe [DeclarationRef]) deriving (Show, D.Data, D.Typeable)
+data Module = Module [Comment] ModuleName [Declaration] (Maybe [DeclarationRef]) deriving (Show, D.Data, D.Typeable)
 
 -- | Return a module's name.
 getModuleName :: Module -> ModuleName
-getModuleName (Module name _ _) = name
+getModuleName (Module _ name _ _) = name
 
 -- |
 -- Test if a declaration is exported, given a module's export list.
@@ -60,7 +61,7 @@
   matches _ _ = False
 
 exportedDeclarations :: Module -> [Declaration]
-exportedDeclarations (Module _ decls exps) = filter (isExported exps) (flattenDecls decls)
+exportedDeclarations (Module _ _ decls exps) = filter (isExported exps) (flattenDecls decls)
 
 -- |
 -- Test if a data constructor for a given type is exported, given a module's export list.
@@ -78,7 +79,7 @@
 -- Return the exported data constructors for a given type.
 --
 exportedDctors :: Module -> ProperName -> [ProperName]
-exportedDctors (Module _ decls exps) ident =
+exportedDctors (Module _ _ decls exps) ident =
   filter (isDctorExported ident exps) dctors
   where
   dctors = concatMap getDctors (flattenDecls decls)
@@ -126,15 +127,15 @@
 --
 data ImportDeclarationType
   -- |
-  -- Unqualified import
+  -- An import with no explicit list: `import M`
   --
-  = Unqualified
+  = Implicit
   -- |
-  -- Qualified import with a list of references to import
+  -- An import with an explicit list of references to import: `import M (foo)`
   --
-  | Qualifying [DeclarationRef]
+  | Explicit [DeclarationRef]
   -- |
-  -- Import with hiding clause with a list of references to hide
+  -- An import with a list of references to hide: `import M hiding (foo)`
   --
   | Hiding [DeclarationRef]
   deriving (Show, D.Data, D.Typeable)
@@ -260,11 +261,18 @@
 isExternDecl _ = False
 
 -- |
--- Test if a declaration is a type class or instance declaration
+-- Test if a declaration is a type class instance declaration
 --
+isTypeClassInstanceDeclaration :: Declaration -> Bool
+isTypeClassInstanceDeclaration TypeInstanceDeclaration{} = True
+isTypeClassInstanceDeclaration (PositionedDeclaration _ _ d) = isTypeClassInstanceDeclaration d
+isTypeClassInstanceDeclaration _ = False
+
+-- |
+-- Test if a declaration is a type class declaration
+--
 isTypeClassDeclaration :: Declaration -> Bool
 isTypeClassDeclaration TypeClassDeclaration{} = True
-isTypeClassDeclaration TypeInstanceDeclaration{} = True
 isTypeClassDeclaration (PositionedDeclaration _ _ d) = isTypeClassDeclaration d
 isTypeClassDeclaration _ = False
 
diff --git a/src/Language/PureScript/AST/SourcePos.hs b/src/Language/PureScript/AST/SourcePos.hs
--- a/src/Language/PureScript/AST/SourcePos.hs
+++ b/src/Language/PureScript/AST/SourcePos.hs
@@ -30,7 +30,7 @@
     -- Column number
     --
   , sourcePosColumn :: Int
-  } deriving (D.Data, D.Typeable)
+  } deriving (Eq, D.Data, D.Typeable)
 
 instance Show SourcePos where
   show sp = "line " ++ show (sourcePosLine sp) ++ ", column " ++ show (sourcePosColumn sp)
@@ -47,7 +47,7 @@
     -- End of the span
     --
   , spanEnd :: SourcePos
-  } deriving (D.Data, D.Typeable)
+  } deriving (Eq, D.Data, D.Typeable)
 
 instance Show SourceSpan where
   show sp = spanName sp ++ " " ++ show (spanStart sp) ++ " - " ++ show (spanEnd sp)
diff --git a/src/Language/PureScript/CodeGen/Externs.hs b/src/Language/PureScript/CodeGen/Externs.hs
--- a/src/Language/PureScript/CodeGen/Externs.hs
+++ b/src/Language/PureScript/CodeGen/Externs.hs
@@ -36,8 +36,8 @@
 -- Generate foreign imports for all declarations in a module
 --
 moduleToPs :: Module -> Environment -> String
-moduleToPs (Module _ _ Nothing) _ = error "Module exports were not elaborated in moduleToPs"
-moduleToPs (Module moduleName ds (Just exts)) env = intercalate "\n" . execWriter $ do
+moduleToPs (Module _ _ _ Nothing) _ = error "Module exports were not elaborated in moduleToPs"
+moduleToPs (Module _ moduleName ds (Just exts)) env = intercalate "\n" . execWriter $ do
   tell [ "module " ++ runModuleName moduleName ++ " where"]
   mapM_ declToPs ds
   mapM_ exportToPs exts
diff --git a/src/Language/PureScript/CodeGen/JS.hs b/src/Language/PureScript/CodeGen/JS.hs
--- a/src/Language/PureScript/CodeGen/JS.hs
+++ b/src/Language/PureScript/CodeGen/JS.hs
@@ -13,7 +13,10 @@
 --
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE GADTs, ViewPatterns, FlexibleContexts #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
 module Language.PureScript.CodeGen.JS (
     module AST,
@@ -28,7 +31,8 @@
 import Control.Applicative
 import Control.Arrow ((&&&))
 import Control.Monad (foldM, replicateM, forM)
-import Control.Monad.Reader (MonadReader, asks, lift)
+import Control.Monad.Reader (MonadReader, asks)
+import Control.Monad.Supply.Class
 
 import Language.PureScript.CodeGen.JS.AST as AST
 import Language.PureScript.CodeGen.JS.Common as Common
@@ -36,7 +40,6 @@
 import Language.PureScript.Names
 import Language.PureScript.CodeGen.JS.Optimizer
 import Language.PureScript.Options
-import Language.PureScript.Supply
 import Language.PureScript.Traversals (sndM)
 import qualified Language.PureScript.Constants as C
 
@@ -44,320 +47,313 @@
 -- Generate code in the simplified Javascript intermediate representation for all declarations in a
 -- module.
 --
-moduleToJs :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-           => Module Ann -> SupplyT m [JS]
-moduleToJs (Module name imps exps foreigns decls) = do
-  additional <- lift $ asks optionsAdditional
-  jsImports <- lift . T.traverse importToJs . delete (ModuleName [ProperName C.prim]) . (\\ [name]) $ imps
+moduleToJs :: forall m mode. (Applicative m, Monad m, MonadReader (Options mode) m, MonadSupply m)
+           => Module Ann -> m [JS]
+moduleToJs (Module coms mn imps exps foreigns decls) = do
+  additional <- asks optionsAdditional
+  jsImports <- T.traverse importToJs . delete (ModuleName [ProperName C.prim]) . (\\ [mn]) $ imps
   let foreigns' = mapMaybe (\(_, js, _) -> js) foreigns
-  jsDecls <- mapM (bindToJs name) decls
-  optimized <- lift $ T.traverse (T.traverse optimize) jsDecls
+  jsDecls <- mapM bindToJs decls
+  optimized <- T.traverse (T.traverse optimize) jsDecls
   let isModuleEmpty = null exps
-  let moduleBody = JSStringLiteral "use strict" : jsImports ++ foreigns' ++ concat optimized
+  comments <- not <$> asks optionsNoComments
+  let strict = JSStringLiteral "use strict"
+  let header = if comments && not (null coms) then JSComment coms strict else strict
+  let moduleBody = header : jsImports ++ foreigns' ++ concat optimized
   let exps' = JSObjectLiteral $ map (runIdent &&& JSVar . identToJs) exps
   return $ case additional of
     MakeOptions -> moduleBody ++ [JSAssignment (JSAccessor "exports" (JSVar "module")) exps']
     CompileOptions ns _ _ | not isModuleEmpty ->
       [ JSVariableIntroduction ns
                                (Just (JSBinary Or (JSVar ns) (JSObjectLiteral [])) )
-      , JSAssignment (JSAccessor (moduleNameToJs name) (JSVar ns))
+      , JSAssignment (JSAccessor (moduleNameToJs mn) (JSVar ns))
                      (JSApp (JSFunction Nothing [] (JSBlock (moduleBody ++ [JSReturn exps']))) [])
       ]
     _ -> []
 
--- |
--- Generates Javascript code for a module import.
---
-importToJs :: (Monad m, MonadReader (Options mode) m) => ModuleName -> m JS
-importToJs mn = do
-  additional <- asks optionsAdditional
-  let moduleBody = case additional of
-        MakeOptions -> JSApp (JSVar "require") [JSStringLiteral (runModuleName mn)]
-        CompileOptions ns _ _ -> JSAccessor (moduleNameToJs mn) (JSVar ns)
-  return $ JSVariableIntroduction (moduleNameToJs mn) (Just moduleBody)
+  where
 
--- |
--- Generate code in the simplified Javascript intermediate representation for a declaration
---
-bindToJs :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-         => ModuleName -> Bind Ann -> SupplyT m [JS]
-bindToJs mp (NonRec ident val) = return <$> nonRecToJS mp ident val
-bindToJs mp (Rec vals) = forM vals (uncurry (nonRecToJS mp))
+  -- |
+  -- Generates Javascript code for a module import.
+  --
+  importToJs :: ModuleName -> m JS
+  importToJs mn' = do
+    additional <- asks optionsAdditional
+    let moduleBody = case additional of
+          MakeOptions -> JSApp (JSVar "require") [JSStringLiteral (runModuleName mn')]
+          CompileOptions ns _ _ -> JSAccessor (moduleNameToJs mn') (JSVar ns)
+    return $ JSVariableIntroduction (moduleNameToJs mn') (Just moduleBody)
 
--- |
--- Generate code in the simplified Javascript intermediate representation for a single non-recursive
--- declaration.
---
--- The main purpose of this function is to handle code generation for comments.
---
-nonRecToJS :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-           => ModuleName -> Ident -> Expr Ann -> SupplyT m JS
-nonRecToJS m i e@(extractAnn -> (_, com, _, _)) | not (null com) = do
-  withoutComment <- lift $ asks optionsNoComments
-  if withoutComment
-     then nonRecToJS m i (modifyAnn removeComments e)
-     else JSComment com <$> nonRecToJS m i (modifyAnn removeComments e)
-nonRecToJS mp ident val = do
-  js <- valueToJs mp val
-  return $ JSVariableIntroduction (identToJs ident) (Just js)
+  -- |
+  -- Generate code in the simplified Javascript intermediate representation for a declaration
+  --
+  bindToJs :: Bind Ann -> m [JS]
+  bindToJs (NonRec ident val) = return <$> nonRecToJS ident val
+  bindToJs (Rec vals) = forM vals (uncurry nonRecToJS)
 
+  -- |
+  -- Generate code in the simplified Javascript intermediate representation for a single non-recursive
+  -- declaration.
+  --
+  -- The main purpose of this function is to handle code generation for comments.
+  --
+  nonRecToJS :: Ident -> Expr Ann -> m JS
+  nonRecToJS i e@(extractAnn -> (_, com, _, _)) | not (null com) = do
+    withoutComment <- asks optionsNoComments
+    if withoutComment
+       then nonRecToJS i (modifyAnn removeComments e)
+       else JSComment com <$> nonRecToJS i (modifyAnn removeComments e)
+  nonRecToJS ident val = do
+    js <- valueToJs val
+    return $ JSVariableIntroduction (identToJs ident) (Just js)
 
--- |
--- Generate code in the simplified Javascript intermediate representation for a variable based on a
--- PureScript identifier.
---
-var :: Ident -> JS
-var = JSVar . identToJs
+  -- |
+  -- Generate code in the simplified Javascript intermediate representation for a variable based on a
+  -- PureScript identifier.
+  --
+  var :: Ident -> JS
+  var = JSVar . identToJs
 
--- |
--- Generate code in the simplified Javascript intermediate representation for an accessor based on
--- a PureScript identifier. If the name is not valid in Javascript (symbol based, reserved name) an
--- indexer is returned.
---
-accessor :: Ident -> JS -> JS
-accessor (Ident prop) = accessorString prop
-accessor (Op op) = JSIndexer (JSStringLiteral op)
+  -- |
+  -- Generate code in the simplified Javascript intermediate representation for an accessor based on
+  -- a PureScript identifier. If the name is not valid in Javascript (symbol based, reserved name) an
+  -- indexer is returned.
+  --
+  accessor :: Ident -> JS -> JS
+  accessor (Ident prop) = accessorString prop
+  accessor (Op op) = JSIndexer (JSStringLiteral op)
 
-accessorString :: String -> JS -> JS
-accessorString prop | identNeedsEscaping prop = JSIndexer (JSStringLiteral prop)
-                    | otherwise = JSAccessor prop
+  accessorString :: String -> JS -> JS
+  accessorString prop | identNeedsEscaping prop = JSIndexer (JSStringLiteral prop)
+                      | otherwise = JSAccessor prop
 
--- |
--- Generate code in the simplified Javascript intermediate representation for a value or expression.
---
-valueToJs :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-          => ModuleName -> Expr Ann -> SupplyT m JS
-valueToJs m (Literal _ l) =
-  literalToValueJS m l
-valueToJs m (Var (_, _, _, Just (IsConstructor _ [])) name) =
-  return $ JSAccessor "value" $ qualifiedToJS m id name
-valueToJs m (Var (_, _, _, Just (IsConstructor _ _)) name) =
-  return $ JSAccessor "create" $ qualifiedToJS m id name
-valueToJs m (Accessor _ prop val) =
-  accessorString prop <$> valueToJs m val
-valueToJs m (ObjectUpdate _ o ps) = do
-  obj <- valueToJs m o
-  sts <- mapM (sndM $ valueToJs m) ps
-  extendObj obj sts
-valueToJs _ e@(Abs (_, _, _, Just IsTypeClassConstructor) _ _) =
-  let args = unAbs e
-  in return $ JSFunction Nothing (map identToJs args) (JSBlock $ map assign args)
-  where
-  unAbs :: Expr Ann -> [Ident]
-  unAbs (Abs _ arg val) = arg : unAbs val
-  unAbs _ = []
-  assign :: Ident -> JS
-  assign name = JSAssignment (accessorString (runIdent name) (JSVar "this"))
-                             (var name)
-valueToJs m (Abs _ arg val) = do
-  ret <- valueToJs m val
-  return $ JSFunction Nothing [identToJs arg] (JSBlock [JSReturn ret])
-valueToJs m e@App{} = do
-  let (f, args) = unApp e []
-  args' <- mapM (valueToJs m) args
-  case f of
-    Var (_, _, _, Just IsNewtype) _ -> return (head args')
-    Var (_, _, _, Just (IsConstructor _ fields)) name | length args == length fields ->
-      return $ JSUnary JSNew $ JSApp (qualifiedToJS m id name) args'
-    Var (_, _, _, Just IsTypeClassConstructor) name ->
-      return $ JSUnary JSNew $ JSApp (qualifiedToJS m id name) args'
-    _ -> flip (foldl (\fn a -> JSApp fn [a])) args' <$> valueToJs m f
-  where
-  unApp :: Expr Ann -> [Expr Ann] -> (Expr Ann, [Expr Ann])
-  unApp (App _ val arg) args = unApp val (arg : args)
-  unApp other args = (other, args)
-valueToJs m (Var _ ident) =
-  return $ varToJs m ident
-valueToJs m (Case _ values binders) = do
-  vals <- mapM (valueToJs m) values
-  bindersToJs m binders vals
-valueToJs m (Let _ ds val) = do
-  decls <- concat <$> mapM (bindToJs m) ds
-  ret <- valueToJs m val
-  return $ JSApp (JSFunction Nothing [] (JSBlock (decls ++ [JSReturn ret]))) []
-valueToJs _ (Constructor (_, _, _, Just IsNewtype) _ (ProperName ctor) _) =
-  return $ JSVariableIntroduction ctor (Just $
-              JSObjectLiteral [("create",
-                JSFunction Nothing ["value"]
-                  (JSBlock [JSReturn $ JSVar "value"]))])
-valueToJs _ (Constructor _ _ (ProperName ctor) []) =
-  return $ iife ctor [ JSFunction (Just ctor) [] (JSBlock [])
-         , JSAssignment (JSAccessor "value" (JSVar ctor))
-              (JSUnary JSNew $ JSApp (JSVar ctor) []) ]
-valueToJs _ (Constructor _ _ (ProperName ctor) fields) =
-  let constructor =
-        let body = [ JSAssignment (JSAccessor (identToJs f) (JSVar "this")) (var f) | f <- fields ]
-        in JSFunction (Just ctor) (identToJs `map` fields) (JSBlock body)
-      createFn =
-        let body = JSUnary JSNew $ JSApp (JSVar ctor) (var `map` fields)
-        in foldr (\f inner -> JSFunction Nothing [identToJs f] (JSBlock [JSReturn inner])) body fields
-  in return $ iife ctor [ constructor
-                        , JSAssignment (JSAccessor "create" (JSVar ctor)) createFn
-                        ]
-iife :: String -> [JS] -> JS
-iife v exprs = JSApp (JSFunction Nothing [] (JSBlock $ exprs ++ [JSReturn $ JSVar v])) []
+  -- |
+  -- Generate code in the simplified Javascript intermediate representation for a value or expression.
+  --
+  valueToJs :: Expr Ann -> m JS
+  valueToJs (Literal _ l) =
+    literalToValueJS l
+  valueToJs (Var (_, _, _, Just (IsConstructor _ [])) name) =
+    return $ JSAccessor "value" $ qualifiedToJS id name
+  valueToJs (Var (_, _, _, Just (IsConstructor _ _)) name) =
+    return $ JSAccessor "create" $ qualifiedToJS id name
+  valueToJs (Accessor _ prop val) =
+    accessorString prop <$> valueToJs val
+  valueToJs (ObjectUpdate _ o ps) = do
+    obj <- valueToJs o
+    sts <- mapM (sndM valueToJs) ps
+    extendObj obj sts
+  valueToJs e@(Abs (_, _, _, Just IsTypeClassConstructor) _ _) =
+    let args = unAbs e
+    in return $ JSFunction Nothing (map identToJs args) (JSBlock $ map assign args)
+    where
+    unAbs :: Expr Ann -> [Ident]
+    unAbs (Abs _ arg val) = arg : unAbs val
+    unAbs _ = []
+    assign :: Ident -> JS
+    assign name = JSAssignment (accessorString (runIdent name) (JSVar "this"))
+                               (var name)
+  valueToJs (Abs _ arg val) = do
+    ret <- valueToJs val
+    return $ JSFunction Nothing [identToJs arg] (JSBlock [JSReturn ret])
+  valueToJs e@App{} = do
+    let (f, args) = unApp e []
+    args' <- mapM valueToJs args
+    case f of
+      Var (_, _, _, Just IsNewtype) _ -> return (head args')
+      Var (_, _, _, Just (IsConstructor _ fields)) name | length args == length fields ->
+        return $ JSUnary JSNew $ JSApp (qualifiedToJS id name) args'
+      Var (_, _, _, Just IsTypeClassConstructor) name ->
+        return $ JSUnary JSNew $ JSApp (qualifiedToJS id name) args'
+      _ -> flip (foldl (\fn a -> JSApp fn [a])) args' <$> valueToJs f
+    where
+    unApp :: Expr Ann -> [Expr Ann] -> (Expr Ann, [Expr Ann])
+    unApp (App _ val arg) args = unApp val (arg : args)
+    unApp other args = (other, args)
+  valueToJs (Var _ ident) =
+    return $ varToJs ident
+  valueToJs (Case _ values binders) = do
+    vals <- mapM valueToJs values
+    bindersToJs binders vals
+  valueToJs (Let _ ds val) = do
+    ds' <- concat <$> mapM bindToJs ds
+    ret <- valueToJs val
+    return $ JSApp (JSFunction Nothing [] (JSBlock (ds' ++ [JSReturn ret]))) []
+  valueToJs (Constructor (_, _, _, Just IsNewtype) _ (ProperName ctor) _) =
+    return $ JSVariableIntroduction ctor (Just $
+                JSObjectLiteral [("create",
+                  JSFunction Nothing ["value"]
+                    (JSBlock [JSReturn $ JSVar "value"]))])
+  valueToJs (Constructor _ _ (ProperName ctor) []) =
+    return $ iife ctor [ JSFunction (Just ctor) [] (JSBlock [])
+           , JSAssignment (JSAccessor "value" (JSVar ctor))
+                (JSUnary JSNew $ JSApp (JSVar ctor) []) ]
+  valueToJs (Constructor _ _ (ProperName ctor) fields) =
+    let constructor =
+          let body = [ JSAssignment (JSAccessor (identToJs f) (JSVar "this")) (var f) | f <- fields ]
+          in JSFunction (Just ctor) (identToJs `map` fields) (JSBlock body)
+        createFn =
+          let body = JSUnary JSNew $ JSApp (JSVar ctor) (var `map` fields)
+          in foldr (\f inner -> JSFunction Nothing [identToJs f] (JSBlock [JSReturn inner])) body fields
+    in return $ iife ctor [ constructor
+                          , JSAssignment (JSAccessor "create" (JSVar ctor)) createFn
+                          ]
 
-literalToValueJS :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-                 => ModuleName -> Literal (Expr Ann) -> SupplyT m JS
-literalToValueJS _ (NumericLiteral n) = return $ JSNumericLiteral n
-literalToValueJS _ (StringLiteral s) = return $ JSStringLiteral s
-literalToValueJS _ (BooleanLiteral b) = return $ JSBooleanLiteral b
-literalToValueJS m (ArrayLiteral xs) = JSArrayLiteral <$> mapM (valueToJs m) xs
-literalToValueJS m (ObjectLiteral ps) = JSObjectLiteral <$> mapM (sndM (valueToJs m)) ps
+  iife :: String -> [JS] -> JS
+  iife v exprs = JSApp (JSFunction Nothing [] (JSBlock $ exprs ++ [JSReturn $ JSVar v])) []
 
--- |
--- Shallow copy an object.
---
-extendObj :: (Functor m, Applicative m, Monad m) => JS -> [(String, JS)] -> SupplyT m JS
-extendObj obj sts = do
-  newObj <- freshName
-  key <- freshName
-  let
-    jsKey = JSVar key
-    jsNewObj = JSVar newObj
-    block = JSBlock (objAssign:copy:extend ++ [JSReturn jsNewObj])
-    objAssign = JSVariableIntroduction newObj (Just $ JSObjectLiteral [])
-    copy = JSForIn key obj $ JSBlock [JSIfElse cond assign Nothing]
-    cond = JSApp (JSAccessor "hasOwnProperty" obj) [jsKey]
-    assign = JSBlock [JSAssignment (JSIndexer jsKey jsNewObj) (JSIndexer jsKey obj)]
-    stToAssign (s, js) = JSAssignment (JSAccessor s jsNewObj) js
-    extend = map stToAssign sts
-  return $ JSApp (JSFunction Nothing [] block) []
+  literalToValueJS :: Literal (Expr Ann) -> m JS
+  literalToValueJS (NumericLiteral n) = return $ JSNumericLiteral n
+  literalToValueJS (StringLiteral s) = return $ JSStringLiteral s
+  literalToValueJS (BooleanLiteral b) = return $ JSBooleanLiteral b
+  literalToValueJS (ArrayLiteral xs) = JSArrayLiteral <$> mapM valueToJs xs
+  literalToValueJS (ObjectLiteral ps) = JSObjectLiteral <$> mapM (sndM valueToJs) ps
 
--- |
--- Generate code in the simplified Javascript intermediate representation for a reference to a
--- variable.
---
-varToJs :: ModuleName -> Qualified Ident -> JS
-varToJs _ (Qualified Nothing ident) = var ident
-varToJs m qual = qualifiedToJS m id qual
+  -- |
+  -- Shallow copy an object.
+  --
+  extendObj :: JS -> [(String, JS)] -> m JS
+  extendObj obj sts = do
+    newObj <- freshName
+    key <- freshName
+    let
+      jsKey = JSVar key
+      jsNewObj = JSVar newObj
+      block = JSBlock (objAssign:copy:extend ++ [JSReturn jsNewObj])
+      objAssign = JSVariableIntroduction newObj (Just $ JSObjectLiteral [])
+      copy = JSForIn key obj $ JSBlock [JSIfElse cond assign Nothing]
+      cond = JSApp (JSAccessor "hasOwnProperty" obj) [jsKey]
+      assign = JSBlock [JSAssignment (JSIndexer jsKey jsNewObj) (JSIndexer jsKey obj)]
+      stToAssign (s, js) = JSAssignment (JSAccessor s jsNewObj) js
+      extend = map stToAssign sts
+    return $ JSApp (JSFunction Nothing [] block) []
 
--- |
--- Generate code in the simplified Javascript intermediate representation for a reference to a
--- variable that may have a qualified name.
---
-qualifiedToJS :: ModuleName -> (a -> Ident) -> Qualified a -> JS
-qualifiedToJS _ f (Qualified (Just (ModuleName [ProperName mn])) a) | mn == C.prim = JSVar . runIdent $ f a
-qualifiedToJS m f (Qualified (Just m') a) | m /= m' = accessor (f a) (JSVar (moduleNameToJs m'))
-qualifiedToJS _ f (Qualified _ a) = JSVar $ identToJs (f a)
+  -- |
+  -- Generate code in the simplified Javascript intermediate representation for a reference to a
+  -- variable.
+  --
+  varToJs :: Qualified Ident -> JS
+  varToJs (Qualified Nothing ident) = var ident
+  varToJs qual = qualifiedToJS id qual
 
--- |
--- Generate code in the simplified Javascript intermediate representation for pattern match binders
--- and guards.
---
-bindersToJs :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-            => ModuleName -> [CaseAlternative Ann] -> [JS] -> SupplyT m JS
-bindersToJs m binders vals = do
-  valNames <- replicateM (length vals) freshName
-  let assignments = zipWith JSVariableIntroduction valNames (map Just vals)
-  jss <- forM binders $ \(CaseAlternative bs result) -> do
-    ret <- guardsToJs result
-    go valNames ret bs
-  return $ JSApp (JSFunction Nothing [] (JSBlock (assignments ++ concat jss ++ [JSThrow $ JSUnary JSNew $ JSApp (JSVar "Error") [JSStringLiteral "Failed pattern match"]])))
-                 []
-  where
-    go :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-       => [String] -> [JS] -> [Binder Ann] -> SupplyT m [JS]
-    go _ done [] = return done
-    go (v:vs) done' (b:bs) = do
-      done'' <- go vs done' bs
-      binderToJs m v done'' b
-    go _ _ _ = error "Invalid arguments to bindersToJs"
+  -- |
+  -- Generate code in the simplified Javascript intermediate representation for a reference to a
+  -- variable that may have a qualified name.
+  --
+  qualifiedToJS :: (a -> Ident) -> Qualified a -> JS
+  qualifiedToJS f (Qualified (Just (ModuleName [ProperName mn'])) a) | mn' == C.prim = JSVar . runIdent $ f a
+  qualifiedToJS f (Qualified (Just mn') a) | mn /= mn' = accessor (f a) (JSVar (moduleNameToJs mn'))
+  qualifiedToJS f (Qualified _ a) = JSVar $ identToJs (f a)
 
-    guardsToJs :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-               => Either [(Guard Ann, Expr Ann)] (Expr Ann) -> SupplyT m [JS]
-    guardsToJs (Left gs) = forM gs $ \(cond, val) -> do
-      cond' <- valueToJs m cond
-      done  <- valueToJs m val
-      return $ JSIfElse cond' (JSBlock [JSReturn done]) Nothing
-    guardsToJs (Right v) = return . JSReturn <$> valueToJs m v
+  -- |
+  -- Generate code in the simplified Javascript intermediate representation for pattern match binders
+  -- and guards.
+  --
+  bindersToJs :: [CaseAlternative Ann] -> [JS] -> m JS
+  bindersToJs binders vals = do
+    valNames <- replicateM (length vals) freshName
+    let assignments = zipWith JSVariableIntroduction valNames (map Just vals)
+    jss <- forM binders $ \(CaseAlternative bs result) -> do
+      ret <- guardsToJs result
+      go valNames ret bs
+    return $ JSApp (JSFunction Nothing [] (JSBlock (assignments ++ concat jss ++ [JSThrow $ JSUnary JSNew $ JSApp (JSVar "Error") [JSStringLiteral "Failed pattern match"]])))
+                   []
+    where
+      go :: [String] -> [JS] -> [Binder Ann] -> m [JS]
+      go _ done [] = return done
+      go (v:vs) done' (b:bs) = do
+        done'' <- go vs done' bs
+        binderToJs v done'' b
+      go _ _ _ = error "Invalid arguments to bindersToJs"
 
--- |
--- Generate code in the simplified Javascript intermediate representation for a pattern match
--- binder.
---
-binderToJs :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-           => ModuleName -> String -> [JS] -> Binder Ann -> SupplyT m [JS]
-binderToJs _ _ done (NullBinder{}) = return done
-binderToJs m varName done (LiteralBinder _ l) =
-  literalToBinderJS m varName done l
-binderToJs _ varName done (VarBinder _ ident) =
-  return (JSVariableIntroduction (identToJs ident) (Just (JSVar varName)) : done)
-binderToJs m varName done (ConstructorBinder (_, _, _, Just IsNewtype) _ _ [b]) =
-  binderToJs m varName done b
-binderToJs m varName done (ConstructorBinder (_, _, _, Just (IsConstructor ctorType fields)) _ ctor bs) = do
-  js <- go (zip fields bs) done
-  return $ case ctorType of
-    ProductType -> js
-    SumType ->
-      [JSIfElse (JSInstanceOf (JSVar varName) (qualifiedToJS m (Ident . runProperName) ctor))
-                (JSBlock js)
-                Nothing]
-  where
-  go :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-     => [(Ident, Binder Ann)] -> [JS] -> SupplyT m [JS]
-  go [] done' = return done'
-  go ((field, binder) : remain) done' = do
-    argVar <- freshName
-    done'' <- go remain done'
-    js <- binderToJs m argVar done'' binder
-    return (JSVariableIntroduction argVar (Just (JSAccessor (identToJs field) (JSVar varName))) : js)
-binderToJs m varName done binder@(ConstructorBinder _ _ ctor _) | isCons ctor = do
-  let (headBinders, tailBinder) = uncons [] binder
-      numberOfHeadBinders = fromIntegral $ length headBinders
-  js1 <- foldM (\done' (headBinder, index) -> do
-    headVar <- freshName
-    jss <- binderToJs m headVar done' headBinder
-    return (JSVariableIntroduction headVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSVar varName))) : jss)) done (zip headBinders [0..])
-  tailVar <- freshName
-  js2 <- binderToJs m tailVar js1 tailBinder
-  return [JSIfElse (JSBinary GreaterThanOrEqualTo (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left numberOfHeadBinders))) (JSBlock
-    ( JSVariableIntroduction tailVar (Just (JSApp (JSAccessor "slice" (JSVar varName)) [JSNumericLiteral (Left numberOfHeadBinders)])) :
-      js2
-    )) Nothing]
-  where
-  uncons :: [Binder Ann] -> Binder Ann -> ([Binder Ann], Binder Ann)
-  uncons acc (ConstructorBinder _ _ ctor' [h, t]) | isCons ctor' = uncons (h : acc) t
-  uncons acc tailBinder = (reverse acc, tailBinder)
-binderToJs _ _ _ b@(ConstructorBinder{}) =
-  error $ "Invalid ConstructorBinder in binderToJs: " ++ show b
-binderToJs m varName done (NamedBinder _ ident binder) = do
-  js <- binderToJs m varName done binder
-  return (JSVariableIntroduction (identToJs ident) (Just (JSVar varName)) : js)
+      guardsToJs :: Either [(Guard Ann, Expr Ann)] (Expr Ann) -> m [JS]
+      guardsToJs (Left gs) = forM gs $ \(cond, val) -> do
+        cond' <- valueToJs cond
+        done  <- valueToJs val
+        return $ JSIfElse cond' (JSBlock [JSReturn done]) Nothing
+      guardsToJs (Right v) = return . JSReturn <$> valueToJs v
 
-literalToBinderJS :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-                  => ModuleName -> String -> [JS] -> Literal (Binder Ann) -> SupplyT m [JS]
-literalToBinderJS _ varName done (NumericLiteral num) =
-  return [JSIfElse (JSBinary EqualTo (JSVar varName) (JSNumericLiteral num)) (JSBlock done) Nothing]
-literalToBinderJS _ varName done (StringLiteral str) =
-  return [JSIfElse (JSBinary EqualTo (JSVar varName) (JSStringLiteral str)) (JSBlock done) Nothing]
-literalToBinderJS _ varName done (BooleanLiteral True) =
-  return [JSIfElse (JSVar varName) (JSBlock done) Nothing]
-literalToBinderJS _ varName done (BooleanLiteral False) =
-  return [JSIfElse (JSUnary Not (JSVar varName)) (JSBlock done) Nothing]
-literalToBinderJS m varName done (ObjectLiteral bs) = go done bs
-  where
-  go :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-     => [JS] -> [(String, Binder Ann)] -> SupplyT m [JS]
-  go done' [] = return done'
-  go done' ((prop, binder):bs') = do
-    propVar <- freshName
-    done'' <- go done' bs'
-    js <- binderToJs m propVar done'' binder
-    return (JSVariableIntroduction propVar (Just (accessorString prop (JSVar varName))) : js)
-literalToBinderJS m varName done (ArrayLiteral bs) = do
-  js <- go done 0 bs
-  return [JSIfElse (JSBinary EqualTo (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left (fromIntegral $ length bs)))) (JSBlock js) Nothing]
-  where
-  go :: (Functor m, Applicative m, Monad m, MonadReader (Options mode) m)
-     => [JS] -> Integer -> [Binder Ann] -> SupplyT m [JS]
-  go done' _ [] = return done'
-  go done' index (binder:bs') = do
-    elVar <- freshName
-    done'' <- go done' (index + 1) bs'
-    js <- binderToJs m elVar done'' binder
-    return (JSVariableIntroduction elVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSVar varName))) : js)
+  -- |
+  -- Generate code in the simplified Javascript intermediate representation for a pattern match
+  -- binder.
+  --
+  binderToJs :: String -> [JS] -> Binder Ann -> m [JS]
+  binderToJs _ done (NullBinder{}) = return done
+  binderToJs varName done (LiteralBinder _ l) =
+    literalToBinderJS varName done l
+  binderToJs varName done (VarBinder _ ident) =
+    return (JSVariableIntroduction (identToJs ident) (Just (JSVar varName)) : done)
+  binderToJs varName done (ConstructorBinder (_, _, _, Just IsNewtype) _ _ [b]) =
+    binderToJs varName done b
+  binderToJs varName done (ConstructorBinder (_, _, _, Just (IsConstructor ctorType fields)) _ ctor bs) = do
+    js <- go (zip fields bs) done
+    return $ case ctorType of
+      ProductType -> js
+      SumType ->
+        [JSIfElse (JSInstanceOf (JSVar varName) (qualifiedToJS (Ident . runProperName) ctor))
+                  (JSBlock js)
+                  Nothing]
+    where
+    go :: [(Ident, Binder Ann)] -> [JS] -> m [JS]
+    go [] done' = return done'
+    go ((field, binder) : remain) done' = do
+      argVar <- freshName
+      done'' <- go remain done'
+      js <- binderToJs argVar done'' binder
+      return (JSVariableIntroduction argVar (Just (JSAccessor (identToJs field) (JSVar varName))) : js)
+  binderToJs varName done binder@(ConstructorBinder _ _ ctor _) | isCons ctor = do
+    let (headBinders, tailBinder) = uncons [] binder
+        numberOfHeadBinders = fromIntegral $ length headBinders
+    js1 <- foldM (\done' (headBinder, index) -> do
+      headVar <- freshName
+      jss <- binderToJs headVar done' headBinder
+      return (JSVariableIntroduction headVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSVar varName))) : jss)) done (zip headBinders [0..])
+    tailVar <- freshName
+    js2 <- binderToJs tailVar js1 tailBinder
+    return [JSIfElse (JSBinary GreaterThanOrEqualTo (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left numberOfHeadBinders))) (JSBlock
+      ( JSVariableIntroduction tailVar (Just (JSApp (JSAccessor "slice" (JSVar varName)) [JSNumericLiteral (Left numberOfHeadBinders)])) :
+        js2
+      )) Nothing]
+    where
+    uncons :: [Binder Ann] -> Binder Ann -> ([Binder Ann], Binder Ann)
+    uncons acc (ConstructorBinder _ _ ctor' [h, t]) | isCons ctor' = uncons (h : acc) t
+    uncons acc tailBinder = (reverse acc, tailBinder)
+  binderToJs _ _ b@(ConstructorBinder{}) =
+    error $ "Invalid ConstructorBinder in binderToJs: " ++ show b
+  binderToJs varName done (NamedBinder _ ident binder) = do
+    js <- binderToJs varName done binder
+    return (JSVariableIntroduction (identToJs ident) (Just (JSVar varName)) : js)
 
-isCons :: Qualified ProperName -> Bool
-isCons (Qualified (Just mn) ctor) = mn == ModuleName [ProperName C.prim] && ctor == ProperName "Array"
-isCons name = error $ "Unexpected argument in isCons: " ++ show name
+  literalToBinderJS :: String -> [JS] -> Literal (Binder Ann) -> m [JS]
+  literalToBinderJS varName done (NumericLiteral num) =
+    return [JSIfElse (JSBinary EqualTo (JSVar varName) (JSNumericLiteral num)) (JSBlock done) Nothing]
+  literalToBinderJS varName done (StringLiteral str) =
+    return [JSIfElse (JSBinary EqualTo (JSVar varName) (JSStringLiteral str)) (JSBlock done) Nothing]
+  literalToBinderJS varName done (BooleanLiteral True) =
+    return [JSIfElse (JSVar varName) (JSBlock done) Nothing]
+  literalToBinderJS varName done (BooleanLiteral False) =
+    return [JSIfElse (JSUnary Not (JSVar varName)) (JSBlock done) Nothing]
+  literalToBinderJS varName done (ObjectLiteral bs) = go done bs
+    where
+    go :: [JS] -> [(String, Binder Ann)] -> m [JS]
+    go done' [] = return done'
+    go done' ((prop, binder):bs') = do
+      propVar <- freshName
+      done'' <- go done' bs'
+      js <- binderToJs propVar done'' binder
+      return (JSVariableIntroduction propVar (Just (accessorString prop (JSVar varName))) : js)
+  literalToBinderJS varName done (ArrayLiteral bs) = do
+    js <- go done 0 bs
+    return [JSIfElse (JSBinary EqualTo (JSAccessor "length" (JSVar varName)) (JSNumericLiteral (Left (fromIntegral $ length bs)))) (JSBlock js) Nothing]
+    where
+    go :: [JS] -> Integer -> [Binder Ann] -> m [JS]
+    go done' _ [] = return done'
+    go done' index (binder:bs') = do
+      elVar <- freshName
+      done'' <- go done' (index + 1) bs'
+      js <- binderToJs elVar done'' binder
+      return (JSVariableIntroduction elVar (Just (JSIndexer (JSNumericLiteral (Left index)) (JSVar varName))) : js)
+
+  isCons :: Qualified ProperName -> Bool
+  isCons (Qualified (Just mn') ctor) = mn' == ModuleName [ProperName C.prim] && ctor == ProperName "Array"
+  isCons name = error $ "Unexpected argument in isCons: " ++ show name
diff --git a/src/Language/PureScript/CoreFn/Desugar.hs b/src/Language/PureScript/CoreFn/Desugar.hs
--- a/src/Language/PureScript/CoreFn/Desugar.hs
+++ b/src/Language/PureScript/CoreFn/Desugar.hs
@@ -40,14 +40,14 @@
 -- Desugars a module from AST to CoreFn representation.
 --
 moduleToCoreFn :: Environment -> A.Module -> Module Ann
-moduleToCoreFn _ (A.Module _ _ Nothing) =
+moduleToCoreFn _ (A.Module _ _ _ Nothing) =
   error "Module exports were not elaborated before moduleToCoreFn"
-moduleToCoreFn env (A.Module mn decls (Just exps)) =
+moduleToCoreFn env (A.Module coms mn decls (Just exps)) =
   let imports = nub $ mapMaybe importToCoreFn decls ++ findQualModules decls
       exps' = nub $ concatMap exportToCoreFn exps
       externs = nub $ mapMaybe externToCoreFn decls
       decls' = concatMap (declToCoreFn Nothing []) decls
-  in Module mn imports exps' externs decls'
+  in Module coms mn imports exps' externs decls'
 
   where
 
@@ -190,12 +190,17 @@
 --
 findQualModules :: [A.Declaration] -> [ModuleName]
 findQualModules decls =
-  let (f, _, _, _, _) = everythingOnValues (++) (const []) fqValues (const []) (const []) (const [])
+  let (f, _, _, _, _) = everythingOnValues (++) (const []) fqValues fqBinders (const []) (const [])
   in f `concatMap` decls
   where
   fqValues :: A.Expr -> [ModuleName]
   fqValues (A.Var (Qualified (Just mn) _)) = [mn]
+  fqValues (A.Constructor (Qualified (Just mn) _)) = [mn]
   fqValues _ = []
+  
+  fqBinders :: A.Binder -> [ModuleName]
+  fqBinders (A.ConstructorBinder (Qualified (Just mn) _) _) = [mn]
+  fqBinders _ = []
 
 -- |
 -- Desugars import declarations from AST to CoreFn representation.
diff --git a/src/Language/PureScript/CoreFn/Module.hs b/src/Language/PureScript/CoreFn/Module.hs
--- a/src/Language/PureScript/CoreFn/Module.hs
+++ b/src/Language/PureScript/CoreFn/Module.hs
@@ -15,12 +15,14 @@
 module Language.PureScript.CoreFn.Module where
 
 import Language.PureScript.CodeGen.JS.AST
+import Language.PureScript.Comments
 import Language.PureScript.CoreFn.Expr
 import Language.PureScript.Names
 import Language.PureScript.Types
 
 data Module a = Module
-  { moduleName :: ModuleName
+  { moduleComments :: [Comment]
+  , moduleName :: ModuleName
   , moduleImports :: [ModuleName]
   , moduleExports :: [Ident]
   , moduleForeign :: [ForeignDecl]
diff --git a/src/Language/PureScript/DeadCodeElimination.hs b/src/Language/PureScript/DeadCodeElimination.hs
--- a/src/Language/PureScript/DeadCodeElimination.hs
+++ b/src/Language/PureScript/DeadCodeElimination.hs
@@ -29,7 +29,7 @@
 eliminateDeadCode :: [ModuleName] -> [Module a] -> [Module a]
 eliminateDeadCode entryPoints ms = map go ms
   where
-  go (Module mn imps exps foreigns ds) = Module mn imps exps' foreigns' ds'
+  go (Module coms mn imps exps foreigns ds) = Module coms mn imps exps' foreigns' ds'
     where
     ds' = filter (isUsed mn graph vertexFor entryPointVertices) ds
     foreigns' = filter (isUsed' mn graph vertexFor entryPointVertices . foreignIdent) foreigns
@@ -61,7 +61,7 @@
 -- Find dependencies for each member in a module.
 --
 declarationsByModule :: Module a -> [(Key, [Key])]
-declarationsByModule (Module mn _ _ fs ds) =
+declarationsByModule (Module _ mn _ _ fs ds) =
   let fs' = map ((\name -> ((mn, name), [])) . foreignIdent) fs
   in fs' ++ concatMap go ds
   where
diff --git a/src/Language/PureScript/Errors.hs b/src/Language/PureScript/Errors.hs
--- a/src/Language/PureScript/Errors.hs
+++ b/src/Language/PureScript/Errors.hs
@@ -12,110 +12,488 @@
 --
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
 
 module Language.PureScript.Errors where
 
 import Data.Either (lefts, rights)
-import Data.String (IsString(..))
-import Data.List (intersperse, intercalate)
+import Data.List (intercalate)
 import Data.Monoid
+import Data.Foldable (fold, foldMap)
 
 import Control.Monad.Except
+import Control.Monad.Unify
 import Control.Applicative ((<$>))
 
 import Language.PureScript.AST
 import Language.PureScript.Pretty
 import Language.PureScript.Types
+import Language.PureScript.Names
+import Language.PureScript.Kinds
+import Language.PureScript.TypeClassDictionaries
 
--- |
--- Type for sources of type checking errors
---
-data ErrorSource
-  -- |
-  -- An error which originated at a Expr
-  --
-  = ExprError Expr
-  -- |
-  -- An error which originated at a Type
-  --
-  | TypeError Type deriving (Show)
+import qualified Text.PrettyPrint.Boxes as Box
 
 -- |
--- Compilation errors
+-- A type of error messages
 --
-data CompileError
-  = CompileError
-      { -- |
-        -- Error message
-        --
-        compileErrorMessage :: String
-        -- |
-        -- The value where the error occurred
-        --
-      , compileErrorValue :: Maybe ErrorSource
-        -- |
-        -- Optional source position information
-        --
-      , compileErrorPosition :: Maybe SourceSpan
-      }
+data ErrorMessage 
+  = InfiniteType Type
+  | InfiniteKind Kind
+  | CannotReorderOperators
+  | MultipleFixities Ident
+  | OrphanTypeDeclaration Ident
+  | OrphanFixityDeclaration String
+  | RedefinedModule ModuleName
+  | RedefinedIdent Ident
+  | OverlappingNamesInLet
+  | UnknownModule ModuleName
+  | UnknownType (Qualified ProperName)
+  | UnknownTypeClass (Qualified ProperName)
+  | UnknownValue (Qualified Ident)
+  | UnknownDataConstructor (Qualified ProperName) (Maybe (Qualified ProperName))
+  | UnknownTypeConstructor (Qualified ProperName)
+  | ConflictingImport String ModuleName
+  | ConflictingImports String ModuleName ModuleName  
+  | ConflictingTypeDecls ProperName
+  | ConflictingCtorDecls ProperName
+  | TypeConflictsWithClass ProperName
+  | CtorConflictsWithClass ProperName
+  | ClassConflictsWithType ProperName
+  | ClassConflictsWithCtor ProperName
+  | DuplicateClassExport ProperName
+  | DuplicateValueExport Ident
+  | DuplicateTypeArgument String
+  | InvalidDoBind
+  | InvalidDoLet
+  | CycleInDeclaration Ident
+  | CycleInTypeSynonym (Maybe ProperName)
+  | NameIsUndefined Ident
+  | NameNotInScope Ident
+  | UndefinedTypeVariable ProperName
+  | PartiallyAppliedSynonym (Qualified ProperName)
+  | NotYetDefined [Ident] ErrorMessage
+  | EscapedSkolem (Maybe Expr)
+  | UnspecifiedSkolemScope
+  | TypesDoNotUnify Type Type
+  | KindsDoNotUnify Kind Kind
+  | ConstrainedTypeUnified Type Type
+  | OverlappingInstances (Qualified ProperName) [Type] [DictionaryValue]
+  | NoInstanceFound (Qualified ProperName) [Type]
+  | DuplicateLabel String (Maybe Expr)
+  | DuplicateValueDeclaration Ident
+  | ArgListLengthsDiffer Ident
+  | OverlappingArgNames (Maybe Ident)
+  | MissingClassMember Ident
+  | ExpectedType Kind
+  | IncorrectConstructorArity (Qualified ProperName)
+  | SubsumptionCheckFailed
+  | ExprDoesNotHaveType Expr Type
+  | PropertyIsMissing String Type
+  | ErrorUnifyingTypes Type Type ErrorMessage
+  | CannotApplyFunction Type Expr
+  | TypeSynonymInstance
+  | InvalidNewtype
+  | InvalidInstanceHead Type
+  | TransitiveExportError DeclarationRef [DeclarationRef]
+  | ErrorInExpression Expr ErrorMessage
+  | ErrorInModule ModuleName ErrorMessage
+  | ErrorInInstance (Qualified ProperName) [Type] ErrorMessage
+  | ErrorInSubsumption Type Type ErrorMessage
+  | ErrorCheckingType Expr Type ErrorMessage
+  | ErrorCheckingKind Type ErrorMessage
+  | ErrorInferringType Expr ErrorMessage
+  | ErrorInApplication Expr Type Expr ErrorMessage
+  | ErrorInDataConstructor ProperName ErrorMessage
+  | ErrorInTypeConstructor ProperName ErrorMessage
+  | ErrorInBindingGroup [Ident] ErrorMessage
+  | ErrorInDataBindingGroup ErrorMessage
+  | ErrorInTypeSynonym ProperName ErrorMessage
+  | ErrorInValueDeclaration Ident ErrorMessage
+  | ErrorInForeignImport Ident ErrorMessage
+  | PositionedError SourceSpan ErrorMessage
   deriving (Show)
+  
+instance UnificationError Type ErrorMessage where
+  occursCheckFailed = InfiniteType
+  
+instance UnificationError Kind ErrorMessage where
+  occursCheckFailed = InfiniteKind
 
 -- |
+-- Get the error code for a particular error type
+--
+errorCode :: ErrorMessage -> String
+errorCode (InfiniteType _)              = "InfiniteType"
+errorCode (InfiniteKind _)              = "InfiniteKind"
+errorCode CannotReorderOperators        = "CannotReorderOperators"
+errorCode (MultipleFixities _)          = "MultipleFixities"
+errorCode (OrphanTypeDeclaration _)     = "OrphanTypeDeclaration"
+errorCode (OrphanFixityDeclaration _)   = "OrphanFixityDeclaration"
+errorCode (RedefinedModule _)           = "RedefinedModule"
+errorCode (RedefinedIdent _)            = "RedefinedIdent"
+errorCode OverlappingNamesInLet         = "OverlappingNamesInLet"
+errorCode (UnknownModule _)             = "UnknownModule"
+errorCode (UnknownType _)               = "UnknownType"
+errorCode (UnknownTypeClass _)          = "UnknownTypeClass"
+errorCode (UnknownValue _)              = "UnknownValue"
+errorCode (UnknownDataConstructor _ _)  = "UnknownDataConstructor"
+errorCode (UnknownTypeConstructor _)    = "UnknownTypeConstructor"
+errorCode (ConflictingImport _ _)       = "ConflictingImport"
+errorCode (ConflictingImports _ _ _)    = "ConflictingImports"
+errorCode (ConflictingTypeDecls _)      = "ConflictingTypeDecls"
+errorCode (ConflictingCtorDecls _)      = "ConflictingCtorDecls"
+errorCode (TypeConflictsWithClass _)    = "TypeConflictsWithClass"
+errorCode (CtorConflictsWithClass _)    = "CtorConflictsWithClass"
+errorCode (ClassConflictsWithType _)    = "ClassConflictsWithType"
+errorCode (ClassConflictsWithCtor _)    = "ClassConflictsWithCtor"
+errorCode (DuplicateClassExport _)      = "DuplicateClassExport"
+errorCode (DuplicateValueExport _)      = "DuplicateValueExport"
+errorCode (DuplicateTypeArgument _)     = "DuplicateTypeArgument"
+errorCode InvalidDoBind                 = "InvalidDoBind"
+errorCode InvalidDoLet                  = "InvalidDoLet"
+errorCode (CycleInDeclaration _)        = "CycleInDeclaration"
+errorCode (CycleInTypeSynonym _)        = "CycleInTypeSynonym"
+errorCode (NameIsUndefined _)           = "NameIsUndefined"
+errorCode (NameNotInScope _)            = "NameNotInScope"
+errorCode (UndefinedTypeVariable _)     = "UndefinedTypeVariable"
+errorCode (PartiallyAppliedSynonym _)   = "PartiallyAppliedSynonym"
+errorCode (EscapedSkolem _)             = "EscapedSkolem"
+errorCode UnspecifiedSkolemScope        = "UnspecifiedSkolemScope"
+errorCode (TypesDoNotUnify _ _)         = "TypesDoNotUnify"
+errorCode (KindsDoNotUnify _ _)         = "KindsDoNotUnify"
+errorCode (ConstrainedTypeUnified _ _)  = "ConstrainedTypeUnified"
+errorCode (OverlappingInstances _ _ _)  = "OverlappingInstances"
+errorCode (NoInstanceFound _ _)         = "NoInstanceFound"
+errorCode (DuplicateLabel _ _)          = "DuplicateLabel"
+errorCode (DuplicateValueDeclaration _) = "DuplicateValueDeclaration"
+errorCode (ArgListLengthsDiffer _)      = "ArgListLengthsDiffer"
+errorCode (OverlappingArgNames _)       = "OverlappingArgNames"
+errorCode (MissingClassMember _)        = "MissingClassMember"
+errorCode (ExpectedType _)              = "ExpectedType"
+errorCode (IncorrectConstructorArity _) = "IncorrectConstructorArity"
+errorCode SubsumptionCheckFailed        = "SubsumptionCheckFailed"
+errorCode (ExprDoesNotHaveType _ _)     = "ExprDoesNotHaveType"
+errorCode (PropertyIsMissing _ _)       = "PropertyIsMissing"
+errorCode (ErrorUnifyingTypes _ _ _)    = "ErrorUnifyingTypes"
+errorCode (CannotApplyFunction _ _)     = "CannotApplyFunction"
+errorCode TypeSynonymInstance           = "TypeSynonymInstance"
+errorCode InvalidNewtype                = "InvalidNewtype"
+errorCode (InvalidInstanceHead _)       = "InvalidInstanceHead"
+errorCode (TransitiveExportError _ _)   = "TransitiveExportError"
+errorCode (NotYetDefined _ e)           = errorCode e
+errorCode (ErrorInExpression _ e)       = errorCode e
+errorCode (ErrorInModule _ e)           = errorCode e
+errorCode (ErrorInInstance _ _ e)       = errorCode e
+errorCode (ErrorInSubsumption _ _ e)    = errorCode e
+errorCode (ErrorCheckingType _ _ e)     = errorCode e
+errorCode (ErrorCheckingKind _ e)       = errorCode e
+errorCode (ErrorInferringType _ e)      = errorCode e
+errorCode (ErrorInApplication _ _ _ e)  = errorCode e
+errorCode (ErrorInDataConstructor _ e)  = errorCode e
+errorCode (ErrorInTypeConstructor _ e)  = errorCode e
+errorCode (ErrorInBindingGroup _ e)     = errorCode e
+errorCode (ErrorInDataBindingGroup e)   = errorCode e
+errorCode (ErrorInTypeSynonym _ e)      = errorCode e
+errorCode (ErrorInValueDeclaration _ e) = errorCode e
+errorCode (ErrorInForeignImport _ e)    = errorCode e
+errorCode (PositionedError _ e)         = errorCode e
+  
+-- |
 -- A stack trace for an error
 --
-data ErrorStack
-  = ErrorStack { runErrorStack :: [CompileError] }
-  | MultipleErrors [ErrorStack] deriving (Show)
+newtype MultipleErrors = MultipleErrors 
+  { runMultipleErrors :: [ErrorMessage] } deriving (Show, Monoid)
+  
+instance UnificationError Type MultipleErrors where
+  occursCheckFailed = errorMessage . occursCheckFailed
+  
+instance UnificationError Kind MultipleErrors where
+  occursCheckFailed = errorMessage . occursCheckFailed
 
--- TODO: Remove strMsg, the IsString instance, and unnecessary
--- OverloadedStrings pragmas. See #745
--- | Create an ErrorStack from a string
-strMsg :: String -> ErrorStack
-strMsg s = ErrorStack [CompileError s Nothing Nothing]
+-- |
+-- Create an error set from a single error message
+--
+errorMessage :: ErrorMessage -> MultipleErrors
+errorMessage err = MultipleErrors [err]
 
-instance IsString ErrorStack where
-  fromString = strMsg
+-- |
+-- Lift a function on ErrorMessage to a function on MultipleErrors
+--
+onErrorMessages :: (ErrorMessage -> ErrorMessage) -> MultipleErrors -> MultipleErrors
+onErrorMessages f = MultipleErrors . map f . runMultipleErrors
 
-prettyPrintErrorStack :: Bool -> ErrorStack -> String
-prettyPrintErrorStack printFullStack (ErrorStack es) =
-  case mconcat $ map (Last . compileErrorPosition) es of
-    Last (Just sourcePos) -> "Error at " ++ show sourcePos ++ ": \n" ++ prettyPrintErrorStack'
-    _ -> prettyPrintErrorStack'
+-- |
+-- Pretty print a single error, simplifying if necessary
+--
+prettyPrintSingleError :: Bool -> ErrorMessage -> Box.Box
+prettyPrintSingleError full e = prettyPrintErrorMessage (if full then e else simplifyErrorMessage e)
   where
-  prettyPrintErrorStack' :: String
-  prettyPrintErrorStack'
-    | printFullStack = intercalate "\n" (map showError (filter isErrorNonEmpty es))
-    | otherwise =
-      let
-        es' = filter isErrorNonEmpty es
-      in case length es' of
-        1 -> showError (head es')
-        _ -> showError (head es') ++ "\n" ++ showError (last es')
-prettyPrintErrorStack printFullStack (MultipleErrors es) =
-  unlines $ intersperse "" $ "Multiple errors:" : map (prettyPrintErrorStack printFullStack) es
+  -- |
+  -- Pretty print an ErrorMessage
+  --
+  prettyPrintErrorMessage :: ErrorMessage -> Box.Box
+  prettyPrintErrorMessage em =
+    paras
+      [ go em
+      , line ("See " ++ wikiUri ++ " for more information, or to contribute content related to this error.") 
+      ]
+    where
+    wikiUri :: String
+    wikiUri = "https://github.com/purescript/purescript/wiki/Error-Code-" ++ errorCode e
+      
+    go :: ErrorMessage -> Box.Box
+    go InvalidDoBind                   = line "Bind statement cannot be the last statement in a do block"
+    go InvalidDoLet                    = line "Let statement cannot be the last statement in a do block"
+    go CannotReorderOperators          = line "Unable to reorder operators"
+    go UnspecifiedSkolemScope          = line "Skolem variable scope is unspecified"
+    go OverlappingNamesInLet           = line "Overlapping names in let binding."
+    go (InfiniteType ty)               = paras [ line "Infinite type detected: "
+                                               , indent $ line $ prettyPrintType ty
+                                               ]
+    go (InfiniteKind ki)               = paras [ line "Infinite kind detected: "
+                                               , indent $ line $ prettyPrintKind ki
+                                               ]
+    go (MultipleFixities name)         = line $ "Multiple fixity declarations for " ++ show name
+    go (OrphanTypeDeclaration nm)      = line $ "Orphan type declaration for " ++ show nm
+    go (OrphanFixityDeclaration op)    = line $ "Orphan fixity declaration for " ++ show op
+    go (RedefinedModule name)          = line $ "Module " ++ show name ++ " has been defined multiple times"
+    go (RedefinedIdent name)           = line $ "Name " ++ show name ++ " has been defined multiple times"
+    go (UnknownModule mn)              = line $ "Unknown module " ++ show mn
+    go (UnknownType name)              = line $ "Unknown type " ++ show name
+    go (UnknownTypeClass name)         = line $ "Unknown type class " ++ show name
+    go (UnknownValue name)             = line $ "Unknown value " ++ show name
+    go (UnknownTypeConstructor name)   = line $ "Unknown type constructor " ++ show name
+    go (UnknownDataConstructor dc tc)  = line $ "Unknown data constructor " ++ show dc ++ foldMap ((" for type constructor " ++) . show) tc
+    go (ConflictingImport nm mn)       = line $ "Declaration " ++ nm ++ " conflicts with import " ++ show mn 
+    go (ConflictingImports nm m1 m2)   = line $ "Conflicting imports for " ++ nm ++ " from modules " ++ show m1 ++ " and " ++ show m2
+    go (ConflictingTypeDecls nm)       = line $ "Conflicting type declarations for " ++ show nm
+    go (ConflictingCtorDecls nm)       = line $ "Conflicting data constructor declarations for " ++ show nm
+    go (TypeConflictsWithClass nm)     = line $ "Type " ++ show nm ++ " conflicts with type class declaration of the same name"
+    go (CtorConflictsWithClass nm)     = line $ "Data constructor " ++ show nm ++ " conflicts with type class declaration of the same name"
+    go (ClassConflictsWithType nm)     = line $ "Type class " ++ show nm ++ " conflicts with type declaration of the same name"
+    go (ClassConflictsWithCtor nm)     = line $ "Type class " ++ show nm ++ " conflicts with data constructor declaration of the same name"
+    go (DuplicateClassExport nm)       = line $ "Duplicate export declaration for type class " ++ show nm
+    go (DuplicateValueExport nm)       = line $ "Duplicate export declaration for value " ++ show nm
+    go (CycleInDeclaration nm)         = line $ "Cycle in declaration of " ++ show nm
+    go (NotYetDefined names err)       = paras [ line $ "The following are not yet defined here: " ++ intercalate ", " (map show names) ++ ":"
+                                               , indent $ go err
+                                               ]
+    go (CycleInTypeSynonym pn)         = line $ "Cycle in type synonym" ++ foldMap ((" " ++) . show) pn
+    go (NameIsUndefined ident)         = line $ show ident ++ " is undefined"
+    go (NameNotInScope ident)          = line $ show ident ++ " may not be defined in the current scope"
+    go (UndefinedTypeVariable name)    = line $ "Type variable " ++ show name ++ " is undefined"
+    go (PartiallyAppliedSynonym name)  = line $ "Partially applied type synonym " ++ show name
+    go (EscapedSkolem binding)         = paras $ [ line "Rigid/skolem type variable has escaped." ]
+                                                 <> foldMap (\expr -> [ line "Relevant expression: "
+                                                                      , indent $ line $ prettyPrintValue expr 
+                                                                      ]) binding
+    go (TypesDoNotUnify t1 t2)         = paras [ line "Cannot unify type"
+                                               , indent $ line $ prettyPrintType t1
+                                               , line "with type"
+                                               , indent $ line $ prettyPrintType t2
+                                               ]
+    go (KindsDoNotUnify k1 k2)         = paras [ line "Cannot unify kind"
+                                               , indent $ line $ prettyPrintKind k1
+                                               , line "with kind"
+                                               , indent $ line $ prettyPrintKind k2
+                                               ]
+    go (ConstrainedTypeUnified t1 t2)  = paras [ line "Cannot unify constrained type"
+                                               , indent $ line $ prettyPrintType t1
+                                               , line "with type"
+                                               , indent $ line $ prettyPrintType t2
+                                               ]
+    go (OverlappingInstances nm ts ds) = paras [ line $ "Overlapping instances found for " ++ show nm ++ " " ++ unwords (map prettyPrintTypeAtom ts) ++ ":"
+                                               , paras $ map prettyPrintDictionaryValue ds
+                                               ]
+    go (NoInstanceFound nm ts)         = line $ "No instance found for " ++ show nm ++ " " ++ unwords (map prettyPrintTypeAtom ts)
+    go (DuplicateLabel l expr)         = paras $ [ line $ "Duplicate label " ++ show l ++ " in row." ]
+                                                 <> foldMap (\expr' -> [ line "Relevant expression: "
+                                                                       , indent $ line $ prettyPrintValue expr' 
+                                                                       ]) expr
+    go (DuplicateTypeArgument name)    = line $ "Duplicate type argument " ++ show name
+    go (DuplicateValueDeclaration nm)  = line $ "Duplicate value declaration for " ++ show nm
+    go (ArgListLengthsDiffer ident)    = line $ "Argument list lengths differ in declaration " ++ show ident
+    go (OverlappingArgNames ident)     = line $ "Overlapping names in function/binder" ++ foldMap ((" in declaration" ++) . show) ident
+    go (MissingClassMember ident)      = line $ "Member " ++ show ident ++ " has not been implemented"
+    go (ExpectedType kind)             = line $ "Expected type of kind *, was " ++ prettyPrintKind kind
+    go (IncorrectConstructorArity nm)  = line $ "Wrong number of arguments to constructor " ++ show nm
+    go SubsumptionCheckFailed          = line $ "Unable to check type subsumption"
+    go (ExprDoesNotHaveType expr ty)   = paras [ line "Expression"
+                                               , indent $ line $ prettyPrintValue expr
+                                               , line "does not have type"
+                                               , indent $ line $ prettyPrintType ty
+                                               ]
+    go (PropertyIsMissing prop row)    = line $ "Row " ++ prettyPrintRow row ++ " lacks required property " ++ show prop
+    go (CannotApplyFunction fn arg)    = paras [ line "Cannot apply function of type"
+                                               , indent $ line $ prettyPrintType fn
+                                               , line "to argument"
+                                               , indent $ line $ prettyPrintValue arg
+                                               ]
+    go TypeSynonymInstance             = line "Type synonym instances are disallowed"
+    go InvalidNewtype                  = line "Newtypes must define a single constructor with a single argument"
+    go (InvalidInstanceHead ty)        = paras [ line "Invalid type in class instance head:"
+                                               , indent $ line $ prettyPrintType ty
+                                               ]
+    go (TransitiveExportError x ys)    = paras $ (line $ "An export for " ++ prettyPrintExport x ++ " requires the following to also be exported: ")
+                                                 : map (line . prettyPrintExport) ys
+    go (ErrorUnifyingTypes t1 t2 err)  = paras [ line "Error unifying type "
+                                               , indent $ line $ prettyPrintType t1
+                                               , line "with type"
+                                               , indent $ line $ prettyPrintType t2
+                                               , go err
+                                               ]
+    go (ErrorInExpression expr err)    = paras [ line "Error in expression:"
+                                               , indent $ line $ prettyPrintValue expr
+                                               , go err
+                                               ]
+    go (ErrorInModule mn err)          = paras [ line $ "Error in module " ++ show mn ++ ":"
+                                               , go err
+                                               ]
+    go (ErrorInSubsumption t1 t2 err)  = paras [ line "Error checking that type "
+                                               , indent $ line $ prettyPrintType t1
+                                               , line "subsumes type"
+                                               , indent $ line $ prettyPrintType t2
+                                               , go err
+                                               ]
+    go (ErrorInInstance name ts err)   = paras [ line $ "Error in type class instance " ++ show name ++ " " ++ unwords (map prettyPrintTypeAtom ts) ++ ":"
+                                               , go err
+                                               ]
+    go (ErrorCheckingKind ty err)      = paras [ line "Error checking kind of type "
+                                               , indent $ line $ prettyPrintType ty
+                                               , go err
+                                               ]
+    go (ErrorInferringType expr err)   = paras [ line "Error inferring type of value "
+                                               , indent $ line $ prettyPrintValue expr
+                                               , go err
+                                               ]
+    go (ErrorCheckingType expr ty err) = paras [ line "Error checking that value "
+                                               , indent $ line $ prettyPrintValue expr
+                                               , line "has type"
+                                               , indent $ line $ prettyPrintType ty
+                                               , go err
+                                               ]
+    go (ErrorInApplication f t a err)  = paras [ line "Error applying function"
+                                               , indent $ line $ prettyPrintValue f
+                                               , line "of type"
+                                               , indent $ line $ prettyPrintType t
+                                               , line "to argument"
+                                               , indent $ line $ prettyPrintValue a
+                                               , go err
+                                               ]
+    go (ErrorInDataConstructor nm err) = paras [ line $ "Error in data constructor " ++ show nm ++ ":"
+                                               , go err
+                                               ]
+    go (ErrorInTypeConstructor nm err) = paras [ line $ "Error in type constructor " ++ show nm ++ ":"
+                                               , go err
+                                               ]
+    go (ErrorInBindingGroup nms err)   = paras [ line $ "Error in binding group " ++ intercalate ", " (map show nms) ++ ":"
+                                               , go err
+                                               ]
+    go (ErrorInDataBindingGroup err)   = paras [ line $ "Error in data binding group:"
+                                               , go err
+                                               ]
+    go (ErrorInTypeSynonym name err)   = paras [ line $ "Error in type synonym " ++ show name ++ ":"
+                                               , go err
+                                               ]
+    go (ErrorInValueDeclaration n err) = paras [ line $ "Error in value declaration " ++ show n ++ ":"
+                                               , go err
+                                               ]
+    go (ErrorInForeignImport nm err)   = paras [ line $ "Error in foreign import " ++ show nm ++ ":"
+                                               , go err
+                                               ]
+    go (PositionedError pos err)       = paras [ line $ "Error at " ++ show pos ++ ":"
+                                               , indent $ go err
+                                               ]
 
-stringifyErrorStack :: (MonadError String m) => Bool -> Either ErrorStack a -> m a
-stringifyErrorStack printFullStack = either (throwError . prettyPrintErrorStack printFullStack) return
+  line :: String -> Box.Box
+  line = Box.text
 
-isErrorNonEmpty :: CompileError -> Bool
-isErrorNonEmpty = not . null . compileErrorMessage
+  paras :: [Box.Box] -> Box.Box
+  paras = Box.vcat Box.left
 
-showError :: CompileError -> String
-showError (CompileError msg Nothing _) = msg
-showError (CompileError msg (Just (ExprError val)) _) = "Error in expression " ++ prettyPrintValue val ++ ":\n" ++ msg
-showError (CompileError msg (Just (TypeError ty)) _) = "Error in type " ++ prettyPrintType ty ++ ":\n" ++ msg
+  indent :: Box.Box -> Box.Box
+  indent = Box.moveRight 2
 
-mkErrorStack :: String -> Maybe ErrorSource -> ErrorStack
-mkErrorStack msg t = ErrorStack [mkCompileError msg t]
+  -- |
+  -- Render a DictionaryValue fit for human consumption in error messages
+  --
+  prettyPrintDictionaryValue :: DictionaryValue -> Box.Box
+  prettyPrintDictionaryValue (LocalDictionaryValue _)           = line "Dictionary in scope"
+  prettyPrintDictionaryValue (GlobalDictionaryValue nm)         = line (show nm)
+  prettyPrintDictionaryValue (DependentDictionaryValue nm args) = paras [ line $ (show nm) ++ " via"
+                                                                        , indent $ paras $ map prettyPrintDictionaryValue args
+                                                                        ]
+  prettyPrintDictionaryValue (SubclassDictionaryValue sup nm _) = paras [ line $ (show nm) ++ " via superclass"
+                                                                        , indent $ prettyPrintDictionaryValue sup
+                                                                        ]
+  
+  -- |
+  -- Pretty print and export declaration
+  --  
+  prettyPrintExport :: DeclarationRef -> String
+  prettyPrintExport (TypeRef pn _) = show pn
+  prettyPrintExport (ValueRef ident) = show ident
+  prettyPrintExport (TypeClassRef pn) = show pn
+  prettyPrintExport (TypeInstanceRef ident) = show ident
+  prettyPrintExport (PositionedDeclarationRef _ _ ref) = prettyPrintExport ref
 
-mkCompileError :: String -> Maybe ErrorSource -> CompileError
-mkCompileError msg t = CompileError msg t Nothing
+  -- |
+  -- Simplify an error message
+  --
+  simplifyErrorMessage :: ErrorMessage -> ErrorMessage
+  simplifyErrorMessage = unwrap Nothing
+    where
+    unwrap :: Maybe SourceSpan -> ErrorMessage -> ErrorMessage
+    unwrap pos (ErrorInExpression _ err) = unwrap pos err
+    unwrap pos (ErrorInInstance name ts err) = ErrorInInstance name ts (unwrap pos err)
+    unwrap pos (ErrorInSubsumption t1 t2 err) = ErrorInSubsumption t1 t2 (unwrap pos err)
+    unwrap pos (ErrorUnifyingTypes _ _ err) = unwrap pos err
+    unwrap pos (ErrorInferringType _ err) = unwrap pos err
+    unwrap pos (ErrorCheckingType _ _ err) = unwrap pos err
+    unwrap pos (ErrorCheckingKind ty err) = ErrorCheckingKind ty (unwrap pos err)
+    unwrap pos (ErrorInModule mn err) = ErrorInModule mn (unwrap pos err)
+    unwrap pos (ErrorInApplication _ _ _ err) = unwrap pos err
+    unwrap pos (ErrorInDataConstructor nm err) = ErrorInDataConstructor nm (unwrap pos err)
+    unwrap pos (ErrorInTypeConstructor nm err) = ErrorInTypeConstructor nm (unwrap pos err)
+    unwrap pos (ErrorInBindingGroup nms err) = ErrorInBindingGroup nms (unwrap pos err)
+    unwrap pos (ErrorInDataBindingGroup err) = ErrorInDataBindingGroup (unwrap pos err)
+    unwrap pos (ErrorInTypeSynonym nm err) = ErrorInTypeSynonym nm (unwrap pos err)
+    unwrap pos (ErrorInValueDeclaration nm err) = ErrorInValueDeclaration nm (unwrap pos err)
+    unwrap pos (ErrorInForeignImport nm err) = ErrorInForeignImport nm (unwrap pos err)
+    unwrap pos (NotYetDefined ns err) = NotYetDefined ns (unwrap pos err)
+    unwrap _   (PositionedError pos err) = unwrap (Just pos) err
+    unwrap pos other = wrap pos other
+  
+    wrap :: Maybe SourceSpan -> ErrorMessage -> ErrorMessage
+    wrap Nothing    = id
+    wrap (Just pos) = PositionedError pos
 
-positionError :: SourceSpan -> CompileError
-positionError pos = CompileError "" Nothing (Just pos)
+-- |
+-- Pretty print multiple errors
+--
+prettyPrintMultipleErrors :: Bool -> MultipleErrors -> String
+prettyPrintMultipleErrors full  (MultipleErrors [e]) = renderBox $
+  prettyPrintSingleError full e 
+prettyPrintMultipleErrors full  (MultipleErrors es) = renderBox $
+  Box.vcat Box.left [ Box.text "Multiple errors:"
+                    , Box.vsep 1 Box.left $ map (Box.moveRight 2 . prettyPrintSingleError full) es
+                    ]
 
+renderBox :: Box.Box -> String
+renderBox = unlines . map trimEnd . lines . Box.render
+  where
+  trimEnd = reverse . dropWhile (== ' ') . reverse
+
 -- |
+-- Interpret multiple errors in a monad supporting errors
+--
+interpretMultipleErrors :: (MonadError String m) => Bool -> Either MultipleErrors a -> m a
+interpretMultipleErrors printFullStack = either (throwError . prettyPrintMultipleErrors printFullStack) return
+
+-- |
 -- Rethrow an error with a more detailed error message in the case of failure
 --
 rethrow :: (MonadError e m) => (e -> e) -> m a -> m a
@@ -124,30 +502,23 @@
 -- |
 -- Rethrow an error with source position information
 --
-rethrowWithPosition :: (MonadError ErrorStack m) => SourceSpan -> m a -> m a
-rethrowWithPosition pos = rethrow (positionError pos `combineErrors`)
+rethrowWithPosition :: (MonadError MultipleErrors m) => SourceSpan -> m a -> m a
+rethrowWithPosition pos = rethrow (onErrorMessages withPosition)
+  where
+  withPosition :: ErrorMessage -> ErrorMessage
+  withPosition (PositionedError _ err) = withPosition err
+  withPosition err = PositionedError pos err
 
 -- |
 -- Collect errors in in parallel
 --
-parU :: (MonadError ErrorStack m, Functor m) => [a] -> (a -> m b) -> m [b]
+parU :: (MonadError MultipleErrors m, Functor m) => [a] -> (a -> m b) -> m [b]
 parU xs f = forM xs (withError . f) >>= collectErrors
   where
-  withError :: (MonadError ErrorStack m, Functor m) => m a -> m (Either ErrorStack a)
+  withError :: (MonadError MultipleErrors m, Functor m) => m a -> m (Either MultipleErrors a)
   withError u = catchError (Right <$> u) (return . Left)
 
-  collectErrors :: (MonadError ErrorStack m, Functor m) => [Either ErrorStack a] -> m [a]
+  collectErrors :: (MonadError MultipleErrors m, Functor m) => [Either MultipleErrors a] -> m [a]
   collectErrors es = case lefts es of
-    [err] -> throwError err
     [] -> return $ rights es
-    errs -> throwError $ MultipleErrors errs
- 
--- |
--- Add an extra error string onto the top of each error stack in a list of possibly many errors
---
-combineErrors :: CompileError -> ErrorStack -> ErrorStack
-combineErrors ce err = go (ErrorStack [ce]) err
-  where 
-  go (ErrorStack xs) (ErrorStack ys) = ErrorStack (xs ++ ys)
-  go (MultipleErrors es) x = MultipleErrors [ go e x | e <- es ]
-  go x (MultipleErrors es) = MultipleErrors [ go x e | e <- es ]
+    errs -> throwError $ fold errs
diff --git a/src/Language/PureScript/ModuleDependencies.hs b/src/Language/PureScript/ModuleDependencies.hs
--- a/src/Language/PureScript/ModuleDependencies.hs
+++ b/src/Language/PureScript/ModuleDependencies.hs
@@ -41,7 +41,7 @@
 --
 sortModules :: (MonadError String m) => [Module] -> m ([Module], ModuleGraph)
 sortModules ms = do
-  let verts = map (\m@(Module _ ds _) -> (m, getModuleName m, nub (concatMap usedModules ds))) ms
+  let verts = map (\m@(Module _ _ ds _) -> (m, getModuleName m, nub (concatMap usedModules ds))) ms
   ms' <- mapM toModule $ stronglyConnComp verts
   let moduleGraph = map (\(_, mn, deps) -> (mn, deps)) verts
   return (ms', moduleGraph)
diff --git a/src/Language/PureScript/Parser/Declarations.hs b/src/Language/PureScript/Parser/Declarations.hs
--- a/src/Language/PureScript/Parser/Declarations.hs
+++ b/src/Language/PureScript/Parser/Declarations.hs
@@ -24,6 +24,8 @@
     parseGuard,
     parseBinder,
     parseBinderNoParens,
+    parseImportDeclaration',
+    parseLocalDeclaration
 ) where
 
 import Prelude hiding (lex)
@@ -146,6 +148,11 @@
 
 parseImportDeclaration :: TokenParser Declaration
 parseImportDeclaration = do
+  (mn, declType, asQ) <- parseImportDeclaration'
+  return $ ImportDeclaration mn declType asQ
+
+parseImportDeclaration' :: TokenParser (ModuleName, ImportDeclarationType, (Maybe ModuleName))
+parseImportDeclaration' = do
   reserved "import"
   indented
   qualImport <|> stdImport
@@ -157,21 +164,21 @@
     stdImportHiding mn = do
       reserved "hiding"
       declType <- importDeclarationType Hiding
-      return $ ImportDeclaration mn declType Nothing
+      return (mn, declType, Nothing)
     stdImportQualifying mn = do
-      declType <- importDeclarationType Qualifying
-      return $ ImportDeclaration mn declType Nothing
+      declType <- importDeclarationType Explicit
+      return (mn, declType, Nothing)
   qualImport = do
     reserved "qualified"
     indented
     moduleName' <- moduleName
-    declType <- importDeclarationType Qualifying
+    declType <- importDeclarationType Explicit
     reserved "as"
     asQ <- moduleName
-    return $ ImportDeclaration moduleName' declType (Just asQ)
+    return (moduleName', declType, Just asQ)
   importDeclarationType expectedType = do
     idents <- P.optionMaybe $ indented *> (parens $ commaSep parseDeclarationRef)
-    return $ fromMaybe Unqualified (expectedType <$> idents)
+    return $ fromMaybe Implicit (expectedType <$> idents)
 
 
 parseDeclarationRef :: TokenParser DeclarationRef
@@ -242,13 +249,14 @@
 --
 parseModule :: TokenParser Module
 parseModule = do
+  comments <- C.readComments
   reserved "module"
   indented
   name <- moduleName
   exports <- P.optionMaybe $ parens $ commaSep1 parseDeclarationRef
   reserved "where"
   decls <- mark (P.many (same *> parseDeclaration))
-  return $ Module name decls exports
+  return $ Module comments name decls exports
 
 -- |
 -- Parse a collection of modules
diff --git a/src/Language/PureScript/Pretty/JS.hs b/src/Language/PureScript/Pretty/JS.hs
--- a/src/Language/PureScript/Pretty/JS.hs
+++ b/src/Language/PureScript/Pretty/JS.hs
@@ -272,26 +272,26 @@
                         ++ fromMaybe "" name
                         ++ "(" ++ intercalate ", " args ++ ") "
                         ++ ret ]
-                  , [ binary    LessThan             "<" ]
-                  , [ binary    LessThanOrEqualTo    "<=" ]
-                  , [ binary    GreaterThan          ">" ]
-                  , [ binary    GreaterThanOrEqualTo ">=" ]
                   , [ Wrap typeOf $ \_ s -> "typeof " ++ s ]
-                  , [ AssocR instanceOf $ \v1 v2 -> v1 ++ " instanceof " ++ v2 ]
-                  , [ unary     Not                  "!" ]
-                  , [ unary     BitwiseNot           "~" ]
-                  , [ negateOperator ]
-                  , [ unary     Positive             "+" ]
+                  , [ unary     Not                  "!"
+                    , unary     BitwiseNot           "~"
+                    , unary     Positive             "+"
+                    , negateOperator ]
                   , [ binary    Multiply             "*"
                     , binary    Divide               "/"
                     , binary    Modulus              "%" ]
                   , [ binary    Add                  "+"
                     , binary    Subtract             "-" ]
-                  , [ binary    ShiftLeft            "<<" ]
-                  , [ binary    ShiftRight           ">>" ]
-                  , [ binary    ZeroFillShiftRight   ">>>" ]
-                  , [ binary    EqualTo              "===" ]
-                  , [ binary    NotEqualTo           "!==" ]
+                  , [ binary    ShiftLeft            "<<"
+                    , binary    ShiftRight           ">>"
+                    , binary    ZeroFillShiftRight   ">>>" ]
+                  , [ binary    LessThan             "<"
+                    , binary    LessThanOrEqualTo    "<="
+                    , binary    GreaterThan          ">"
+                    , binary    GreaterThanOrEqualTo ">="
+                    , AssocR instanceOf $ \v1 v2 -> v1 ++ " instanceof " ++ v2 ]
+                  , [ binary    EqualTo              "==="
+                    , binary    NotEqualTo           "!==" ]
                   , [ binary    BitwiseAnd           "&" ]
                   , [ binary    BitwiseXor           "^" ]
                   , [ binary    BitwiseOr            "|" ]
diff --git a/src/Language/PureScript/Pretty/Values.hs b/src/Language/PureScript/Pretty/Values.hs
--- a/src/Language/PureScript/Pretty/Values.hs
+++ b/src/Language/PureScript/Pretty/Values.hs
@@ -1,7 +1,7 @@
 -----------------------------------------------------------------------------
 --
 -- Module      :  Language.PureScript.Pretty.Values
--- Copyright   :  Kinds.hs(c) Phil Freeman 2013
+-- Copyright   :  (c) Phil Freeman 2013
 -- License     :  MIT
 --
 -- Maintainer  :  Phil Freeman <paf31@cantab.net>
@@ -29,7 +29,7 @@
 import Language.PureScript.AST
 import Language.PureScript.Names
 import Language.PureScript.Pretty.Common
-import Language.PureScript.Pretty.Types (prettyPrintType)
+import Language.PureScript.Pretty.Types (prettyPrintType, prettyPrintTypeAtom)
 
 literals :: Pattern PrinterState Expr String
 literals = mkPattern' match
@@ -76,7 +76,7 @@
     ]
   match (OperatorSection op (Right val)) = return $ "(" ++ prettyPrintValue op ++ " " ++ prettyPrintValue val ++ ")"
   match (OperatorSection op (Left val)) = return $ "(" ++ prettyPrintValue val ++ " " ++ prettyPrintValue op ++ ")"
-  match (TypeClassDictionary name _ _) = return $ "<<dict " ++ show name ++ ">>"
+  match (TypeClassDictionary _ (name, tys) _) = return $ "<<dict " ++ show name ++ " " ++ unwords (map prettyPrintTypeAtom tys) ++ ">>"
   match (SuperClassDictionary name _) = return $ "<<superclass dict " ++ show name ++ ">>"
   match (TypedValue _ val _) = prettyPrintValue' val
   match (PositionedValue _ _ val) = prettyPrintValue' val
diff --git a/src/Language/PureScript/Renamer.hs b/src/Language/PureScript/Renamer.hs
--- a/src/Language/PureScript/Renamer.hs
+++ b/src/Language/PureScript/Renamer.hs
@@ -113,7 +113,7 @@
 renameInModules = map go
   where
   go :: Module Ann -> Module Ann
-  go m@(Module _ _ _ _ decls) = m { moduleDecls = renameInDecl' (findDeclIdents decls) `map` decls }
+  go m@(Module _ _ _ _ _ decls) = m { moduleDecls = renameInDecl' (findDeclIdents decls) `map` decls }
   renameInDecl' :: [Ident] -> Bind Ann -> Bind Ann
   renameInDecl' scope = runRename scope . renameInDecl True
 
diff --git a/src/Language/PureScript/Sugar.hs b/src/Language/PureScript/Sugar.hs
--- a/src/Language/PureScript/Sugar.hs
+++ b/src/Language/PureScript/Sugar.hs
@@ -13,15 +13,18 @@
 --
 -----------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleContexts #-}
+
 module Language.PureScript.Sugar (desugar, module S) where
 
 import Control.Monad
 import Control.Category ((>>>))
-import Control.Monad.Trans.Class
+import Control.Applicative
+import Control.Monad.Error.Class
+import Control.Monad.Supply.Class
 
 import Language.PureScript.AST
 import Language.PureScript.Errors
-import Language.PureScript.Supply
 
 import Language.PureScript.Sugar.BindingGroups as S
 import Language.PureScript.Sugar.CaseDeclarations as S
@@ -55,14 +58,14 @@
 --
 --  * Group mutually recursive value and data declarations into binding groups.
 --
-desugar :: [Module] -> SupplyT (Either ErrorStack) [Module]
+desugar :: (Applicative m, MonadSupply m, MonadError MultipleErrors m) => [Module] -> m [Module]
 desugar = map removeSignedLiterals
           >>> mapM desugarObjectConstructors
           >=> mapM desugarOperatorSections
           >=> mapM desugarDoModule
           >=> desugarCasesModule
-          >=> lift . (desugarTypeDeclarationsModule
-                      >=> desugarImports
-                      >=> rebracket)
+          >=> desugarTypeDeclarationsModule
+          >=> desugarImports
+          >=> rebracket
           >=> desugarTypeClasses
-          >=> lift . createBindingGroupsModule
+          >=> createBindingGroupsModule
diff --git a/src/Language/PureScript/Sugar/BindingGroups.hs b/src/Language/PureScript/Sugar/BindingGroups.hs
--- a/src/Language/PureScript/Sugar/BindingGroups.hs
+++ b/src/Language/PureScript/Sugar/BindingGroups.hs
@@ -14,6 +14,8 @@
 --
 -----------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleContexts #-}
+
 module Language.PureScript.Sugar.BindingGroups (
     createBindingGroups,
     createBindingGroupsModule,
@@ -24,8 +26,9 @@
 import Data.Graph
 import Data.List (nub, intersect)
 import Data.Maybe (isJust, mapMaybe)
-import Control.Applicative ((<$>), (<*>), pure)
+import Control.Applicative
 import Control.Monad ((<=<))
+import Control.Monad.Error.Class
 
 import qualified Data.Set as S
 
@@ -38,29 +41,29 @@
 -- |
 -- Replace all sets of mutually-recursive declarations in a module with binding groups
 --
-createBindingGroupsModule :: [Module] -> Either ErrorStack [Module]
-createBindingGroupsModule = mapM $ \(Module name ds exps) -> Module name <$> createBindingGroups name ds <*> pure exps
+createBindingGroupsModule :: (Functor m, Applicative m, MonadError MultipleErrors m) => [Module] -> m [Module]
+createBindingGroupsModule = mapM $ \(Module coms name ds exps) -> Module coms name <$> createBindingGroups name ds <*> pure exps
 
 -- |
 -- Collapse all binding groups in a module to individual declarations
 --
 collapseBindingGroupsModule :: [Module] -> [Module]
-collapseBindingGroupsModule = map $ \(Module name ds exps) -> Module name (collapseBindingGroups ds) exps
+collapseBindingGroupsModule = map $ \(Module coms name ds exps) -> Module coms name (collapseBindingGroups ds) exps
 
-createBindingGroups :: ModuleName -> [Declaration] -> Either ErrorStack [Declaration]
+createBindingGroups :: (Functor m, Applicative m, MonadError MultipleErrors m) => ModuleName -> [Declaration] -> m [Declaration]
 createBindingGroups moduleName = mapM f <=< handleDecls
 
   where
   (f, _, _) = everywhereOnValuesTopDownM return handleExprs return
 
-  handleExprs :: Expr -> Either ErrorStack Expr
+  handleExprs :: (Functor m, MonadError MultipleErrors m) => Expr -> m Expr
   handleExprs (Let ds val) = flip Let val <$> handleDecls ds
   handleExprs other = return other
 
   -- |
   -- Replace all sets of mutually-recursive declarations with binding groups
   --
-  handleDecls :: [Declaration] -> Either ErrorStack [Declaration]
+  handleDecls :: (Functor m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration]
   handleDecls ds = do
     let values = filter isValueDecl ds
         dataDecls = filter isDataDecl ds
@@ -75,6 +78,7 @@
              filter isExternInstanceDecl ds ++
              dataBindingGroupDecls ++
              filter isTypeClassDeclaration ds ++
+             filter isTypeClassInstanceDeclaration ds ++
              filter isFixityDecl ds ++
              filter isExternDecl ds ++
              bindingGroupDecls
@@ -151,7 +155,7 @@
 -- Convert a group of mutually-recursive dependencies into a BindingGroupDeclaration (or simple ValueDeclaration).
 --
 --
-toBindingGroup :: ModuleName -> SCC Declaration -> Either ErrorStack Declaration
+toBindingGroup :: (Functor m, MonadError MultipleErrors m) => ModuleName -> SCC Declaration -> m Declaration
 toBindingGroup _ (AcyclicSCC d) = return d
 toBindingGroup _ (CyclicSCC [d]) = return d
 toBindingGroup moduleName (CyclicSCC ds') =
@@ -172,24 +176,23 @@
   valueVerts :: [(Declaration, Ident, [Ident])]
   valueVerts = map (\d -> (d, getIdent d, usedImmediateIdents moduleName d `intersect` idents)) ds'
 
-  toBinding :: SCC Declaration -> Either ErrorStack (Ident, NameKind, Expr)
+  toBinding :: (MonadError MultipleErrors m) => SCC Declaration -> m (Ident, NameKind, Expr)
   toBinding (AcyclicSCC d) = return $ fromValueDecl d
   toBinding (CyclicSCC ~(d:ds)) = cycleError d ds
 
-  cycleError :: Declaration -> [Declaration] -> Either ErrorStack a
+  cycleError :: (MonadError MultipleErrors m) => Declaration -> [Declaration] -> m a
   cycleError (PositionedDeclaration p _ d) ds = rethrowWithPosition p $ cycleError d ds
-  cycleError (ValueDeclaration n _ _ (Right e)) [] = Left $
-    mkErrorStack ("Cycle in definition of " ++ show n) (Just (ExprError e))
-  cycleError d ds@(_:_) = rethrow (mkCompileError ("The following are not yet defined here: " ++ unwords (map (show . getIdent) ds)) Nothing `combineErrors`) $ cycleError d []
+  cycleError (ValueDeclaration n _ _ (Right _)) [] = throwError . errorMessage $ CycleInDeclaration n
+  cycleError d ds@(_:_) = rethrow (onErrorMessages (NotYetDefined (map getIdent ds))) $ cycleError d []
   cycleError _ _ = error "Expected ValueDeclaration"
 
-toDataBindingGroup :: SCC Declaration -> Either ErrorStack Declaration
+toDataBindingGroup :: (MonadError MultipleErrors m) => SCC Declaration -> m Declaration
 toDataBindingGroup (AcyclicSCC d) = return d
 toDataBindingGroup (CyclicSCC [d]) = case isTypeSynonym d of
-  Just pn -> Left $ mkErrorStack ("Cycle in type synonym " ++ show pn) Nothing
+  Just pn -> throwError . errorMessage $ CycleInTypeSynonym (Just pn)
   _ -> return d
 toDataBindingGroup (CyclicSCC ds')
-  | all (isJust . isTypeSynonym) ds' = Left $ mkErrorStack "Cycle in type synonyms" Nothing
+  | all (isJust . isTypeSynonym) ds' = throwError . errorMessage $ CycleInTypeSynonym Nothing
   | otherwise = return $ DataBindingGroupDeclaration ds'
 
 isTypeSynonym :: Declaration -> Maybe ProperName
diff --git a/src/Language/PureScript/Sugar/CaseDeclarations.hs b/src/Language/PureScript/Sugar/CaseDeclarations.hs
--- a/src/Language/PureScript/Sugar/CaseDeclarations.hs
+++ b/src/Language/PureScript/Sugar/CaseDeclarations.hs
@@ -14,6 +14,8 @@
 --
 -----------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleContexts #-}
+
 module Language.PureScript.Sugar.CaseDeclarations (
     desugarCases,
     desugarCasesModule
@@ -24,12 +26,13 @@
 import Control.Applicative
 import Control.Monad ((<=<), forM, join, unless, replicateM)
 import Control.Monad.Except (throwError)
+import Control.Monad.Error.Class (MonadError)
+import Control.Monad.Supply.Class
 
 import Language.PureScript.Names
 import Language.PureScript.AST
 import Language.PureScript.Environment
 import Language.PureScript.Errors
-import Language.PureScript.Supply
 import Language.PureScript.Traversals
 import Language.PureScript.TypeChecker.Monad (guardWith)
 
@@ -41,17 +44,17 @@
 -- |
 -- Replace all top-level binders in a module with case expressions.
 --
-desugarCasesModule :: [Module] -> SupplyT (Either ErrorStack) [Module]
-desugarCasesModule ms = forM ms $ \(Module name ds exps) ->
-  rethrow (mkCompileError ("Error in module " ++ show name) Nothing `combineErrors`) $
-    Module name <$> (desugarCases <=< desugarAbs $ ds) <*> pure exps
+desugarCasesModule :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => [Module] -> m [Module]
+desugarCasesModule ms = forM ms $ \(Module coms name ds exps) ->
+  rethrow (onErrorMessages (ErrorInModule name)) $
+    Module coms name <$> (desugarCases <=< desugarAbs $ ds) <*> pure exps
 
-desugarAbs :: [Declaration] -> SupplyT (Either ErrorStack) [Declaration]
+desugarAbs :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration]
 desugarAbs = flip parU f
   where
   (f, _, _) = everywhereOnValuesM return replace return
 
-  replace :: Expr -> SupplyT (Either ErrorStack) Expr
+  replace :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => Expr -> m Expr
   replace (Abs (Right binder) val) = do
     ident <- Ident <$> freshName
     return $ Abs (Left ident) $ Case [Var (Qualified Nothing ident)] [CaseAlternative [binder] (Right val)]
@@ -60,10 +63,10 @@
 -- |
 -- Replace all top-level binders with case expressions.
 --
-desugarCases :: [Declaration] -> SupplyT (Either ErrorStack) [Declaration]
+desugarCases :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration]
 desugarCases = desugarRest <=< fmap join . flip parU toDecls . groupBy inSameGroup
   where
-    desugarRest :: [Declaration] -> SupplyT (Either ErrorStack) [Declaration]
+    desugarRest :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration]
     desugarRest (TypeInstanceDeclaration name constraints className tys ds : rest) =
       (:) <$> (TypeInstanceDeclaration name constraints className tys <$> desugarCases ds) <*> desugarRest rest
     desugarRest (ValueDeclaration name nameKind bs result : rest) =
@@ -86,18 +89,18 @@
 inSameGroup d1 (PositionedDeclaration _ _ d2) = inSameGroup d1 d2
 inSameGroup _ _ = False
 
-toDecls :: [Declaration] -> SupplyT (Either ErrorStack) [Declaration]
+toDecls :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration]
 toDecls [ValueDeclaration ident nameKind bs (Right val)] | all isVarBinder bs = do
   let args = map (\(VarBinder arg) -> arg) bs
       body = foldr (Abs . Left) val args
-  guardWith (strMsg "Overlapping function argument names") $ length (nub args) == length args
+  guardWith (errorMessage (OverlappingArgNames (Just ident))) $ length (nub args) == length args
   return [ValueDeclaration ident nameKind [] (Right body)]
 toDecls ds@(ValueDeclaration ident _ bs result : _) = do
   let tuples = map toTuple ds
   unless (all ((== length bs) . length . fst) tuples) $
-      throwError $ mkErrorStack ("Argument list lengths differ in declaration " ++ show ident) Nothing
+      throwError . errorMessage $ ArgListLengthsDiffer ident
   unless (not (null bs) || isLeft result) $
-      throwError $ mkErrorStack ("Duplicate value declaration '" ++ show ident ++ "'") Nothing
+      throwError . errorMessage $ DuplicateValueDeclaration ident
   caseDecl <- makeCaseDeclaration ident tuples
   return [caseDecl]
 toDecls (PositionedDeclaration pos com d : ds) = do
@@ -114,13 +117,34 @@
 toTuple (PositionedDeclaration _ _ d) = toTuple d
 toTuple _ = error "Not a value declaration"
 
-makeCaseDeclaration :: Ident -> [([Binder], Either [(Guard, Expr)] Expr)] -> SupplyT (Either ErrorStack) Declaration
+makeCaseDeclaration :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => Ident -> [([Binder], Either [(Guard, Expr)] Expr)] -> m Declaration
 makeCaseDeclaration ident alternatives = do
-  let argPattern = length . fst . head $ alternatives
-  args <- map Ident <$> replicateM argPattern freshName
+  let namedArgs = map findName . fst <$> alternatives
+  args <- mapM argName $ foldl1 resolveNames namedArgs
   let
     vars = map (Var . Qualified Nothing) args
     binders = [ CaseAlternative bs result | (bs, result) <- alternatives ]
     value = foldr (Abs . Left) (Case vars binders) args
   return $ ValueDeclaration ident Value [] (Right value)
+  where
+  findName :: Binder -> Maybe Ident
+  findName (VarBinder name) = Just name
+  findName (PositionedBinder _ _ binder) = findName binder
+  findName _ = Nothing
 
+  argName :: (MonadSupply m) => Maybe Ident -> m Ident
+  argName (Just name) = return name
+  argName Nothing = do
+    name <- freshName
+    return (Ident name)
+
+  resolveNames :: [Maybe Ident] -> [Maybe Ident] -> [Maybe Ident]
+  resolveNames = zipWith resolveName
+
+  resolveName :: Maybe Ident -> Maybe Ident -> Maybe Ident
+  resolveName (Just a) (Just b)
+    | a == b = Just a
+    | otherwise = Nothing
+  resolveName Nothing Nothing = Nothing
+  resolveName (Just a) Nothing = Just a
+  resolveName Nothing (Just b) = Just b
diff --git a/src/Language/PureScript/Sugar/DoNotation.hs b/src/Language/PureScript/Sugar/DoNotation.hs
--- a/src/Language/PureScript/Sugar/DoNotation.hs
+++ b/src/Language/PureScript/Sugar/DoNotation.hs
@@ -14,6 +14,9 @@
 --
 -----------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
 module Language.PureScript.Sugar.DoNotation (
     desugarDoModule
 ) where
@@ -21,21 +24,21 @@
 import Language.PureScript.Names
 import Language.PureScript.AST
 import Language.PureScript.Errors
-import Language.PureScript.Supply
 
 import qualified Language.PureScript.Constants as C
 
 import Control.Applicative
-import Control.Monad.Trans.Class
+import Control.Monad.Error.Class
+import Control.Monad.Supply.Class
 
 -- |
 -- Replace all @DoNotationBind@ and @DoNotationValue@ constructors with applications of the Prelude.(>>=) function,
 -- and all @DoNotationLet@ constructors with let expressions.
 --
-desugarDoModule :: Module -> SupplyT (Either ErrorStack) Module
-desugarDoModule (Module mn ds exts) = Module mn <$> parU ds desugarDo <*> pure exts
+desugarDoModule :: forall m. (Applicative m, MonadSupply m, MonadError MultipleErrors m) => Module -> m Module
+desugarDoModule (Module coms mn ds exts) = Module coms mn <$> parU ds desugarDo <*> pure exts
 
-desugarDo :: Declaration -> SupplyT (Either ErrorStack) Declaration
+desugarDo :: forall m. (Applicative m, MonadSupply m, MonadError MultipleErrors m) => Declaration -> m Declaration
 desugarDo (PositionedDeclaration pos com d) = PositionedDeclaration pos com <$> (rethrowWithPosition pos $ desugarDo d)
 desugarDo d =
   let (f, _, _) = everywhereOnValuesM return replace return
@@ -47,18 +50,18 @@
   bind :: Expr
   bind = Var (Qualified (Just prelude) (Op (C.>>=)))
 
-  replace :: Expr -> SupplyT (Either ErrorStack) Expr
+  replace :: Expr -> m Expr
   replace (Do els) = go els
   replace (PositionedValue pos com v) = PositionedValue pos com <$> rethrowWithPosition pos (replace v)
   replace other = return other
 
-  go :: [DoNotationElement] -> SupplyT (Either ErrorStack) Expr
+  go :: [DoNotationElement] -> m Expr
   go [] = error "The impossible happened in desugarDo"
   go [DoNotationValue val] = return val
   go (DoNotationValue val : rest) = do
     rest' <- go rest
     return $ App (App bind val) (Abs (Left (Ident C.__unused)) rest')
-  go [DoNotationBind _ _] = lift $ Left $ mkErrorStack "Bind statement cannot be the last statement in a do block" Nothing
+  go [DoNotationBind _ _] = throwError . errorMessage $ InvalidDoBind
   go (DoNotationBind NullBinder val : rest) = go (DoNotationValue val : rest)
   go (DoNotationBind (VarBinder ident) val : rest) = do
     rest' <- go rest
@@ -67,7 +70,7 @@
     rest' <- go rest
     ident <- Ident <$> freshName
     return $ App (App bind val) (Abs (Left ident) (Case [Var (Qualified Nothing ident)] [CaseAlternative [binder] (Right rest')]))
-  go [DoNotationLet _] = lift $ Left $ mkErrorStack "Let statement cannot be the last statement in a do block" Nothing
+  go [DoNotationLet _] = throwError . errorMessage $ InvalidDoLet
   go (DoNotationLet ds : rest) = do
     rest' <- go rest
     return $ Let ds rest'
diff --git a/src/Language/PureScript/Sugar/Names.hs b/src/Language/PureScript/Sugar/Names.hs
--- a/src/Language/PureScript/Sugar/Names.hs
+++ b/src/Language/PureScript/Sugar/Names.hs
@@ -12,6 +12,9 @@
 --
 -----------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
 module Language.PureScript.Sugar.Names (
   desugarImports
 ) where
@@ -84,7 +87,7 @@
 -- Updates the exports for a module from the global environment. If the module was not previously
 -- present in the global environment, it is created.
 --
-updateExportedModule :: ExportEnvironment -> ModuleName -> (Exports -> Either ErrorStack Exports) -> Either ErrorStack ExportEnvironment
+updateExportedModule :: (Applicative m, MonadError MultipleErrors m) => ExportEnvironment -> ModuleName -> (Exports -> m Exports) -> m ExportEnvironment
 updateExportedModule env mn update = do
   let exports = fromMaybe (error "Module was undefined in updateExportedModule") $ mn `M.lookup` env
   exports' <- update exports
@@ -93,61 +96,61 @@
 -- |
 -- Adds an empty module to an ExportEnvironment.
 --
-addEmptyModule :: ExportEnvironment -> ModuleName -> Either ErrorStack ExportEnvironment
+addEmptyModule :: (Applicative m, MonadError MultipleErrors m) => ExportEnvironment -> ModuleName -> m ExportEnvironment
 addEmptyModule env name =
   if name `M.member` env
-    then throwError $ mkErrorStack ("Module '" ++ show name ++ "' has been defined more than once") Nothing
+    then throwError . errorMessage $ RedefinedModule name
     else return $ M.insert name (Exports [] [] []) env
 
 -- |
 -- Adds a type belonging to a module to the export environment.
 --
-addType :: ExportEnvironment -> ModuleName -> ProperName -> [ProperName] -> Either ErrorStack ExportEnvironment
+addType :: (Applicative m, MonadError MultipleErrors m) => ExportEnvironment -> ModuleName -> ProperName -> [ProperName] -> m ExportEnvironment
 addType env mn name dctors = updateExportedModule env mn $ \m -> do
   let exTypes = exportedTypes m
   let exDctors = snd `concatMap` exTypes
   let exClasses = exportedTypeClasses m
-  when (any ((== name) . fst) exTypes) $ throwMultipleDefError "type" name
-  when (name `elem` exClasses) $ throwConflictingDefError "Type" "type class" name
+  when (any ((== name) . fst) exTypes) $ throwConflictError ConflictingTypeDecls name
+  when (name `elem` exClasses) $ throwConflictError TypeConflictsWithClass name
   forM_ dctors $ \dctor -> do
-    when (dctor `elem` exDctors) $ throwMultipleDefError "data constructor" dctor
-    when (dctor `elem` exClasses) $ throwConflictingDefError "Data constructor" "type class" dctor
+    when (dctor `elem` exDctors) $ throwConflictError ConflictingCtorDecls dctor
+    when (dctor `elem` exClasses) $ throwConflictError CtorConflictsWithClass dctor
   return $ m { exportedTypes = (name, dctors) : exTypes }
 
 -- |
 -- Adds a class to the export environment.
 --
-addTypeClass :: ExportEnvironment -> ModuleName -> ProperName -> Either ErrorStack ExportEnvironment
+addTypeClass :: (Applicative m, MonadError MultipleErrors m) => ExportEnvironment -> ModuleName -> ProperName -> m ExportEnvironment
 addTypeClass env mn name = updateExportedModule env mn $ \m -> do
   let exTypes = exportedTypes m
   let exDctors = snd `concatMap` exTypes
-  when (any ((== name) . fst) exTypes) $ throwConflictingDefError "Type class" "type" name
-  when (name `elem` exDctors) $ throwConflictingDefError "Type class" "data constructor" name
-  classes <- addExport "type class" (exportedTypeClasses m) name
+  when (any ((== name) . fst) exTypes) $ throwConflictError ClassConflictsWithType name
+  when (name `elem` exDctors) $ throwConflictError ClassConflictsWithCtor name
+  classes <- addExport DuplicateClassExport (exportedTypeClasses m) name
   return $ m { exportedTypeClasses = classes }
 
 -- |
 -- Adds a class to the export environment.
 --
-addValue :: ExportEnvironment -> ModuleName -> Ident -> Either ErrorStack ExportEnvironment
+addValue :: (Applicative m, MonadError MultipleErrors m) => ExportEnvironment -> ModuleName -> Ident -> m ExportEnvironment
 addValue env mn name = updateExportedModule env mn $ \m -> do
-  values <- addExport "value" (exportedValues m) name
+  values <- addExport DuplicateValueExport (exportedValues m) name
   return $ m { exportedValues = values }
 
 -- |
 -- Adds an entry to a list of exports unless it is already present, in which case an error is
 -- returned.
 --
-addExport :: (Eq a, Show a) => String -> [a] -> a -> Either ErrorStack [a]
+addExport :: (Applicative m, MonadError MultipleErrors m, Eq a, Show a) => (a -> ErrorMessage) -> [a] -> a -> m [a]
 addExport what exports name =
   if name `elem` exports
-  then throwMultipleDefError what name
+  then throwConflictError what name
   else return $ name : exports
 
 -- |
 -- Replaces all local names with qualified names within a set of modules.
 --
-desugarImports :: [Module] -> Either ErrorStack [Module]
+desugarImports :: forall m. (Applicative m, MonadError MultipleErrors m) => [Module] -> m [Module]
 desugarImports modules = do
   unfilteredExports <- findExports modules
   exports <- foldM filterModuleExports unfilteredExports modules
@@ -157,15 +160,15 @@
   -- Filters the exports for a module in the global exports environment so that only explicitly
   -- exported members remain. If the module does not explicitly export anything, everything is
   -- exported.
-  filterModuleExports :: ExportEnvironment -> Module -> Either ErrorStack ExportEnvironment
-  filterModuleExports env (Module mn _ (Just exps)) = filterExports mn exps env
+  filterModuleExports :: ExportEnvironment -> Module -> m ExportEnvironment
+  filterModuleExports env (Module _ mn _ (Just exps)) = filterExports mn exps env
   filterModuleExports env _ = return env
 
   -- Rename and check all the names within a module. We tweak the global exports environment so
   -- the module has access to an unfiltered list of its own members.
-  renameInModule' :: ExportEnvironment -> ExportEnvironment -> Module -> Either ErrorStack Module
-  renameInModule' unfilteredExports exports m@(Module mn _ _) =
-    rethrow (mkCompileError ("Error in module " ++ show mn) Nothing `combineErrors`) $ do
+  renameInModule' :: ExportEnvironment -> ExportEnvironment -> Module -> m Module
+  renameInModule' unfilteredExports exports m@(Module _ mn _ _) =
+    rethrow (onErrorMessages (ErrorInModule mn)) $ do
       let env = M.update (\_ -> M.lookup mn unfilteredExports) mn exports
       let exps = fromMaybe (error "Module is missing in renameInModule'") $ M.lookup mn exports
       imports <- resolveImports env m
@@ -176,7 +179,7 @@
 -- as it will also make all data constructor exports explicit.
 --
 elaborateExports :: Exports -> Module -> Module
-elaborateExports exps (Module mn decls _) = Module mn decls (Just $
+elaborateExports exps (Module coms mn decls _) = Module coms mn decls (Just $
   map (\(ctor, dctors) -> TypeRef ctor (Just dctors)) (exportedTypes exps) ++
   map TypeClassRef (exportedTypeClasses exps) ++
   map ValueRef (exportedValues exps))
@@ -186,7 +189,7 @@
 -- This ensures transitive instances are included when using a member from a module.
 --
 elaborateImports :: Module -> Module
-elaborateImports (Module mn decls exps) = Module mn decls' exps
+elaborateImports (Module coms mn decls exps) = Module coms mn decls' exps
   where
   decls' :: [Declaration]
   decls' =
@@ -196,19 +199,19 @@
   fqValues (Var (Qualified (Just mn') _)) = [mn']
   fqValues _ = []
   mkImport :: ModuleName -> Declaration
-  mkImport mn' = ImportDeclaration mn' (Qualifying []) Nothing
+  mkImport mn' = ImportDeclaration mn' (Explicit []) Nothing
 
 -- |
 -- Replaces all local names with qualified names within a module and checks that all existing
 -- qualified names are valid.
 --
-renameInModule :: ImportEnvironment -> ExportEnvironment -> Module -> Either ErrorStack Module
-renameInModule imports exports (Module mn decls exps) =
-  Module mn <$> parU decls go <*> pure exps
+renameInModule :: forall m. (Applicative m, MonadError MultipleErrors m) => ImportEnvironment -> ExportEnvironment -> Module -> m Module
+renameInModule imports exports (Module coms mn decls exps) =
+  Module coms mn <$> parU decls go <*> pure exps
   where
   (go, _, _, _, _) = everywhereWithContextOnValuesM (Nothing, []) updateDecl updateValue updateBinder updateCase defS
 
-  updateDecl :: (Maybe SourceSpan, [Ident]) -> Declaration -> Either ErrorStack ((Maybe SourceSpan, [Ident]), Declaration)
+  updateDecl :: (Maybe SourceSpan, [Ident]) -> Declaration -> m ((Maybe SourceSpan, [Ident]), Declaration)
   updateDecl (_, bound) d@(PositionedDeclaration pos _ _) = return ((Just pos, bound), d)
   updateDecl (pos, bound) (DataDeclaration dtype name args dctors) =
     (,) (pos, bound) <$> (DataDeclaration dtype name args <$> mapM (sndM (mapM (updateTypesEverywhere pos))) dctors)
@@ -226,14 +229,14 @@
     (,) (pos, name : bound) <$> (ExternDeclaration fit name js <$> updateTypesEverywhere pos ty)
   updateDecl s d = return (s, d)
 
-  updateValue :: (Maybe SourceSpan, [Ident]) -> Expr -> Either ErrorStack ((Maybe SourceSpan, [Ident]), Expr)
+  updateValue :: (Maybe SourceSpan, [Ident]) -> Expr -> m ((Maybe SourceSpan, [Ident]), Expr)
   updateValue (_, bound) v@(PositionedValue pos' _ _) = return ((Just pos', bound), v)
   updateValue (pos, bound) (Abs (Left arg) val') = return ((pos, arg : bound), Abs (Left arg) val')
   updateValue (pos, bound) (Let ds val') = do
       let args = mapMaybe letBoundVariable ds
       unless (length (nub args) == length args) $
-        maybe id rethrowWithPosition pos $ 
-          throwError $ mkErrorStack ("Overlapping names in let binding.") Nothing
+        maybe id rethrowWithPosition pos $
+          throwError . errorMessage $ OverlappingNamesInLet
       return ((pos, args ++ bound), Let ds val')
       where
   updateValue (pos, bound) (Var name'@(Qualified Nothing ident)) | ident `notElem` bound =
@@ -244,12 +247,12 @@
   updateValue s@(pos, _) (TypedValue check val ty) = (,) s <$> (TypedValue check val <$> updateTypesEverywhere pos ty)
   updateValue s v = return (s, v)
 
-  updateBinder :: (Maybe SourceSpan, [Ident]) -> Binder -> Either ErrorStack ((Maybe SourceSpan, [Ident]), Binder)
+  updateBinder :: (Maybe SourceSpan, [Ident]) -> Binder -> m ((Maybe SourceSpan, [Ident]), Binder)
   updateBinder (_, bound) v@(PositionedBinder pos _ _) = return ((Just pos, bound), v)
   updateBinder s@(pos, _) (ConstructorBinder name b) = (,) s <$> (ConstructorBinder <$> updateDataConstructorName name pos <*> pure b)
   updateBinder s v = return (s, v)
 
-  updateCase :: (Maybe SourceSpan, [Ident]) -> CaseAlternative -> Either ErrorStack ((Maybe SourceSpan, [Ident]), CaseAlternative)
+  updateCase :: (Maybe SourceSpan, [Ident]) -> CaseAlternative -> m ((Maybe SourceSpan, [Ident]), CaseAlternative)
   updateCase (pos, bound) c@(CaseAlternative bs _) = return ((pos, concatMap binderNames bs ++ bound), c)
 
   letBoundVariable :: Declaration -> Maybe Ident
@@ -257,10 +260,10 @@
   letBoundVariable (PositionedDeclaration _ _ d) = letBoundVariable d
   letBoundVariable _ = Nothing
 
-  updateTypesEverywhere :: Maybe SourceSpan -> Type -> Either ErrorStack Type
+  updateTypesEverywhere :: Maybe SourceSpan -> Type -> m Type
   updateTypesEverywhere pos0 = everywhereOnTypesM (updateType pos0)
     where
-    updateType :: Maybe SourceSpan -> Type -> Either ErrorStack Type
+    updateType :: Maybe SourceSpan -> Type -> m Type
     updateType pos (TypeConstructor name) = TypeConstructor <$> updateTypeName name pos
     updateType pos (SaturatedTypeSynonym name tys) = SaturatedTypeSynonym <$> updateTypeName name pos <*> pure tys
     updateType pos (ConstrainedType cs t) = ConstrainedType <$> updateConstraints pos cs <*> pure t
@@ -268,28 +271,28 @@
 
   updateConstraints pos = mapM (\(name, ts) -> (,) <$> updateClassName name pos <*> mapM (updateTypesEverywhere pos) ts)
 
-  updateTypeName = update "type" importedTypes (\mes -> isJust . (`lookup` exportedTypes mes))
-  updateClassName = update "type class" importedTypeClasses (flip elem . exportedTypeClasses)
-  updateValueName = update "value" importedValues (flip elem . exportedValues)
-  updateDataConstructorName = update "data constructor" importedDataConstructors (\mes -> flip elem (join $ snd `map` exportedTypes mes))
+  updateTypeName = update UnknownType importedTypes (\mes -> isJust . (`lookup` exportedTypes mes))
+  updateClassName = update UnknownTypeClass importedTypeClasses (flip elem . exportedTypeClasses)
+  updateValueName = update UnknownValue importedValues (flip elem . exportedValues)
+  updateDataConstructorName = update (flip UnknownDataConstructor Nothing) importedDataConstructors (\mes -> flip elem (join $ snd `map` exportedTypes mes))
 
   -- Update names so unqualified references become qualified, and locally qualified references
   -- are replaced with their canoncial qualified names (e.g. M.Map -> Data.Map.Map)
-  update :: (Ord a, Show a) => String
+  update :: (Ord a, Show a) => (Qualified a -> ErrorMessage)
                             -> (ImportEnvironment -> M.Map (Qualified a) (Qualified a))
                             -> (Exports -> a -> Bool)
                             -> Qualified a
                             -> Maybe SourceSpan
-                            -> Either ErrorStack (Qualified a)
-  update t getI checkE qname@(Qualified mn' name) pos = positioned $ case (M.lookup qname imports', mn') of
+                            -> m (Qualified a)
+  update unknown getI checkE qname@(Qualified mn' name) pos = positioned $ case (M.lookup qname imports', mn') of
     (Just qname', _) -> return qname'
     (Nothing, Just mn'') -> do
-      when (isExplicitQualModule mn'') $ throwError $ mkErrorStack ("Unknown " ++ t ++ " '" ++ show qname ++ "'") Nothing
+      when (isExplicitQualModule mn'') . throwError . errorMessage $ unknown qname
       modExports <- getExports mn''
       if checkE modExports name
         then return qname
-        else throwError $ mkErrorStack ("Unknown " ++ t ++ " '" ++ show qname ++ "'") Nothing
-    _ -> throwError $ mkErrorStack ("Unknown " ++ t ++ " '" ++ show name ++ "'") Nothing
+        else throwError . errorMessage $ unknown qname
+    _ -> throwError . errorMessage $ unknown qname
     where
     isExplicitQualModule :: ModuleName -> Bool
     isExplicitQualModule = flip elem $ mapMaybe (\(Qualified q _) -> q) (M.keys imports')
@@ -299,13 +302,13 @@
       Just pos' -> rethrowWithPosition pos' err
 
   -- Gets the exports for a module, or an error message if the module doesn't exist
-  getExports :: ModuleName -> Either ErrorStack Exports
-  getExports mn' = maybe (throwError $ mkErrorStack ("Unknown module '" ++ show mn' ++ "'") Nothing) return $ M.lookup mn' exports
+  getExports :: ModuleName -> m Exports
+  getExports mn' = maybe (throwError . errorMessage $ UnknownModule mn') return $ M.lookup mn' exports
 
 -- |
 -- Finds all exported declarations in a set of modules.
 --
-findExports :: [Module] -> Either ErrorStack ExportEnvironment
+findExports :: forall m. (Applicative m, MonadError MultipleErrors m) => [Module] -> m ExportEnvironment
 findExports = foldM addModule $ M.singleton (ModuleName [ProperName C.prim]) primExports
   where
 
@@ -315,13 +318,13 @@
     mkTypeEntry (Qualified _ name) = (name, [])
 
   -- Add all of the exported declarations from a module to the global export environment
-  addModule :: ExportEnvironment -> Module -> Either ErrorStack ExportEnvironment
-  addModule env (Module mn ds _) = do
+  addModule :: ExportEnvironment -> Module -> m ExportEnvironment
+  addModule env (Module _ mn ds _) = do
     env' <- addEmptyModule env mn
-    rethrow (mkCompileError ("Error in module " ++ show mn) Nothing `combineErrors`) $ foldM (addDecl mn) env' ds
+    rethrow (onErrorMessages (ErrorInModule mn)) $ foldM (addDecl mn) env' ds
 
   -- Add a declaration from a module to the global export environment
-  addDecl :: ModuleName -> ExportEnvironment -> Declaration -> Either ErrorStack ExportEnvironment
+  addDecl :: ModuleName -> ExportEnvironment -> Declaration -> m ExportEnvironment
   addDecl mn env (TypeClassDeclaration tcn _ _ ds) = do
     env' <- addTypeClass env mn tcn
     foldM go env' ds
@@ -341,15 +344,15 @@
 -- Filters the exports for a module to ensure only explicit exports are kept in the global exports
 -- environment.
 --
-filterExports :: ModuleName -> [DeclarationRef] -> ExportEnvironment -> Either ErrorStack ExportEnvironment
+filterExports :: forall m. (Applicative m, MonadError MultipleErrors m) => ModuleName -> [DeclarationRef] -> ExportEnvironment -> m ExportEnvironment
 filterExports mn exps env = do
   let moduleExports = fromMaybe (error "Module is missing") (mn `M.lookup` env)
-  moduleExports' <- rethrow (mkCompileError ("Error in module " ++ show mn) Nothing `combineErrors`) $ filterModule moduleExports
+  moduleExports' <- rethrow (onErrorMessages (ErrorInModule mn)) $ filterModule moduleExports
   return $ M.insert mn moduleExports' env
   where
 
   -- Filter the exports for the specific module
-  filterModule :: Exports -> Either ErrorStack Exports
+  filterModule :: Exports -> m Exports
   filterModule exported = do
     types' <- foldM (filterTypes $ exportedTypes exported) [] exps
     values <- foldM (filterValues $ exportedValues exported) [] exps
@@ -358,37 +361,37 @@
 
   -- Ensure the exported types and data constructors exist in the module and add them to the set of
   -- exports
-  filterTypes :: [(ProperName, [ProperName])] -> [(ProperName, [ProperName])] -> DeclarationRef -> Either ErrorStack [(ProperName, [ProperName])]
+  filterTypes :: [(ProperName, [ProperName])] -> [(ProperName, [ProperName])] -> DeclarationRef -> m [(ProperName, [ProperName])]
   filterTypes expTys result (PositionedDeclarationRef pos _ r) = rethrowWithPosition pos $ filterTypes expTys result r
   filterTypes expTys result (TypeRef name expDcons) = do
-    dcons <- maybe (throwError $ mkErrorStack ("Cannot export undefined type '" ++ show name ++ "'") Nothing) return $ name `lookup` expTys
+    dcons <- maybe (throwError . errorMessage . UnknownType $ Qualified (Just mn) name) return $ name `lookup` expTys
     dcons' <- maybe (return dcons) (foldM (filterDcons name dcons) []) expDcons
     return $ (name, dcons') : result
   filterTypes _ result _ = return result
 
   -- Ensure the exported data constructors exists for a type and add them to the list of exports
-  filterDcons :: ProperName -> [ProperName] -> [ProperName] -> ProperName -> Either ErrorStack [ProperName]
+  filterDcons :: ProperName -> [ProperName] -> [ProperName] -> ProperName -> m [ProperName]
   filterDcons tcon exps' result name =
     if name `elem` exps'
     then return $ name : result
-    else throwError $ mkErrorStack ("Cannot export undefined data constructor '" ++ show name ++ "' for type '" ++ show tcon ++ "'") Nothing
+    else throwError . errorMessage $ UnknownDataConstructor (Qualified (Just mn) name) (Just (Qualified (Just mn) tcon))
 
   -- Ensure the exported classes exist in the module and add them to the set of exports
-  filterClasses :: [ProperName] -> [ProperName] -> DeclarationRef -> Either ErrorStack [ProperName]
+  filterClasses :: [ProperName] -> [ProperName] -> DeclarationRef -> m [ProperName]
   filterClasses exps' result (PositionedDeclarationRef pos _ r) = rethrowWithPosition pos $ filterClasses exps' result r
   filterClasses exps' result (TypeClassRef name) =
     if name `elem` exps'
     then return $ name : result
-    else throwError $ mkErrorStack ("Cannot export undefined type class '" ++ show name ++ "'") Nothing
+    else throwError . errorMessage . UnknownTypeClass $ Qualified (Just mn) name
   filterClasses _ result _ = return result
 
   -- Ensure the exported values exist in the module and add them to the set of exports
-  filterValues :: [Ident] -> [Ident] -> DeclarationRef -> Either ErrorStack [Ident]
+  filterValues :: [Ident] -> [Ident] -> DeclarationRef -> m [Ident]
   filterValues exps' result (PositionedDeclarationRef pos _ r) = rethrowWithPosition pos $ filterValues exps' result r
   filterValues exps' result (ValueRef name) =
     if name `elem` exps'
     then return $ name : result
-    else throwError $ mkErrorStack ("Cannot export undefined value '" ++ show name ++ "'") Nothing
+    else throwError . errorMessage . UnknownValue $ Qualified (Just mn) name
   filterValues _ result _ = return result
 
 -- |
@@ -405,8 +408,8 @@
 -- |
 -- Constructs a local environment for a module.
 --
-resolveImports :: ExportEnvironment -> Module -> Either ErrorStack ImportEnvironment
-resolveImports env (Module currentModule decls _) =
+resolveImports :: forall m. (Applicative m, MonadError MultipleErrors m) => ExportEnvironment -> Module -> m ImportEnvironment
+resolveImports env (Module _ currentModule decls _) =
   foldM resolveImport' (ImportEnvironment M.empty M.empty M.empty M.empty) (M.toList scope)
   where
 
@@ -414,11 +417,11 @@
   -- module (where Nothing indicates everything is to be imported), and optionally a qualified name
   -- for the module
   scope :: M.Map ModuleName (Maybe SourceSpan, ImportDeclarationType, Maybe ModuleName)
-  scope = M.insert currentModule (Nothing, Unqualified, Nothing) (findImports decls)
+  scope = M.insert currentModule (Nothing, Implicit, Nothing) (findImports decls)
 
-  resolveImport' :: ImportEnvironment -> (ModuleName, (Maybe SourceSpan, ImportDeclarationType, Maybe ModuleName)) -> Either ErrorStack ImportEnvironment
+  resolveImport' :: ImportEnvironment -> (ModuleName, (Maybe SourceSpan, ImportDeclarationType, Maybe ModuleName)) -> m ImportEnvironment
   resolveImport' imp (mn, (pos, typ, impQual)) = do
-    modExports <- positioned $ maybe (throwError $ mkErrorStack ("Cannot import unknown module '" ++ show mn ++ "'") Nothing) return $ mn `M.lookup` env
+    modExports <- positioned $ maybe (throwError . errorMessage $ UnknownModule mn) return $ mn `M.lookup` env
     positioned $ resolveImport currentModule mn modExports imp impQual typ
     where
     positioned err = case pos of
@@ -428,19 +431,19 @@
 -- |
 -- Extends the local environment for a module by resolving an import of another module.
 --
-resolveImport :: ModuleName -> ModuleName -> Exports -> ImportEnvironment -> Maybe ModuleName -> ImportDeclarationType -> Either ErrorStack ImportEnvironment
+resolveImport :: forall m. (Applicative m, MonadError MultipleErrors m) => ModuleName -> ModuleName -> Exports -> ImportEnvironment -> Maybe ModuleName -> ImportDeclarationType -> m ImportEnvironment
 resolveImport currentModule importModule exps imps impQual =
   resolveByType
   where
 
-  resolveByType :: ImportDeclarationType -> Either ErrorStack ImportEnvironment
-  resolveByType Unqualified = importAll importExplicit
-  resolveByType (Qualifying explImports) = (checkedRefs >=> foldM importExplicit imps) explImports
+  resolveByType :: ImportDeclarationType -> m ImportEnvironment
+  resolveByType Implicit = importAll importExplicit
+  resolveByType (Explicit explImports) = (checkedRefs >=> foldM importExplicit imps) explImports
   resolveByType (Hiding hiddenImports) = do
     hiddenImports' <- checkedRefs hiddenImports
     importAll (importNonHidden hiddenImports')
 
-  importNonHidden :: [DeclarationRef] -> ImportEnvironment -> DeclarationRef -> Either ErrorStack ImportEnvironment
+  importNonHidden :: [DeclarationRef] -> ImportEnvironment -> DeclarationRef -> m ImportEnvironment
   importNonHidden hidden m ref =
     if isHidden hidden ref
     then return m
@@ -459,14 +462,14 @@
   isHidden hidden ref = ref `elem` hidden
 
   -- Import all symbols
-  importAll :: (ImportEnvironment -> DeclarationRef -> Either ErrorStack ImportEnvironment) -> Either ErrorStack ImportEnvironment
+  importAll :: (ImportEnvironment -> DeclarationRef -> m ImportEnvironment) -> m ImportEnvironment
   importAll importer = do
     imp' <- foldM (\m (name, dctors) -> importer m (TypeRef name (Just dctors))) imps (exportedTypes exps)
     imp'' <- foldM (\m name -> importer m (ValueRef name)) imp' (exportedValues exps)
     foldM (\m name -> importer m (TypeClassRef name)) imp'' (exportedTypeClasses exps)
 
   -- Import something explicitly
-  importExplicit :: ImportEnvironment -> DeclarationRef -> Either ErrorStack ImportEnvironment
+  importExplicit :: ImportEnvironment -> DeclarationRef -> m ImportEnvironment
   importExplicit imp (PositionedDeclarationRef pos _ r) = rethrowWithPosition pos $ importExplicit imp r
   importExplicit imp (ValueRef name) = do
     values' <- updateImports (importedValues imp) name
@@ -483,20 +486,20 @@
   importExplicit _ _ = error "Invalid argument to importExplicit"
 
   -- Check if DeclarationRef points to an existent symbol
-  checkedRefs :: [DeclarationRef] -> Either ErrorStack [DeclarationRef]
+  checkedRefs :: [DeclarationRef] -> m [DeclarationRef]
   checkedRefs = mapM check
     where
     check (PositionedDeclarationRef pos _ r) =
       rethrowWithPosition pos $ check r
     check ref@(ValueRef name) =
-      checkImportExists "value" values name >> return ref
+      checkImportExists UnknownValue values name >> return ref
     check ref@(TypeRef name dctors) = do
-      _ <- checkImportExists "type" availableTypes name
+      _ <- checkImportExists UnknownType availableTypes name
       let allDctors = allExportedDataConstructors name
       _ <- maybe (return allDctors) (mapM $ checkDctorExists allDctors) dctors
       return ref
     check ref@(TypeClassRef name) =
-      checkImportExists "type class" classes name >> return ref
+      checkImportExists UnknownTypeClass classes name >> return ref
     check _ = error "Invalid argument to checkRefIsValid"
 
   -- Find all exported data constructors for a given type
@@ -504,15 +507,15 @@
   allExportedDataConstructors name = fromMaybe [] $ name `lookup` exportedTypes exps
 
   -- Add something to the ImportEnvironment if it does not already exist there
-  updateImports :: (Ord a, Show a) => M.Map (Qualified a) (Qualified a) -> a -> Either ErrorStack (M.Map (Qualified a) (Qualified a))
+  updateImports :: (Ord a, Show a) => M.Map (Qualified a) (Qualified a) -> a -> m (M.Map (Qualified a) (Qualified a))
   updateImports m name = case M.lookup (Qualified impQual name) m of
     Nothing -> return $ M.insert (Qualified impQual name) (Qualified (Just importModule) name) m
     Just (Qualified Nothing _) -> error "Invalid state in updateImports"
-    Just x@(Qualified (Just mn) _) -> throwError $ mkErrorStack err Nothing
+    Just (Qualified (Just mn) _) -> throwError . errorMessage $ err
       where
       err = if mn == currentModule || importModule == currentModule
-            then "Definition '" ++ show name ++ "' conflicts with import '" ++ show (Qualified (Just mn) name) ++ "'"
-            else "Conflicting imports for '" ++ show name ++ "': '" ++ show x ++ "', '" ++ show (Qualified (Just importModule) name) ++ "'"
+            then ConflictingImport (show name) mn
+            else ConflictingImports (show name) mn importModule
 
   -- The available values, types, and classes in the module being imported
   values = exportedValues exps
@@ -521,28 +524,18 @@
 
   -- Ensure that an explicitly imported data constructor exists for the type it is being imported
   -- from
-  checkDctorExists :: [ProperName] -> ProperName -> Either ErrorStack ProperName
-  checkDctorExists = checkImportExists "data constructor"
+  checkDctorExists :: [ProperName] -> ProperName -> m ProperName
+  checkDctorExists = checkImportExists (flip UnknownDataConstructor Nothing)
 
   -- Check that an explicitly imported item exists in the module it is being imported from
-  checkImportExists :: (Eq a, Show a) => String -> [a] -> a -> Either ErrorStack a
-  checkImportExists t exports item =
+  checkImportExists :: (Eq a, Show a) => (Qualified a -> ErrorMessage) -> [a] -> a -> m a
+  checkImportExists unknown exports item =
       if item `elem` exports
       then return item
-      else throwError $ mkErrorStack ("Cannot import unknown " ++ t ++  " '" ++ show item ++ "' from '" ++ show importModule ++ "'") Nothing
+      else throwError . errorMessage . unknown $ Qualified (Just importModule) item
 
 -- |
 -- Raises an error for when there is more than one definition for something.
 --
-throwMultipleDefError :: (Show a) => String -> a -> Either ErrorStack b
-throwMultipleDefError what name = throwError $
-  mkErrorStack ("Multiple definitions for " ++ what ++ " '" ++ show name ++ "'") Nothing
-
--- |
--- Raises an error for when there is a conflicting definition for something, for example, a type
--- class and data constructor of the same name.
---
-throwConflictingDefError :: (Show a) => String -> String -> a -> Either ErrorStack b
-throwConflictingDefError what1 what2 name = throwError $
-  mkErrorStack (what1 ++ " '" ++ show name ++ "' cannot be defined in the same module as a " ++ what2 ++ " of the same name") Nothing
-
+throwConflictError :: (Applicative m, MonadError MultipleErrors m, Show a) => (a -> ErrorMessage) -> a -> m b
+throwConflictError conflict = throwError . errorMessage . conflict
diff --git a/src/Language/PureScript/Sugar/ObjectWildcards.hs b/src/Language/PureScript/Sugar/ObjectWildcards.hs
--- a/src/Language/PureScript/Sugar/ObjectWildcards.hs
+++ b/src/Language/PureScript/Sugar/ObjectWildcards.hs
@@ -12,12 +12,17 @@
 --
 -----------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
 module Language.PureScript.Sugar.ObjectWildcards (
   desugarObjectConstructors
 ) where
 
 import Control.Applicative
 import Control.Arrow (second)
+import Control.Monad.Error.Class
+import Control.Monad.Supply.Class
 
 import Data.List (partition)
 import Data.Maybe (isJust, fromJust, catMaybes)
@@ -25,16 +30,15 @@
 import Language.PureScript.AST
 import Language.PureScript.Errors
 import Language.PureScript.Names
-import Language.PureScript.Supply
 
-desugarObjectConstructors :: Module -> SupplyT (Either ErrorStack) Module
-desugarObjectConstructors (Module mn ds exts) = Module mn <$> mapM desugarDecl ds <*> pure exts
+desugarObjectConstructors :: forall m. (Applicative m, MonadSupply m, MonadError MultipleErrors m) => Module -> m Module
+desugarObjectConstructors (Module coms mn ds exts) = Module coms mn <$> mapM desugarDecl ds <*> pure exts
   where
 
-  desugarDecl :: Declaration -> SupplyT (Either ErrorStack) Declaration
+  desugarDecl :: Declaration -> m Declaration
   (desugarDecl, _, _) = everywhereOnValuesM return desugarExpr return
 
-  desugarExpr :: Expr -> SupplyT (Either ErrorStack) Expr
+  desugarExpr :: Expr -> m Expr
   desugarExpr (ObjectConstructor ps) = wrapLambda ObjectLiteral ps
   desugarExpr (ObjectUpdater (Just obj) ps) = wrapLambda (ObjectUpdate obj) ps
   desugarExpr (ObjectUpdater Nothing ps) = do
@@ -45,7 +49,7 @@
     return $ Abs (Left arg) (Accessor prop (Var (Qualified Nothing arg)))
   desugarExpr e = return e
 
-  wrapLambda :: ([(String, Expr)] -> Expr) -> [(String, Maybe Expr)] -> SupplyT (Either ErrorStack) Expr
+  wrapLambda :: ([(String, Expr)] -> Expr) -> [(String, Maybe Expr)] -> m Expr
   wrapLambda mkVal ps =
     let (props, args) = partition (isJust . snd) ps
     in if null args
@@ -54,7 +58,7 @@
         (args', ps') <- unzip <$> mapM mkProp ps
         return $ foldr (Abs . Left) (mkVal ps') (catMaybes args')
 
-  mkProp :: (String, Maybe Expr) -> SupplyT (Either ErrorStack) (Maybe Ident, (String, Expr))
+  mkProp :: (String, Maybe Expr) -> m (Maybe Ident, (String, Expr))
   mkProp (name, Just e) = return (Nothing, (name, e))
   mkProp (name, Nothing) = do
     arg <- Ident <$> freshName
diff --git a/src/Language/PureScript/Sugar/Operators.hs b/src/Language/PureScript/Sugar/Operators.hs
--- a/src/Language/PureScript/Sugar/Operators.hs
+++ b/src/Language/PureScript/Sugar/Operators.hs
@@ -17,7 +17,9 @@
 --
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE Rank2Types, FlexibleContexts #-}
+{-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
 module Language.PureScript.Sugar.Operators (
   rebracket,
@@ -28,11 +30,11 @@
 import Language.PureScript.AST
 import Language.PureScript.Errors
 import Language.PureScript.Names
-import Language.PureScript.Supply
 
 import Control.Applicative
 import Control.Monad.State
 import Control.Monad.Except
+import Control.Monad.Supply.Class
 
 import Data.Function (on)
 import Data.Functor.Identity
@@ -47,7 +49,7 @@
 -- |
 -- Remove explicit parentheses and reorder binary operator applications
 --
-rebracket :: [Module] -> Either ErrorStack [Module]
+rebracket :: (Applicative m, MonadError MultipleErrors m) => [Module] -> m [Module]
 rebracket ms = do
   let fixities = concatMap collectFixities ms
   ensureNoDuplicates $ map (\(i, pos, _) -> (i, pos)) fixities
@@ -55,17 +57,17 @@
   mapM (rebracketModule opTable) ms
 
 removeSignedLiterals :: Module -> Module
-removeSignedLiterals (Module mn ds exts) = Module mn (map f' ds) exts
+removeSignedLiterals (Module coms mn ds exts) = Module coms mn (map f' ds) exts
   where
   (f', _, _) = everywhereOnValues id go id
 
   go (UnaryMinus val) = App (Var (Qualified (Just (ModuleName [ProperName C.prelude])) (Ident C.negate))) val
   go other = other
 
-rebracketModule :: [[(Qualified Ident, Expr -> Expr -> Expr, Associativity)]] -> Module -> Either ErrorStack Module
-rebracketModule opTable (Module mn ds exts) =
+rebracketModule :: (Applicative m, MonadError MultipleErrors m) => [[(Qualified Ident, Expr -> Expr -> Expr, Associativity)]] -> Module -> m Module
+rebracketModule opTable (Module coms mn ds exts) =
   let (f, _, _) = everywhereOnValuesTopDownM return (matchOperators opTable) return
-  in Module mn <$> (map removeParens <$> parU ds f) <*> pure exts
+  in Module coms mn <$> (map removeParens <$> parU ds f) <*> pure exts
 
 removeParens :: Declaration -> Declaration
 removeParens =
@@ -76,22 +78,22 @@
   go val = val
 
 collectFixities :: Module -> [(Qualified Ident, SourceSpan, Fixity)]
-collectFixities (Module moduleName ds _) = concatMap collect ds
+collectFixities (Module _ moduleName ds _) = concatMap collect ds
   where
   collect :: Declaration -> [(Qualified Ident, SourceSpan, Fixity)]
   collect (PositionedDeclaration pos _ (FixityDeclaration fixity name)) = [(Qualified (Just moduleName) (Op name), pos, fixity)]
   collect FixityDeclaration{} = error "Fixity without srcpos info"
   collect _ = []
 
-ensureNoDuplicates :: [(Qualified Ident, SourceSpan)] -> Either ErrorStack ()
+ensureNoDuplicates :: (MonadError MultipleErrors m) => [(Qualified Ident, SourceSpan)] -> m ()
 ensureNoDuplicates m = go $ sortBy (compare `on` fst) m
   where
   go [] = return ()
   go [_] = return ()
   go ((x@(Qualified (Just mn) name), _) : (y, pos) : _) | x == y =
-    rethrow (mkCompileError ("Error in module " ++ show mn) Nothing `combineErrors`) $
+    rethrow (onErrorMessages (ErrorInModule mn)) $
       rethrowWithPosition pos $
-        throwError $ mkErrorStack ("Redefined fixity for " ++ show name) Nothing
+        throwError . errorMessage $ MultipleFixities name
   go (_ : rest) = go rest
 
 customOperatorTable :: [(Qualified Ident, Fixity)] -> [[(Qualified Ident, Expr -> Expr -> Expr, Associativity)]]
@@ -106,17 +108,17 @@
 
 type Chain = [Either Expr Expr]
 
-matchOperators :: [[(Qualified Ident, Expr -> Expr -> Expr, Associativity)]] -> Expr -> Either ErrorStack Expr
+matchOperators :: forall m. (MonadError MultipleErrors m) => [[(Qualified Ident, Expr -> Expr -> Expr, Associativity)]] -> Expr -> m Expr
 matchOperators ops = parseChains
   where
-  parseChains :: Expr -> Either ErrorStack Expr
+  parseChains :: Expr -> m Expr
   parseChains b@BinaryNoParens{} = bracketChain (extendChain b)
   parseChains other = return other
   extendChain :: Expr -> Chain
   extendChain (BinaryNoParens op l r) = Left l : Right op : extendChain r
   extendChain other = [Left other]
-  bracketChain :: Chain -> Either ErrorStack Expr
-  bracketChain = either (Left . (`mkErrorStack` Nothing) . show) Right . P.parse (P.buildExpressionParser opTable parseValue <* P.eof) "operator expression"
+  bracketChain :: Chain -> m Expr
+  bracketChain = either (const . throwError . errorMessage $ CannotReorderOperators) return . P.parse (P.buildExpressionParser opTable parseValue <* P.eof) "operator expression"
   opTable = [P.Infix (P.try (parseTicks >>= \op -> return (\t1 t2 -> App (App op t1) t2))) P.AssocLeft]
             : map (map (\(name, f, a) -> P.Infix (P.try (matchOp name) >> return f) (toAssoc a))) ops
             ++ [[ P.Infix (P.try (parseOp >>= \ident -> return (\t1 t2 -> App (App (Var ident) t1) t2))) P.AssocLeft ]]
@@ -149,14 +151,14 @@
   ident <- parseOp
   guard $ ident == op
 
-desugarOperatorSections :: Module -> SupplyT (Either ErrorStack) Module
-desugarOperatorSections (Module mn ds exts) = Module mn <$> mapM goDecl ds <*> pure exts
+desugarOperatorSections :: forall m. (Applicative m, MonadSupply m, MonadError MultipleErrors m) => Module -> m Module
+desugarOperatorSections (Module coms mn ds exts) = Module coms mn <$> mapM goDecl ds <*> pure exts
   where
 
-  goDecl :: Declaration -> SupplyT (Either ErrorStack) Declaration
+  goDecl :: Declaration -> m Declaration
   (goDecl, _, _) = everywhereOnValuesM return goExpr return
 
-  goExpr :: Expr -> SupplyT (Either ErrorStack) Expr
+  goExpr :: Expr -> m Expr
   goExpr (OperatorSection op (Left val)) = return $ App op val
   goExpr (OperatorSection op (Right val)) = do
     arg <- Ident <$> freshName
diff --git a/src/Language/PureScript/Sugar/TypeClasses.hs b/src/Language/PureScript/Sugar/TypeClasses.hs
--- a/src/Language/PureScript/Sugar/TypeClasses.hs
+++ b/src/Language/PureScript/Sugar/TypeClasses.hs
@@ -14,6 +14,8 @@
 --
 -----------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleContexts #-}
+
 module Language.PureScript.Sugar.TypeClasses
   ( desugarTypeClasses
   , typeClassMemberName
@@ -25,9 +27,8 @@
 import Language.PureScript.Errors
 import Language.PureScript.Kinds
 import Language.PureScript.Names
-import Language.PureScript.Pretty.Types (prettyPrintTypeAtom)
 import Language.PureScript.Sugar.CaseDeclarations
-import Language.PureScript.Supply
+import Control.Monad.Supply.Class
 import Language.PureScript.Types
 
 import qualified Language.PureScript.Constants as C
@@ -36,26 +37,32 @@
 import Control.Arrow (first, second)
 import Control.Monad.Except
 import Control.Monad.State
-import Data.List ((\\), find)
+import Data.List ((\\), find, sortBy)
 import Data.Maybe (catMaybes, mapMaybe, isJust)
 
 import qualified Data.Map as M
 
 type MemberMap = M.Map (ModuleName, ProperName) Declaration
 
-type Desugar = StateT MemberMap (SupplyT (Either ErrorStack))
+type Desugar = StateT MemberMap
 
 -- |
 -- Add type synonym declarations for type class dictionary types, and value declarations for type class
 -- instance dictionary expressions.
 --
-desugarTypeClasses :: [Module] -> SupplyT (Either ErrorStack) [Module]
+desugarTypeClasses :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => [Module] -> m [Module]
 desugarTypeClasses = flip evalStateT M.empty . mapM desugarModule
 
-desugarModule :: Module -> Desugar Module
-desugarModule (Module name decls (Just exps)) = do
-  (newExpss, declss) <- unzip <$> parU decls (desugarDecl name exps)
-  return $ Module name (concat declss) $ Just (exps ++ catMaybes newExpss)
+desugarModule :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => Module -> Desugar m Module
+desugarModule (Module coms name decls (Just exps)) = do
+  (newExpss, declss) <- unzip <$> parU (sortBy classesFirst decls) (desugarDecl name exps)
+  return $ Module coms name (concat declss) $ Just (exps ++ catMaybes newExpss)
+  where
+  classesFirst :: Declaration -> Declaration -> Ordering
+  classesFirst d1 d2 
+    | isTypeClassDeclaration d1 && not (isTypeClassDeclaration d2) = LT
+    | not (isTypeClassDeclaration d1) && isTypeClassDeclaration d2 = GT
+    | otherwise = EQ
 desugarModule _ = error "Exports should have been elaborated in name desugaring"
 
 {- Desugar type class and type class instance declarations
@@ -152,7 +159,7 @@
 --       return new Sub(fooString, "");
 --   };
 -}
-desugarDecl :: ModuleName -> [DeclarationRef] -> Declaration -> Desugar (Maybe DeclarationRef, [Declaration])
+desugarDecl :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => ModuleName -> [DeclarationRef] -> Declaration -> Desugar m (Maybe DeclarationRef, [Declaration])
 desugarDecl mn exps = go
   where
   go d@(TypeClassDeclaration name args implies members) = do
@@ -160,7 +167,7 @@
     return (Nothing, d : typeClassDictionaryDeclaration name args implies members : map (typeClassMemberToDictionaryAccessor mn name args) members)
   go d@(ExternInstanceDeclaration name _ className tys) = return (expRef name className tys, [d])
   go d@(TypeInstanceDeclaration name deps className tys members) = do
-    desugared <- lift $ desugarCases members
+    desugared <- desugarCases members
     dictDecl <- typeInstanceDictionaryDeclaration name mn deps className tys desugared
     return (expRef name className tys, [d, dictDecl])
   go (PositionedDeclaration pos com d) = do
@@ -222,18 +229,18 @@
 unit :: Type
 unit = TypeApp tyObject REmpty
 
-typeInstanceDictionaryDeclaration :: Ident -> ModuleName -> [Constraint] -> Qualified ProperName -> [Type] -> [Declaration] -> Desugar Declaration
+typeInstanceDictionaryDeclaration :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => Ident -> ModuleName -> [Constraint] -> Qualified ProperName -> [Type] -> [Declaration] -> Desugar m Declaration
 typeInstanceDictionaryDeclaration name mn deps className tys decls =
-  rethrow (mkCompileError ("Error in type class instance " ++ show className ++ " " ++ unwords (map prettyPrintTypeAtom tys) ++ ":") Nothing `combineErrors`) $ do
+  rethrow (onErrorMessages (ErrorInInstance className tys)) $ do
   m <- get
 
   -- Lookup the type arguments and member types for the type class
-  (TypeClassDeclaration _ args implies tyDecls) <- lift . lift $
-    maybe (Left $ mkErrorStack ("Type class " ++ show className ++ " is undefined") Nothing) Right $
+  (TypeClassDeclaration _ args implies tyDecls) <-
+    maybe (throwError . errorMessage $ UnknownTypeClass className) return $
       M.lookup (qualify mn className) m
 
   case mapMaybe declName tyDecls \\ mapMaybe declName decls of
-    x : _ -> throwError $ mkErrorStack ("Member '" ++ show x ++ "' has not been implemented") Nothing
+    member : _ -> throwError . errorMessage $ MissingClassMember member
     [] -> do
 
       let instanceTys = map memberToNameAndType tyDecls
@@ -268,9 +275,9 @@
   declName (TypeDeclaration ident _) = Just ident
   declName _ = Nothing
 
-  memberToValue :: [(Ident, Type)] -> Declaration -> Desugar Expr
+  memberToValue :: (Functor m, Applicative m, MonadSupply m, MonadError MultipleErrors m) => [(Ident, Type)] -> Declaration -> Desugar m Expr
   memberToValue tys' (ValueDeclaration ident _ [] (Right val)) = do
-    _ <- lift . lift . maybe (Left $ mkErrorStack ("Type class does not define member '" ++ show ident ++ "'") Nothing) Right $ lookup ident tys'
+    _ <- maybe (throwError . errorMessage $ MissingClassMember ident) return $ lookup ident tys'
     return val
   memberToValue tys' (PositionedDeclaration pos com d) = rethrowWithPosition pos $ do
     val <- memberToValue tys' d
diff --git a/src/Language/PureScript/Sugar/TypeDeclarations.hs b/src/Language/PureScript/Sugar/TypeDeclarations.hs
--- a/src/Language/PureScript/Sugar/TypeDeclarations.hs
+++ b/src/Language/PureScript/Sugar/TypeDeclarations.hs
@@ -14,6 +14,8 @@
 --
 -----------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleContexts #-}
+
 module Language.PureScript.Sugar.TypeDeclarations (
     desugarTypeDeclarations,
     desugarTypeDeclarationsModule
@@ -22,6 +24,7 @@
 import Control.Applicative
 import Control.Monad (forM)
 import Control.Monad.Except (throwError)
+import Control.Monad.Error.Class (MonadError)
 
 import Language.PureScript.AST
 import Language.PureScript.Names
@@ -32,15 +35,15 @@
 -- |
 -- Replace all top level type declarations in a module with type annotations
 --
-desugarTypeDeclarationsModule :: [Module] -> Either ErrorStack [Module]
-desugarTypeDeclarationsModule ms = forM ms $ \(Module name ds exps) ->
-  rethrow (mkCompileError ("Error in module " ++ show name) Nothing `combineErrors`) $
-    Module name <$> desugarTypeDeclarations ds <*> pure exps
+desugarTypeDeclarationsModule :: (Functor m, Applicative m, MonadError MultipleErrors m) => [Module] -> m [Module]
+desugarTypeDeclarationsModule ms = forM ms $ \(Module coms name ds exps) ->
+  rethrow (onErrorMessages (ErrorInModule name)) $
+    Module coms name <$> desugarTypeDeclarations ds <*> pure exps
 
 -- |
 -- Replace all top level type declarations with type annotations
 --
-desugarTypeDeclarations :: [Declaration] -> Either ErrorStack [Declaration]
+desugarTypeDeclarations :: (Functor m, Applicative m, MonadError MultipleErrors m) => [Declaration] -> m [Declaration]
 desugarTypeDeclarations (PositionedDeclaration pos com d : ds) = do
   (d' : ds') <- rethrowWithPosition pos $ desugarTypeDeclarations (d : ds)
   return (PositionedDeclaration pos com d' : ds')
@@ -48,13 +51,13 @@
   (_, nameKind, val) <- fromValueDeclaration d
   desugarTypeDeclarations (ValueDeclaration name nameKind [] (Right (TypedValue True val ty)) : rest)
   where
-  fromValueDeclaration :: Declaration -> Either ErrorStack (Ident, NameKind, Expr)
+  fromValueDeclaration :: (Functor m, Applicative m, MonadError MultipleErrors m) => Declaration -> m (Ident, NameKind, Expr)
   fromValueDeclaration (ValueDeclaration name' nameKind [] (Right val)) | name == name' = return (name', nameKind, val)
   fromValueDeclaration (PositionedDeclaration pos com d') = do
     (ident, nameKind, val) <- rethrowWithPosition pos $ fromValueDeclaration d'
     return (ident, nameKind, PositionedValue pos com val)
-  fromValueDeclaration _ = throwError $ mkErrorStack ("Orphan type declaration for " ++ show name) Nothing
-desugarTypeDeclarations (TypeDeclaration name _ : []) = throwError $ mkErrorStack ("Orphan type declaration for " ++ show name) Nothing
+  fromValueDeclaration _ = throwError . errorMessage $ OrphanTypeDeclaration name
+desugarTypeDeclarations (TypeDeclaration name _ : []) = throwError . errorMessage $ OrphanTypeDeclaration name
 desugarTypeDeclarations (ValueDeclaration name nameKind bs val : rest) = do
   let (_, f, _) = everywhereOnValuesTopDownM return go return
       f' (Left gs) = Left <$> mapM (pairM return f) gs
diff --git a/src/Language/PureScript/Supply.hs b/src/Language/PureScript/Supply.hs
deleted file mode 100644
--- a/src/Language/PureScript/Supply.hs
+++ /dev/null
@@ -1,56 +0,0 @@
------------------------------------------------------------------------------
---
--- Module      :  Language.PureScript.Supply
--- Copyright   :  (c) Phil Freeman 2014
--- License     :  MIT
---
--- Maintainer  :  Phil Freeman <paf31@cantab.net>
--- Stability   :  experimental
--- Portability :
---
--- |
--- Fresh variable supply
---
------------------------------------------------------------------------------
-
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE UndecidableInstances #-}
-
-module Language.PureScript.Supply where
-
-import Data.Functor.Identity
-
-import Control.Applicative
-import Control.Monad.State
-import Control.Monad.Except
-
-newtype SupplyT m a = SupplyT { unSupplyT :: StateT Integer m a } deriving (Functor, Applicative, Monad, MonadTrans)
-
-runSupplyT :: Integer -> SupplyT m a -> m (a, Integer)
-runSupplyT n = flip runStateT n . unSupplyT
-
-evalSupplyT :: (Functor m) => Integer -> SupplyT m a -> m a
-evalSupplyT n = fmap fst . runSupplyT n
-
-type Supply = SupplyT Identity
-
-runSupply :: Integer -> Supply a -> (a, Integer)
-runSupply n = runIdentity . runSupplyT n
-
-evalSupply :: Integer -> Supply a -> a
-evalSupply n = runIdentity . evalSupplyT n
-
-fresh :: (Monad m) => SupplyT m Integer
-fresh = SupplyT $ do
-  n <- get
-  put (n + 1)
-  return n
-
-freshName :: (Functor m, Monad m) => SupplyT m String
-freshName = ('_' :) . show <$> fresh
-
-instance (MonadError e m) => MonadError e (SupplyT m) where
-  throwError = SupplyT . throwError
-  catchError e f = SupplyT $ catchError (unSupplyT e) (unSupplyT . f)
diff --git a/src/Language/PureScript/TypeChecker.hs b/src/Language/PureScript/TypeChecker.hs
--- a/src/Language/PureScript/TypeChecker.hs
+++ b/src/Language/PureScript/TypeChecker.hs
@@ -26,8 +26,9 @@
 import Language.PureScript.TypeChecker.Synonyms as T
 
 import Data.Maybe
-import Data.List (nub, (\\), find, intercalate)
+import Data.List (nub, (\\))
 import Data.Foldable (for_)
+
 import qualified Data.Map as M
 
 import Control.Monad.State
@@ -46,7 +47,7 @@
   env <- getEnv
   putEnv $ env { types = M.insert (Qualified (Just moduleName) name) (ctorKind, DataType args dctors) (types env) }
   forM_ dctors $ \(dctor, tys) ->
-    rethrow (mkCompileError ("Error in data constructor " ++ show dctor) Nothing `combineErrors`) $
+    rethrow (onErrorMessages (ErrorInDataConstructor dctor)) $
       addDataConstructor moduleName dtype name (map fst args) dctor tys
 
 addDataConstructor :: ModuleName -> DataDeclType -> ProperName -> [String] -> ProperName -> [Type] -> Check ()
@@ -70,7 +71,7 @@
 valueIsNotDefined moduleName name = do
   env <- getEnv
   case M.lookup (moduleName, name) (names env) of
-    Just _ -> throwError . strMsg $ show name ++ " is already defined"
+    Just _ -> throwError . errorMessage $ RedefinedIdent name
     Nothing -> return ()
 
 addValue :: ModuleName -> Ident -> Type -> NameKind -> Check ()
@@ -94,7 +95,7 @@
 
 checkDuplicateTypeArguments :: [String] -> Check ()
 checkDuplicateTypeArguments args = for_ firstDup $ \dup ->
-  throwError . strMsg $ "Duplicate type argument '" ++ dup ++ "'"
+  throwError . errorMessage $ DuplicateTypeArgument dup
   where
   firstDup :: Maybe String
   firstDup = listToMaybe $ args \\ nub args
@@ -103,10 +104,10 @@
 checkTypeClassInstance _ (TypeVar _) = return ()
 checkTypeClassInstance _ (TypeConstructor ctor) = do
   env <- getEnv
-  when (ctor `M.member` typeSynonyms env) . throwError . strMsg $ "Type synonym instances are disallowed"
+  when (ctor `M.member` typeSynonyms env) . throwError . errorMessage $ TypeSynonymInstance
   return ()
 checkTypeClassInstance m (TypeApp t1 t2) = checkTypeClassInstance m t1 >> checkTypeClassInstance m t2
-checkTypeClassInstance _ ty = throwError $ mkErrorStack "Type class instance head is invalid." (Just (TypeError ty))
+checkTypeClassInstance _ ty = throwError . errorMessage $ InvalidInstanceHead ty
 
 -- |
 -- Check that type synonyms are fully-applied in a type
@@ -133,7 +134,7 @@
   go :: [Declaration] -> Check [Declaration]
   go [] = return []
   go (DataDeclaration dtype name args dctors : rest) = do
-    rethrow (mkCompileError ("Error in type constructor " ++ show name) Nothing `combineErrors`) $ do
+    rethrow (onErrorMessages (ErrorInTypeConstructor name)) $ do
       when (dtype == Newtype) $ checkNewtype dctors
       checkDuplicateTypeArguments $ map fst args
       ctorKind <- kindsOf True moduleName name args (concatMap snd dctors)
@@ -144,10 +145,10 @@
     where
     checkNewtype :: [(ProperName, [Type])] -> Check ()
     checkNewtype [(_, [_])] = return ()
-    checkNewtype [(_, _)] = throwError . strMsg $ "newtypes constructors must have a single argument"
-    checkNewtype _ = throwError . strMsg $ "newtypes must have a single constructor"
+    checkNewtype [(_, _)] = throwError . errorMessage $ InvalidNewtype
+    checkNewtype _ = throwError . errorMessage $ InvalidNewtype
   go (d@(DataBindingGroupDeclaration tys) : rest) = do
-    rethrow (mkCompileError "Error in data binding group" Nothing `combineErrors`) $ do
+    rethrow (onErrorMessages ErrorInDataBindingGroup) $ do
       let syns = mapMaybe toTypeSynonym tys
       let dataDecls = mapMaybe toDataDecl tys
       (syn_ks, data_ks) <- kindsOfAll moduleName syns (map (\(_, name, args, dctors) -> (name, args, concatMap snd dctors)) dataDecls)
@@ -169,7 +170,7 @@
     toDataDecl (PositionedDeclaration _ _ d') = toDataDecl d'
     toDataDecl _ = Nothing
   go (TypeSynonymDeclaration name args ty : rest) = do
-    rethrow (mkCompileError ("Error in type synonym " ++ show name) Nothing `combineErrors`) $ do
+    rethrow (onErrorMessages (ErrorInTypeSynonym name)) $ do
       checkDuplicateTypeArguments $ map fst args
       kind <- kindsOf False moduleName name args [ty]
       let args' = args `withKinds` kind
@@ -178,7 +179,7 @@
     return $ TypeSynonymDeclaration name args ty : ds
   go (TypeDeclaration _ _ : _) = error "Type declarations should have been removed"
   go (ValueDeclaration name nameKind [] (Right val) : rest) = do
-    d <- rethrow (mkCompileError ("Error in declaration " ++ show name) Nothing `combineErrors`) $ do
+    d <- rethrow (onErrorMessages (ErrorInValueDeclaration name)) $ do
       valueIsNotDefined moduleName name
       [(_, (val', ty))] <- typesOf mainModuleName moduleName [(name, val)]
       addValue moduleName name ty nameKind
@@ -187,7 +188,7 @@
     return $ d : ds
   go (ValueDeclaration{} : _) = error "Binders were not desugared"
   go (BindingGroupDeclaration vals : rest) = do
-    d <- rethrow (mkCompileError ("Error in binding group " ++ show (map (\(ident, _, _) -> ident) vals)) Nothing `combineErrors`) $ do
+    d <- rethrow (onErrorMessages (ErrorInBindingGroup (map (\(ident, _, _) -> ident) vals))) $ do
       forM_ (map (\(ident, _, _) -> ident) vals) $ \name ->
         valueIsNotDefined moduleName name
       tys <- typesOf mainModuleName moduleName $ map (\(ident, _, ty) -> (ident, ty)) vals
@@ -207,19 +208,19 @@
     ds <- go rest
     return $ d : ds
   go (d@(ExternDeclaration importTy name _ ty) : rest) = do
-    rethrow (mkCompileError ("Error in foreign import declaration " ++ show name) Nothing `combineErrors`) $ do
+    rethrow (onErrorMessages (ErrorInForeignImport name)) $ do
       env <- getEnv
       kind <- kindOf moduleName ty
-      guardWith (strMsg "Expected kind *") $ kind == Star
+      guardWith (errorMessage (ExpectedType kind)) $ kind == Star
       case M.lookup (moduleName, name) (names env) of
-        Just _ -> throwError . strMsg $ show name ++ " is already defined"
+        Just _ -> throwError . errorMessage $ RedefinedIdent name
         Nothing -> putEnv (env { names = M.insert (moduleName, name) (ty, Extern importTy, Defined) (names env) })
     ds <- go rest
     return $ d : ds
   go (d@(FixityDeclaration _ name) : rest) = do
     ds <- go rest
     env <- getEnv
-    guardWith (strMsg ("Fixity declaration with no binding: " ++ name)) $ M.member (moduleName, Op name) $ names env
+    guardWith (errorMessage (OrphanFixityDeclaration name)) $ M.member (moduleName, Op name) $ names env
     return $ d : ds
   go (d@(ImportDeclaration importedModule _ _) : rest) = do
     tcds <- getTypeClassDictionaries
@@ -276,74 +277,65 @@
 -- required by exported members are also exported.
 --
 typeCheckModule :: Maybe ModuleName -> Module -> Check Module
-typeCheckModule _ (Module _ _ Nothing) = error "exports should have been elaborated"
-typeCheckModule mainModuleName (Module mn decls (Just exps)) = do
+typeCheckModule _ (Module _ _ _ Nothing) = error "exports should have been elaborated"
+typeCheckModule mainModuleName (Module coms mn decls (Just exps)) = rethrow (onErrorMessages (ErrorInModule mn)) $ do
   modify (\s -> s { checkCurrentModule = Just mn })
   decls' <- typeCheckAll mainModuleName mn exps decls
   forM_ exps $ \e -> do
     checkTypesAreExported e
     checkClassMembersAreExported e
     checkClassesAreExported e
-  return $ Module mn decls' (Just exps)
+  return $ Module coms mn decls' (Just exps)
   where
 
-  checkMemberExport :: (Show a) => String -> (Type -> [a]) -> (a -> Bool) -> DeclarationRef -> Check ()
-  checkMemberExport thing extract test (ValueRef name) = do
+  checkMemberExport :: (Type -> [DeclarationRef]) -> DeclarationRef -> Check ()
+  checkMemberExport extract dr@(ValueRef name) = do
     ty <- lookupVariable mn (Qualified (Just mn) name)
-    case find test (extract ty) of
-      Just hiddenType -> throwError . strMsg $
-        "Error in module '" ++ show mn ++ "':\n\
-        \Exporting declaration '" ++ show name ++ "' requires " ++ thing ++ " '" ++ show hiddenType ++ "' to be exported as well"
-      Nothing -> return ()
-  checkMemberExport _ _ _ _ = return ()
+    case filter (not . exported) (extract ty) of
+      [] -> return ()
+      hidden -> throwError . errorMessage $ TransitiveExportError dr hidden
+      where
+      exported e = any (exports e) exps
+      exports (TypeRef pn1 _) (TypeRef pn2 _) = pn1 == pn2
+      exports (ValueRef id1) (ValueRef id2) = id1 == id2
+      exports (TypeClassRef pn1) (TypeClassRef pn2) = pn1 == pn2
+      exports (TypeInstanceRef id1) (TypeInstanceRef id2) = id1 == id2
+      exports (PositionedDeclarationRef _ _ r1) r2 = exports r1 r2
+      exports r1 (PositionedDeclarationRef _ _ r2) = exports r1 r2
+      exports _ _ = False
+  checkMemberExport _ _ = return ()
 
   -- Check that all the type constructors defined in the current module that appear in member types
   -- have also been exported from the module
   checkTypesAreExported :: DeclarationRef -> Check ()
-  checkTypesAreExported = checkMemberExport "type" findTcons isTconHidden
+  checkTypesAreExported = checkMemberExport findTcons
     where
-    findTcons :: Type -> [ProperName]
+    findTcons :: Type -> [DeclarationRef]
     findTcons = everythingOnTypes (++) go
       where
-      go (TypeConstructor (Qualified (Just mn') name)) | mn' == mn = [name]
+      go (TypeConstructor (Qualified (Just mn') name)) | mn' == mn = [TypeRef name (error "Data constructors unused in checkTypesAreExported")]
       go _ = []
-    isTconHidden :: ProperName -> Bool
-    isTconHidden tyName = all go exps
-      where
-      go (TypeRef tyName' _) = tyName' /= tyName
-      go _ = True
 
   -- Check that all the classes defined in the current module that appear in member types have also
   -- been exported from the module
   checkClassesAreExported :: DeclarationRef -> Check ()
-  checkClassesAreExported = checkMemberExport "class" findClasses isClassHidden
+  checkClassesAreExported = checkMemberExport findClasses
     where
-    findClasses :: Type -> [ProperName]
+    findClasses :: Type -> [DeclarationRef]
     findClasses = everythingOnTypes (++) go
       where
-      go (ConstrainedType cs _) = mapMaybe (extractCurrentModuleClass . fst) cs
+      go (ConstrainedType cs _) = mapMaybe (fmap TypeClassRef . extractCurrentModuleClass . fst) cs
       go _ = []
     extractCurrentModuleClass :: Qualified ProperName -> Maybe ProperName
     extractCurrentModuleClass (Qualified (Just mn') name) | mn == mn' = Just name
     extractCurrentModuleClass _ = Nothing
-    isClassHidden :: ProperName -> Bool
-    isClassHidden clsName = all go exps
-      where
-      go (TypeClassRef clsName') = clsName' /= clsName
-      go _ = True
 
   checkClassMembersAreExported :: DeclarationRef -> Check ()
-  checkClassMembersAreExported (TypeClassRef name) = do
+  checkClassMembersAreExported dr@(TypeClassRef name) = do
     let members = ValueRef `map` head (mapMaybe findClassMembers decls)
     let missingMembers = members \\ exps
-    unless (null missingMembers) $
-      throwError . strMsg $
-        "Error in module '" ++ show mn ++ "':\n\
-        \Class '" ++ show name ++ "' is exported but is missing member exports for '" ++ intercalate "', '" (map (show . runValueRef) missingMembers) ++ "'"
+    unless (null missingMembers) $ throwError . errorMessage $ TransitiveExportError dr members
     where
-    runValueRef :: DeclarationRef -> Ident
-    runValueRef (ValueRef refName) = refName
-    runValueRef _ = error "non-ValueRef passed to runValueRef"
     findClassMembers :: Declaration -> Maybe [Ident]
     findClassMembers (TypeClassDeclaration name' _ _ ds) | name == name' = Just $ map extractMemberName ds
     findClassMembers (PositionedDeclaration _ _ d) = findClassMembers d
diff --git a/src/Language/PureScript/TypeChecker/Entailment.hs b/src/Language/PureScript/TypeChecker/Entailment.hs
--- a/src/Language/PureScript/TypeChecker/Entailment.hs
+++ b/src/Language/PureScript/TypeChecker/Entailment.hs
@@ -31,7 +31,6 @@
 import Language.PureScript.Errors
 import Language.PureScript.Environment
 import Language.PureScript.Names
-import Language.PureScript.Pretty
 import Language.PureScript.TypeChecker.Monad
 import Language.PureScript.TypeChecker.Synonyms
 import Language.PureScript.TypeChecker.Unify
@@ -40,29 +39,6 @@
 import qualified Language.PureScript.Constants as C
 
 -- |
--- A simplified representation of expressions which are used to represent type
--- class dictionaries at runtime, which can be compared for equality
---
-data DictionaryValue
-  -- |
-  -- A dictionary which is brought into scope by a local constraint
-  --
-  = LocalDictionaryValue (Qualified Ident)
-  -- |
-  -- A dictionary which is brought into scope by an instance declaration
-  --
-  | GlobalDictionaryValue (Qualified Ident)
-  -- |
-  -- A dictionary which depends on other dictionaries
-  --
-  | DependentDictionaryValue (Qualified Ident) [DictionaryValue]
-  -- |
-  -- A subclass dictionary
-  --
-  | SubclassDictionaryValue DictionaryValue (Qualified ProperName) Integer
-  deriving (Show, Ord, Eq)
-
--- |
 -- Check that the current set of type class dictionaries entail the specified type class goal, and, if so,
 -- return a type class dictionary reference.
 --
@@ -140,16 +116,9 @@
       checkOverlaps :: [DictionaryValue] -> Check Expr
       checkOverlaps dicts =
         case [ (d1, d2) | d1 <- dicts, d2 <- dicts, d1 `overlapping` d2 ] of
-          (d1, d2) : _ -> throwError . strMsg $ unlines
-            [ "Overlapping instances found for " ++ show className ++ " " ++ unwords (map prettyPrintType tys) ++ "."
-            , "For example:"
-            , prettyPrintDictionaryValue d1
-            , "and:"
-            , prettyPrintDictionaryValue d2
-            ]
+          ds@(_ : _) -> throwError . errorMessage $ OverlappingInstances className tys (map fst ds)
           _ -> case chooseSimplestDictionaries dicts of
-                 [] -> throwError . strMsg $
-                         "No instance found for " ++ show className ++ " " ++ unwords (map prettyPrintTypeAtom tys)
+                 [] -> throwError . errorMessage $ NoInstanceFound className tys
                  d : _ -> return $ dictionaryValueToValue d
       -- Choose the simplest DictionaryValues from a list of candidates
       -- The reason for this function is as follows:
@@ -180,18 +149,6 @@
       overlapping SubclassDictionaryValue{} _ = False
       overlapping _ SubclassDictionaryValue{} = False
       overlapping _ _ = True
-      -- |
-      -- Render a DictionaryValue fit for human consumption in error messages
-      --
-      prettyPrintDictionaryValue :: DictionaryValue -> String
-      prettyPrintDictionaryValue = unlines . indented 0
-        where
-        indented n (LocalDictionaryValue _)           = [spaces n ++ "Dictionary in scope"]
-        indented n (GlobalDictionaryValue nm)         = [spaces n ++ show nm]
-        indented n (DependentDictionaryValue nm args) = (spaces n ++ show nm ++ " via") : concatMap (indented (n + 2)) args
-        indented n (SubclassDictionaryValue sup nm _) = (spaces n ++ show nm ++ " via superclass") : indented (n + 2) sup
-
-        spaces n = replicate n ' ' ++ "- "
 
     valUndefined :: Expr
     valUndefined = Var (Qualified (Just (ModuleName [ProperName C.prim])) (Ident C.undefined))
diff --git a/src/Language/PureScript/TypeChecker/Kinds.hs b/src/Language/PureScript/TypeChecker/Kinds.hs
--- a/src/Language/PureScript/TypeChecker/Kinds.hs
+++ b/src/Language/PureScript/TypeChecker/Kinds.hs
@@ -38,7 +38,6 @@
 import Language.PureScript.Errors
 import Language.PureScript.Kinds
 import Language.PureScript.Names
-import Language.PureScript.Pretty
 import Language.PureScript.TypeChecker.Monad
 import Language.PureScript.Types
 
@@ -65,7 +64,7 @@
   FunKind k1 k2 =?= FunKind k3 k4 = do
     k1 =?= k3
     k2 =?= k4
-  k1 =?= k2 = UnifyT . lift . throwError . strMsg $ "Cannot unify " ++ prettyPrintKind k1 ++ " with " ++ prettyPrintKind k2 ++ "."
+  k1 =?= k2 = UnifyT . lift . throwError . errorMessage $ KindsDoNotUnify k1 k2
 
 -- |
 -- Infer the kind of a single type
@@ -78,7 +77,7 @@
 --
 kindOfWithScopedVars :: Type -> Check (Kind, [(String, Kind)])   
 kindOfWithScopedVars ty = 
-  rethrow (mkCompileError "Error checking kind" (Just (TypeError ty)) `combineErrors`) $
+  rethrow (onErrorMessages (ErrorCheckingKind ty)) $
     fmap tidyUp . liftUnify $ infer ty
   where
   tidyUp ((k, args), sub) = ( starIfUnknown (sub $? k)
@@ -156,7 +155,7 @@
 -- Infer a kind for a type
 --
 infer :: Type -> UnifyT Kind Check (Kind, [(String, Kind)])
-infer ty = rethrow (mkCompileError "Error inferring type of value" (Just (TypeError ty)) `combineErrors`) $ infer' ty
+infer ty = rethrow (onErrorMessages (ErrorCheckingKind ty)) $ infer' ty
 
 infer' :: Type -> UnifyT Kind Check (Kind, [(String, Kind)])
 infer' (ForAll ident ty _) = do
@@ -189,10 +188,10 @@
   go (Skolem v _ _) = do
     Just moduleName <- checkCurrentModule <$> get
     UnifyT . lift $ lookupTypeVariable moduleName (Qualified Nothing (ProperName v))
-  go c@(TypeConstructor v) = do
+  go (TypeConstructor v) = do
     env <- liftCheck getEnv
     case M.lookup v (types env) of
-      Nothing -> UnifyT . lift . throwError $ mkErrorStack "Unknown type constructor" (Just (TypeError c))
+      Nothing -> UnifyT . lift . throwError . errorMessage $ UnknownTypeConstructor v
       Just (kind, _) -> return kind
   go (TypeApp t1 t2) = do
     k0 <- fresh
diff --git a/src/Language/PureScript/TypeChecker/Monad.hs b/src/Language/PureScript/TypeChecker/Monad.hs
--- a/src/Language/PureScript/TypeChecker/Monad.hs
+++ b/src/Language/PureScript/TypeChecker/Monad.hs
@@ -109,41 +109,41 @@
 -- |
 -- Lookup the type of a value by name in the @Environment@
 --
-lookupVariable :: (e ~ ErrorStack, Functor m, MonadState CheckState m, MonadError e m) => ModuleName -> Qualified Ident -> m Type
+lookupVariable :: (e ~ MultipleErrors, Functor m, MonadState CheckState m, MonadError e m) => ModuleName -> Qualified Ident -> m Type
 lookupVariable currentModule (Qualified moduleName var) = do
   env <- getEnv
   case M.lookup (fromMaybe currentModule moduleName, var) (names env) of
-    Nothing -> throwError . strMsg $ show var ++ " is undefined"
+    Nothing -> throwError . errorMessage $ NameIsUndefined var
     Just (ty, _, _) -> return ty
 
 -- |
 -- Lookup the visibility of a value by name in the @Environment@
 --
-getVisibility :: (e ~ ErrorStack, Functor m, MonadState CheckState m, MonadError e m) => ModuleName -> Qualified Ident -> m NameVisibility
+getVisibility :: (e ~ MultipleErrors, Functor m, MonadState CheckState m, MonadError e m) => ModuleName -> Qualified Ident -> m NameVisibility
 getVisibility currentModule (Qualified moduleName var) = do
   env <- getEnv
   case M.lookup (fromMaybe currentModule moduleName, var) (names env) of
-    Nothing -> throwError . strMsg $ show var ++ " is undefined"
+    Nothing -> throwError . errorMessage $ NameIsUndefined var
     Just (_, _, vis) -> return vis
 
 -- |
 -- Assert that a name is visible
 --
-checkVisibility :: (e ~ ErrorStack, Functor m, MonadState CheckState m, MonadError e m) => ModuleName -> Qualified Ident -> m ()
+checkVisibility :: (e ~ MultipleErrors, Functor m, MonadState CheckState m, MonadError e m) => ModuleName -> Qualified Ident -> m ()
 checkVisibility currentModule name@(Qualified _ var) = do
   vis <- getVisibility currentModule name
   case vis of
-    Undefined -> throwError . strMsg $ show var ++ " may not be defined in the current scope."
+    Undefined -> throwError . errorMessage $ NameNotInScope var
     _ -> return ()
 
 -- |
 -- Lookup the kind of a type by name in the @Environment@
 --
-lookupTypeVariable :: (e ~ ErrorStack, Functor m, MonadState CheckState m, MonadError e m) => ModuleName -> Qualified ProperName -> m Kind
+lookupTypeVariable :: (e ~ MultipleErrors, Functor m, MonadState CheckState m, MonadError e m) => ModuleName -> Qualified ProperName -> m Kind
 lookupTypeVariable currentModule (Qualified moduleName name) = do
   env <- getEnv
   case M.lookup (Qualified (Just $ fromMaybe currentModule moduleName) name) (types env) of
-    Nothing -> throwError . strMsg $ "Type variable " ++ show name ++ " is undefined"
+    Nothing -> throwError . errorMessage $ UndefinedTypeVariable name
     Just (k, _) -> return k
 
 -- |
@@ -171,8 +171,8 @@
 -- |
 -- The type checking monad, which provides the state of the type checker, and error reporting capabilities
 --
-newtype Check a = Check { unCheck :: StateT CheckState (Either ErrorStack) a }
-  deriving (Functor, Monad, Applicative, MonadState CheckState, MonadError ErrorStack)
+newtype Check a = Check { unCheck :: StateT CheckState (Either MultipleErrors) a }
+  deriving (Functor, Monad, Applicative, MonadState CheckState, MonadError MultipleErrors)
 
 -- |
 -- Get the current @Environment@
@@ -204,9 +204,9 @@
 runCheck' :: (MonadReader (Options mode) m, MonadError String m) => Environment -> Check a -> m (a, Environment)
 runCheck' env c = do
   verbose <- asks optionsVerboseErrors
-  stringifyErrorStack verbose $ do
-  (a, s) <- flip runStateT (CheckState env 0 0 Nothing) $ unCheck c
-  return (a, checkEnv s)
+  interpretMultipleErrors verbose $ do
+    (a, s) <- flip runStateT (CheckState env 0 0 Nothing) $ unCheck c
+    return (a, checkEnv s)
 
 -- |
 -- Make an assertion, failing with an error message
diff --git a/src/Language/PureScript/TypeChecker/Rows.hs b/src/Language/PureScript/TypeChecker/Rows.hs
--- a/src/Language/PureScript/TypeChecker/Rows.hs
+++ b/src/Language/PureScript/TypeChecker/Rows.hs
@@ -53,7 +53,7 @@
     checkDups r@RCons{} =
       let (ls, _) = rowToList r in
       case firstDup . sort . map fst $ ls of
-        Just l -> throwError $ mkErrorStack ("Duplicate label " ++ show l ++ " in row") $ Just (ExprError val)
+        Just l -> throwError . errorMessage $ DuplicateLabel l (Just val)
         Nothing -> return ()
     checkDups _ = return ()
 
diff --git a/src/Language/PureScript/TypeChecker/Skolems.hs b/src/Language/PureScript/TypeChecker/Skolems.hs
--- a/src/Language/PureScript/TypeChecker/Skolems.hs
+++ b/src/Language/PureScript/TypeChecker/Skolems.hs
@@ -31,7 +31,6 @@
 
 import Language.PureScript.AST
 import Language.PureScript.Errors
-import Language.PureScript.Pretty
 import Language.PureScript.TypeChecker.Monad
 import Language.PureScript.Types
 
@@ -89,7 +88,7 @@
   let (_, f, _, _, _) = everythingWithContextOnValues [] [] (++) def go def def def
   in case f root of
        [] -> return ()
-       ((binding, val) : _) -> throwError $ mkErrorStack ("Rigid/skolem type variable " ++ maybe "" (("bound by " ++) . prettyPrintValue) binding ++ " has escaped.") (Just (ExprError val))
+       ((binding, val) : _) -> throwError . errorMessage . ErrorInExpression val $ EscapedSkolem binding
   where
   def s _ = (s, [])
 
@@ -112,4 +111,4 @@
     where
     go' val@(TypedValue _ _ (ForAll _ _ (Just sco'))) | sco == sco' = First (Just val)
     go' _ = mempty
-skolemEscapeCheck val = throwError $ mkErrorStack "Untyped value passed to skolemEscapeCheck" (Just (ExprError val))
+skolemEscapeCheck _ = error "Untyped value passed to skolemEscapeCheck"
diff --git a/src/Language/PureScript/TypeChecker/Subsumption.hs b/src/Language/PureScript/TypeChecker/Subsumption.hs
--- a/src/Language/PureScript/TypeChecker/Subsumption.hs
+++ b/src/Language/PureScript/TypeChecker/Subsumption.hs
@@ -20,14 +20,12 @@
 import Data.List (sortBy)
 import Data.Ord (comparing)
 
-import Control.Applicative
 import Control.Monad.Except
 import Control.Monad.Unify
 
 import Language.PureScript.AST
 import Language.PureScript.Environment
 import Language.PureScript.Errors
-import Language.PureScript.Pretty
 import Language.PureScript.TypeChecker.Monad
 import Language.PureScript.TypeChecker.Skolems
 import Language.PureScript.TypeChecker.Synonyms
@@ -38,12 +36,7 @@
 -- Check whether one type subsumes another, rethrowing errors to provide a better error message
 --
 subsumes :: Maybe Expr -> Type -> Type -> UnifyT Type Check (Maybe Expr)
-subsumes val ty1 ty2 = rethrow (mkCompileError errorMessage (ExprError <$> val) `combineErrors`) $ subsumes' val ty1 ty2
-  where
-  errorMessage = "Error checking that type "
-    ++ prettyPrintType ty1
-    ++ " subsumes type "
-    ++ prettyPrintType ty2
+subsumes val ty1 ty2 = rethrow (onErrorMessages (ErrorInSubsumption ty1 ty2)) $ subsumes' val ty1 ty2
 
 -- |
 -- Check whether one type subsumes another
@@ -58,7 +51,7 @@
       sko <- newSkolemConstant
       let sk = skolemize ident sko sco' ty2
       subsumes val ty1 sk
-    Nothing -> throwError . strMsg $ "Skolem variable scope is unspecified"
+    Nothing -> throwError . errorMessage $ UnspecifiedSkolemScope
 subsumes' val (TypeApp (TypeApp f1 arg1) ret1) (TypeApp (TypeApp f2 arg2) ret2) | f1 == tyFunction && f2 == tyFunction = do
   _ <- subsumes Nothing arg2 arg1
   _ <- subsumes Nothing ret1 ret2
diff --git a/src/Language/PureScript/TypeChecker/Synonyms.hs b/src/Language/PureScript/TypeChecker/Synonyms.hs
--- a/src/Language/PureScript/TypeChecker/Synonyms.hs
+++ b/src/Language/PureScript/TypeChecker/Synonyms.hs
@@ -40,19 +40,19 @@
 -- |
 -- Build a type substitution for a type synonym
 --
-buildTypeSubstitution :: Qualified ProperName -> Int -> Type -> Either String (Maybe Type)
+buildTypeSubstitution :: Qualified ProperName -> Int -> Type -> Either ErrorMessage (Maybe Type)
 buildTypeSubstitution name n = go n []
   where
-  go :: Int -> [Type] -> Type -> Either String (Maybe Type)
+  go :: Int -> [Type] -> Type -> Either ErrorMessage (Maybe Type)
   go 0 args (TypeConstructor ctor) | name == ctor = return (Just $ SaturatedTypeSynonym ctor args)
-  go m _ (TypeConstructor ctor) | m > 0 && name == ctor = throwError $ "Partially applied type synonym " ++ show name
+  go m _ (TypeConstructor ctor) | m > 0 && name == ctor = throwError $ PartiallyAppliedSynonym name
   go m args (TypeApp f arg) = go (m - 1) (arg:args) f
   go _ _ _ = return Nothing
 
 -- |
 -- Replace all instances of a specific type synonym with the @SaturatedTypeSynonym@ data constructor
 --
-saturateTypeSynonym :: Qualified ProperName -> Int -> Type -> Either String Type
+saturateTypeSynonym :: Qualified ProperName -> Int -> Type -> Either ErrorMessage Type
 saturateTypeSynonym name n = everywhereOnTypesTopDownM replace
   where
   replace t = fromMaybe t <$> buildTypeSubstitution name n t
@@ -60,7 +60,7 @@
 -- |
 -- Replace all type synonyms with the @SaturatedTypeSynonym@ data constructor
 --
-saturateAllTypeSynonyms :: [(Qualified ProperName, Int)] -> Type -> Either String Type
+saturateAllTypeSynonyms :: [(Qualified ProperName, Int)] -> Type -> Either ErrorMessage Type
 saturateAllTypeSynonyms syns d = foldM (\result (name, n) -> saturateTypeSynonym name n result) d syns
 
 -- |
@@ -76,22 +76,22 @@
 -- Replace fully applied type synonyms with the @SaturatedTypeSynonym@ data constructor, which helps generate
 -- better error messages during unification.
 --
-replaceAllTypeSynonyms' :: Environment -> Type -> Either String Type
+replaceAllTypeSynonyms' :: Environment -> Type -> Either ErrorMessage Type
 replaceAllTypeSynonyms' env d =
   let
     syns = map (\(name, (args, _)) -> (name, length args)) . M.toList $ typeSynonyms env
   in
     saturateAllTypeSynonyms syns d
 
-replaceAllTypeSynonyms :: (e ~ ErrorStack, Functor m, Monad m, MonadState CheckState m, MonadError e m) => Type -> m Type
+replaceAllTypeSynonyms :: (e ~ MultipleErrors, Functor m, Monad m, MonadState CheckState m, MonadError e m) => Type -> m Type
 replaceAllTypeSynonyms d = do
   env <- getEnv
-  either (throwError . strMsg) return $ replaceAllTypeSynonyms' env d
+  either (throwError . errorMessage) return $ replaceAllTypeSynonyms' env d
 
 -- |
 -- Replace a type synonym and its arguments with the aliased type
 --
-expandTypeSynonym' :: Environment -> Qualified ProperName -> [Type] -> Either String Type
+expandTypeSynonym' :: Environment -> Qualified ProperName -> [Type] -> Either ErrorMessage Type
 expandTypeSynonym' env name args =
   case M.lookup name (typeSynonyms env) of
     Just (synArgs, body) -> do
@@ -99,12 +99,12 @@
       replaceAllTypeSynonyms' env repl
     Nothing -> error "Type synonym was not defined"
 
-expandTypeSynonym :: (e ~ ErrorStack, Functor m, Monad m, MonadState CheckState m, MonadError e m) => Qualified ProperName -> [Type] -> m Type
+expandTypeSynonym :: (e ~ MultipleErrors, Functor m, Monad m, MonadState CheckState m, MonadError e m) => Qualified ProperName -> [Type] -> m Type
 expandTypeSynonym name args = do
   env <- getEnv
-  either (throwError . strMsg) return $ expandTypeSynonym' env name args
+  either (throwError . errorMessage) return $ expandTypeSynonym' env name args
 
-expandAllTypeSynonyms :: (e ~ ErrorStack, Functor m, Applicative m, Monad m, MonadState CheckState m, MonadError e m) => Type -> m Type
+expandAllTypeSynonyms :: (e ~ MultipleErrors, Functor m, Applicative m, Monad m, MonadState CheckState m, MonadError e m) => Type -> m Type
 expandAllTypeSynonyms = everywhereOnTypesTopDownM go
   where
   go (SaturatedTypeSynonym name args) = expandTypeSynonym name args
diff --git a/src/Language/PureScript/TypeChecker/Types.hs b/src/Language/PureScript/TypeChecker/Types.hs
--- a/src/Language/PureScript/TypeChecker/Types.hs
+++ b/src/Language/PureScript/TypeChecker/Types.hs
@@ -39,7 +39,6 @@
 import Data.List
 import Data.Maybe (fromMaybe)
 import qualified Data.Map as M
-import Data.String (IsString)
 
 import Control.Applicative
 import Control.Monad.Except
@@ -51,7 +50,6 @@
 import Language.PureScript.Errors
 import Language.PureScript.Kinds
 import Language.PureScript.Names
-import Language.PureScript.Pretty
 import Language.PureScript.Traversals
 import Language.PureScript.TypeChecker.Entailment
 import Language.PureScript.TypeChecker.Kinds
@@ -184,7 +182,7 @@
 -- Check the kind of a type, failing if it is not of kind *.
 --
 checkTypeKind :: Kind -> UnifyT t Check ()
-checkTypeKind kind = guardWith (strMsg $ "Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
+checkTypeKind kind = guardWith (errorMessage (ExpectedType kind)) $ kind == Star
 
 -- |
 -- Remove any ForAlls and ConstrainedType constructors in a type by introducing new unknowns
@@ -207,7 +205,7 @@
 -- Infer a type for a value, rethrowing any error to provide a more useful error message
 --
 infer :: Expr -> UnifyT Type Check Expr
-infer val = rethrow (mkCompileError "Error inferring type of value" (Just (ExprError val)) `combineErrors`) $ infer' val
+infer val = rethrow (onErrorMessages (ErrorInferringType val)) $ infer' val
 
 -- |
 -- Infer a type for a value
@@ -269,7 +267,7 @@
 infer' v@(Constructor c) = do
   env <- getEnv
   case M.lookup c (dataConstructors env) of
-    Nothing -> throwError . strMsg $ "Constructor " ++ show c ++ " is undefined"
+    Nothing -> throwError . errorMessage $ UnknownDataConstructor c Nothing
     Just (_, _, ty, _) -> do (v', ty') <- sndM (introduceSkolemScope <=< replaceAllTypeSynonyms) <=< instantiatePolyTypeWithUnknowns v $ ty
                              return $ TypedValue True v' ty'
 infer' (Case vals binders) = do
@@ -365,8 +363,8 @@
           return M.empty
         go (binder : binders') (TypeApp (TypeApp t obj) ret) | t == tyFunction =
           M.union <$> inferBinder obj binder <*> go binders' ret
-        go _ _ = throwError . strMsg $ "Wrong number of arguments to constructor " ++ show ctor
-    _ -> throwError . strMsg $ "Constructor " ++ show ctor ++ " is not defined"
+        go _ _ = throwError . errorMessage $ IncorrectConstructorArity ctor
+    _ -> throwError . errorMessage $ UnknownDataConstructor ctor Nothing
 inferBinder val (ObjectBinder props) = do
   row <- fresh
   rest <- fresh
@@ -404,7 +402,7 @@
 checkBinders :: [Type] -> Type -> [CaseAlternative] -> UnifyT Type Check [CaseAlternative]
 checkBinders _ _ [] = return []
 checkBinders nvals ret (CaseAlternative binders result : bs) = do
-  guardWith (strMsg "Overlapping binders in case statement") $
+  guardWith (errorMessage $ OverlappingArgNames Nothing) $
     let ns = concatMap binderNames binders in length (nub ns) == length ns
   Just moduleName <- checkCurrentModule <$> get
   m1 <- M.unions <$> zipWithM inferBinder nvals binders
@@ -427,13 +425,7 @@
 -- Check the type of a value, rethrowing errors to provide a better error message
 --
 check :: Expr -> Type -> UnifyT Type Check Expr
-check val ty = rethrow (mkCompileError errorMessage (Just (ExprError val)) `combineErrors`) $ check' val ty
-  where
-  errorMessage =
-    "Error checking type of term " ++
-    prettyPrintValue val ++
-    " against type " ++
-    prettyPrintType ty
+check val ty = rethrow (onErrorMessages (ErrorCheckingType val ty)) $ check' val ty
 
 -- |
 -- Check the type of a value
@@ -490,7 +482,7 @@
   ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ ty
   v' <- subsumes (Just v) repl ty'
   case v' of
-    Nothing -> throwError . strMsg $ "Unable to check type subsumption"
+    Nothing -> throwError . errorMessage $ SubsumptionCheckFailed
     Just v'' -> return $ TypedValue True v'' ty'
 check' (SuperClassDictionary className tys) _ = do
   {-
@@ -513,10 +505,10 @@
   ty2' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ ty2
   val' <- subsumes (Just val) ty1' ty2'
   case val' of
-    Nothing -> throwError . strMsg $ "Unable to check type subsumption"
-    Just val'' -> do
-      val''' <- if checkType then withScopedTypeVars moduleName args (check val'' ty2') else return val''
-      return $ TypedValue checkType (TypedValue True val''' ty1') ty2'
+    Nothing -> throwError . errorMessage $ SubsumptionCheckFailed
+    Just _ -> do
+      val''' <- if checkType then withScopedTypeVars moduleName args (check val ty2') else return val
+      return $ TypedValue checkType val''' ty2'
 check' (Case vals binders) ret = do
   vals' <- mapM infer vals
   let ts = map (\(TypedValue _ _ t) -> t) vals'
@@ -551,7 +543,7 @@
 check' (Constructor c) ty = do
   env <- getEnv
   case M.lookup c (dataConstructors env) of
-    Nothing -> throwError . strMsg $ "Constructor " ++ show c ++ " is undefined"
+    Nothing -> throwError . errorMessage $ UnknownDataConstructor c Nothing
     Just (_, _, ty1, _) -> do
       repl <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty1
       _ <- subsumes Nothing repl ty
@@ -568,7 +560,7 @@
   return $ TypedValue True val' kt
 check' (PositionedValue pos _ val) ty =
   rethrowWithPosition pos $ check' val ty
-check' val ty = throwError $ mkErrorStack ("Expr does not have type " ++ prettyPrintType ty) (Just (ExprError val))
+check' val ty = throwError . errorMessage $ ExprDoesNotHaveType val ty
 
 containsTypeSynonyms :: Type -> Bool
 containsTypeSynonyms = everythingOnTypes (||) go where
@@ -589,8 +581,8 @@
                      return []
   go [] [] Skolem{} | lax = return []
   go [] ((p, _): _) _ | lax = return []
-                      | otherwise = throwError $ mkErrorStack ("Object does not have property " ++ p) (Just (ExprError (ObjectLiteral ps)))
-  go ((p,_):_) [] REmpty = throwError $ mkErrorStack ("Property " ++ p ++ " is not present in closed object type " ++ prettyPrintRow row) (Just (ExprError (ObjectLiteral ps)))
+                      | otherwise = throwError . errorMessage $ PropertyIsMissing p row
+  go ((p,_):_) [] REmpty = throwError . errorMessage $ PropertyIsMissing p row
   go ((p,v):ps') ts r =
     case lookup p ts of
       Nothing -> do
@@ -603,19 +595,15 @@
         v' <- check v ty
         ps'' <- go ps' (delete (p, ty) ts) r
         return $ (p, v') : ps''
-  go _ _ _ = throwError $ mkErrorStack ("Object does not have type " ++ prettyPrintType (TypeApp tyObject row)) (Just (ExprError (ObjectLiteral ps)))
+  go _ _ _ = throwError . errorMessage $ ExprDoesNotHaveType (ObjectLiteral ps) (TypeApp tyObject row)
 
 -- |
 -- Check the type of a function application, rethrowing errors to provide a better error message
 --
 checkFunctionApplication :: Expr -> Type -> Expr -> Maybe Type -> UnifyT Type Check (Type, Expr)
-checkFunctionApplication fn fnTy arg ret = rethrow (mkCompileError errorMessage (Just (ExprError fn)) `combineErrors`) $ do
+checkFunctionApplication fn fnTy arg ret = rethrow (onErrorMessages (ErrorInApplication fn fnTy arg)) $ do
   subst <- unifyCurrentSubstitution <$> UnifyT get
   checkFunctionApplication' fn (subst $? fnTy) arg (($?) subst <$> ret)
-  where
-  errorMessage = "Error applying function of type "
-    ++ prettyPrintType fnTy
-    ++ " to argument " ++ prettyPrintValue arg
 
 -- |
 -- Check the type of a function application
@@ -651,9 +639,7 @@
   checkFunctionApplication' (foldl App fn (map (flip (TypeClassDictionary True) dicts) constraints)) fnTy arg ret
 checkFunctionApplication' fn fnTy dict@TypeClassDictionary{} _ =
   return (fnTy, App fn dict)
-checkFunctionApplication' _ fnTy arg _ = throwError . strMsg $ "Cannot apply a function of type "
-  ++ prettyPrintType fnTy
-  ++ " to argument " ++ prettyPrintValue arg
+checkFunctionApplication' _ fnTy arg _ = throwError . errorMessage $ CannotApplyFunction fnTy arg
 
 -- |
 -- Compute the meet of two types, i.e. the most general type which both types subsume.
@@ -673,5 +659,9 @@
 -- |
 -- Ensure a set of property names and value does not contain duplicate labels
 --
-ensureNoDuplicateProperties :: (IsString e, MonadError e m) => [(String, Expr)] -> m ()
-ensureNoDuplicateProperties ps = guardWith "Duplicate property names" $ length (nub . map fst $ ps) == length ps
+ensureNoDuplicateProperties :: (MonadError MultipleErrors m) => [(String, Expr)] -> m ()
+ensureNoDuplicateProperties ps =
+  let ls = map fst ps in
+  case ls \\ nub ls of
+    l : _ -> throwError . errorMessage $ DuplicateLabel l Nothing
+    _ -> return ()
diff --git a/src/Language/PureScript/TypeChecker/Unify.hs b/src/Language/PureScript/TypeChecker/Unify.hs
--- a/src/Language/PureScript/TypeChecker/Unify.hs
+++ b/src/Language/PureScript/TypeChecker/Unify.hs
@@ -34,7 +34,6 @@
 
 import Language.PureScript.Environment
 import Language.PureScript.Errors
-import Language.PureScript.Pretty
 import Language.PureScript.TypeChecker.Monad
 import Language.PureScript.TypeChecker.Skolems
 import Language.PureScript.TypeChecker.Synonyms
@@ -60,7 +59,7 @@
 -- Unify two types, updating the current substitution
 --
 unifyTypes :: Type -> Type -> UnifyT Type Check ()
-unifyTypes t1 t2 = rethrow (mkCompileError ("Error unifying type " ++ prettyPrintType t1 ++ " with type " ++ prettyPrintType t2) Nothing `combineErrors`) $
+unifyTypes t1 t2 = rethrow (onErrorMessages (ErrorUnifyingTypes t1 t2)) $
   unifyTypes' t1 t2
   where
   unifyTypes' (TUnknown u1) (TUnknown u2) | u1 == u2 = return ()
@@ -82,11 +81,11 @@
     sko <- newSkolemConstant
     let sk = skolemize ident sko sc ty1
     sk `unifyTypes` ty2
-  unifyTypes' ForAll{} _ = throwError . strMsg $ "Skolem variable scope is unspecified"
+  unifyTypes' ForAll{} _ = throwError . errorMessage $ UnspecifiedSkolemScope
   unifyTypes' ty f@ForAll{} = f `unifyTypes` ty
   unifyTypes' (TypeVar v1) (TypeVar v2) | v1 == v2 = return ()
-  unifyTypes' (TypeConstructor c1) (TypeConstructor c2) =
-    guardWith (strMsg ("Cannot unify " ++ show c1 ++ " with " ++ show c2 ++ ".")) (c1 == c2)
+  unifyTypes' ty1@(TypeConstructor c1) ty2@(TypeConstructor c2) =
+    guardWith (errorMessage (TypesDoNotUnify ty1 ty2)) (c1 == c2)
   unifyTypes' (TypeApp t3 t4) (TypeApp t5 t6) = do
     t3 `unifyTypes` t5
     t4 `unifyTypes` t6
@@ -97,9 +96,9 @@
   unifyTypes' r1 r2@RCons{} = unifyRows r1 r2
   unifyTypes' r1@REmpty r2 = unifyRows r1 r2
   unifyTypes' r1 r2@REmpty = unifyRows r1 r2
-  unifyTypes' t@(ConstrainedType _ _) _ = throwError . strMsg $ "Attempted to unify a constrained type " ++ prettyPrintType t ++ " with another type."
+  unifyTypes' ty1@(ConstrainedType _ _) ty2 = throwError . errorMessage $ ConstrainedTypeUnified ty1 ty2
   unifyTypes' t3 t4@(ConstrainedType _ _) = unifyTypes' t4 t3
-  unifyTypes' t3 t4 = throwError . strMsg $ "Cannot unify " ++ prettyPrintType t3 ++ " with " ++ prettyPrintType t4 ++ "."
+  unifyTypes' t3 t4 = throwError . errorMessage $ TypesDoNotUnify t3 t4
 
 -- |
 -- Unify two rows, updating the current substitution
@@ -136,7 +135,7 @@
   unifyRows' [] REmpty [] REmpty = return ()
   unifyRows' [] (TypeVar v1) [] (TypeVar v2) | v1 == v2 = return ()
   unifyRows' [] (Skolem _ s1 _) [] (Skolem _ s2 _) | s1 == s2 = return ()
-  unifyRows' sd3 r3 sd4 r4 = throwError . strMsg $ "Cannot unify (" ++ prettyPrintRow (rowFromList (sd3, r3)) ++ ") with (" ++ prettyPrintRow (rowFromList (sd4, r4)) ++ ")"
+  unifyRows' sd3 r3 sd4 r4 = throwError . errorMessage $ TypesDoNotUnify (rowFromList (sd3, r3)) (rowFromList (sd4, r4))
 
 -- |
 -- Check that two types unify
diff --git a/src/Language/PureScript/TypeClassDictionaries.hs b/src/Language/PureScript/TypeClassDictionaries.hs
--- a/src/Language/PureScript/TypeClassDictionaries.hs
+++ b/src/Language/PureScript/TypeClassDictionaries.hs
@@ -66,6 +66,29 @@
   | TCDAlias (Qualified Ident) deriving (Show, Eq, Data, Typeable)
 
 -- |
+-- A simplified representation of expressions which are used to represent type
+-- class dictionaries at runtime, which can be compared for equality
+--
+data DictionaryValue
+  -- |
+  -- A dictionary which is brought into scope by a local constraint
+  --
+  = LocalDictionaryValue (Qualified Ident)
+  -- |
+  -- A dictionary which is brought into scope by an instance declaration
+  --
+  | GlobalDictionaryValue (Qualified Ident)
+  -- |
+  -- A dictionary which depends on other dictionaries
+  --
+  | DependentDictionaryValue (Qualified Ident) [DictionaryValue]
+  -- |
+  -- A subclass dictionary
+  --
+  | SubclassDictionaryValue DictionaryValue (Qualified ProperName) Integer
+  deriving (Show, Ord, Eq)
+
+-- |
 -- Find the original dictionary which a type class dictionary in scope refers to
 --
 canonicalizeDictionary :: TypeClassDictionaryInScope -> Qualified Ident
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -77,7 +77,7 @@
   putStrLn $ "Assert " ++ inputFile ++ " does not compile"
   assert preludeExterns (P.defaultCompileOptions { P.optionsAdditional = P.CompileOptions "Tests" [] [] }) inputFile $ \e ->
     case e of
-      Left _ -> return Nothing
+      Left err -> putStrLn err >> return Nothing
       Right _ -> return $ Just "Should not have compiled"
 
 findNodeProcess :: IO (Maybe String)
