diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -63,6 +63,7 @@
 - [@paulyoung](https://github.com/paulyoung) (Paul Young) My existing contributions and all future contributions until further notice are Copyright Paul Young, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
 - [@pelotom](https://github.com/pelotom) (Thomas Crockett) My existing contributions and all future contributions until further notice are Copyright Thomas Crockett, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
 - [@phadej](https://github.com/phadej) (Oleg Grenrus) My existing contributions and all future contributions until further notice are Copyright Oleg Grenrus, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
+- [@phiggins](https://github.com/phiggins) (Pete Higgins) My existing contributions and all future contributions until further notice are Copyright Pete Higgins, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
 - [@philopon](https://github.com/philopon) (Hirotomo Moriwaki) - My existing contributions and all future contributions until further notice are Copyright Hirotomo Moriwaki, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
 - [@pseudonom](https://github.com/pseudonom) (Eric Easley) My existing contributions and all future contributions until further notice are Copyright Eric Easley, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
 - [@puffnfresh](https://github.com/puffnfresh) (Brian McKenna) All contributions I made during June 2015 were during employment at [SlamData, Inc.](#companies) who owns the copyright. I assign copyright of all my personal contributions before June 2015 to the owners of the PureScript compiler.
@@ -81,6 +82,8 @@
 - [@utkarshkukreti](https://github.com/utkarshkukreti) (Utkarsh Kukreti) My existing contributions and all future contributions until further notice are Copyright Utkarsh Kukreti, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
 - [@vkorablin](https://github.com/vkorablin) (Vladimir Korablin) - My existing contributions and all future contributions until further notice are Copyright Vladimir Korablin, and are licensed to the owners and users of the PureScript compiler project under the terms of the MIT license.
 - [@zudov](https://github.com/zudov) (Konstantin Zudov) My existing contributions and all future contributions until further notice are Copyright Konstantin Zudov, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
+- [@brandonhamilton](https://github.com/brandonhamilton) (Brandon Hamilton) My existing contributions and all future contributions until further notice are Copyright Brandon Hamilton, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
+- [@bbqbaron](https://github.com/bbqbaron) (Eric Loren) My existing contributions and all future contributions until further notice are Copyright Eric Loren, and are licensed to the owners and users of the PureScript compiler project under the terms of the [MIT license](http://opensource.org/licenses/MIT).
 
 ### Companies
 
diff --git a/examples/failing/2378.purs b/examples/failing/2378.purs
new file mode 100644
--- /dev/null
+++ b/examples/failing/2378.purs
@@ -0,0 +1,6 @@
+-- @shouldFailWith OrphanInstance
+module Main where
+
+import Lib
+
+instance fooX :: Foo "x"
diff --git a/examples/failing/2378/Lib.purs b/examples/failing/2378/Lib.purs
new file mode 100644
--- /dev/null
+++ b/examples/failing/2378/Lib.purs
@@ -0,0 +1,3 @@
+module Lib (class Foo) where
+
+class Foo (a :: Symbol)
diff --git a/examples/failing/2379.purs b/examples/failing/2379.purs
new file mode 100644
--- /dev/null
+++ b/examples/failing/2379.purs
@@ -0,0 +1,6 @@
+-- @shouldFailWith UnknownClass
+module Main where
+
+import Lib
+
+test = x [1, 2, 3]
diff --git a/examples/failing/2379/Lib.purs b/examples/failing/2379/Lib.purs
new file mode 100644
--- /dev/null
+++ b/examples/failing/2379/Lib.purs
@@ -0,0 +1,9 @@
+module Lib (class X, x) where
+
+class X a where
+  x :: a -> String
+
+class Y a
+
+instance xArray :: Y a => X (Array a) where
+  x _ = "[]"
diff --git a/examples/failing/ConstraintFailure.purs b/examples/failing/ConstraintFailure.purs
new file mode 100644
--- /dev/null
+++ b/examples/failing/ConstraintFailure.purs
@@ -0,0 +1,13 @@
+-- @shouldFailWith NoInstanceFound
+
+module Main where
+
+import Prelude
+
+data Foo = Bar
+       
+spin :: forall a. a -> Foo
+spin x = Bar
+
+main = show <<< spin
+     
diff --git a/examples/failing/ConstraintInference.purs b/examples/failing/ConstraintInference.purs
--- a/examples/failing/ConstraintInference.purs
+++ b/examples/failing/ConstraintInference.purs
@@ -1,4 +1,4 @@
--- @shouldFailWith NoInstanceFound
+-- @shouldFailWith AmbiguousTypeVariables
 
 module Main where
 
diff --git a/examples/failing/NonWildcardNewtypeInstance.purs b/examples/failing/NonWildcardNewtypeInstance.purs
--- a/examples/failing/NonWildcardNewtypeInstance.purs
+++ b/examples/failing/NonWildcardNewtypeInstance.purs
@@ -1,4 +1,4 @@
--- @shouldFailWith NonWildcardNewtypeInstance
+-- @shouldFailWith ExpectedWildcard
 module NonWildcardNewtypeInstance where
 
 import Data.Newtype
diff --git a/examples/passing/2378.purs b/examples/passing/2378.purs
new file mode 100644
--- /dev/null
+++ b/examples/passing/2378.purs
@@ -0,0 +1,9 @@
+module Main where
+
+import Control.Monad.Eff.Console (log)
+
+class Foo (a :: Symbol)
+
+instance fooX :: Foo "x"
+
+main = log "Done"
diff --git a/examples/passing/DctorName.purs b/examples/passing/DctorName.purs
new file mode 100644
--- /dev/null
+++ b/examples/passing/DctorName.purs
@@ -0,0 +1,33 @@
+module Main where
+
+import Prelude
+import Control.Monad.Eff.Console (log)
+
+newtype Bar' = Bar' Int
+
+data Foo' = Foo' Bar'
+
+data Baz'' = Baz'' | Baz'
+
+f ∷ Foo' → Boolean
+f a = case a of Foo' b → true
+
+f' ∷ Boolean
+f' = f $ Foo' $ Bar' 0
+
+g ∷ Baz'' → Int
+g Baz'' = 0
+g Baz' = 1
+
+g' ∷ Int
+g' = g Baz''
+
+h ∷ Bar' → Int
+h (Bar' x)
+ | x <= 10 = x * 2 
+ | otherwise = 10
+
+h' ∷ Int
+h' = h $ Bar' 4
+
+main = log "Done"
diff --git a/examples/passing/GHCGenerics.purs b/examples/passing/GHCGenerics.purs
deleted file mode 100644
--- a/examples/passing/GHCGenerics.purs
+++ /dev/null
@@ -1,140 +0,0 @@
--- An example to show how we could implement GHC-style Generics using
--- functional dependencies.
---
--- See https://hackage.haskell.org/package/base-4.9.0.0/docs/GHC-Generics.html
-
-module Main where
-
-import Prelude
-import Control.Monad.Eff (Eff)
-import Control.Monad.Eff.Console (CONSOLE, log, logShow)
-
--- Representation for types with no constructors
-data V1
-
--- Representation for constructors with no arguments
-data U1 = U1
-
--- Representation for sum types
-data Sum a b = Inl a | Inr b
-
-infixr 5 type Sum as +
-
--- Representation for product types
-data Product a b = Product a b
-
-infixr 6 type Product as *
-
--- Representation for data constructors, with the data constructor name indicated
--- at the type level.
-data Ctor (name :: Symbol) a = Ctor a
-
--- Representation for occurrences of other types in a data type definition.
-data K a = K a
-
--- The Generic class asserts the existence of a type function from "real" types
--- to representation types, and an isomorphism between them.
-class Generic a repr | a -> repr where
-  to :: a -> repr
-  from :: repr -> a
-
--- We can write an instance for the (recursive) type of lists. Note that these
--- instances would be generated by the compiler ideally.
-data List a = Nil | Cons a (List a)
-
-instance genericList :: Generic (List a) (Ctor "Nil" U1 + Ctor "Cons" (K a * K (List a))) where
-  to Nil = Inl (Ctor U1)
-  to (Cons x xs) = Inr (Ctor (Product (K x) (K xs)))
-  from (Inl (Ctor U1)) = Nil
-  from (Inr (Ctor (Product (K x) (K xs)))) = Cons x xs
-
--- We'd like to refect type level strings (for data constructor names) at the value
--- level, so that we can "show" them. Again, these instances would ideally be derived
--- for us.
-class KnownSymbol (sym :: Symbol) where
-  symbol :: forall proxy. proxy sym -> String
-
-instance knownSymbolNil :: KnownSymbol "Nil" where
-  symbol _ = "Nil"
-
-instance knownSymbolCons :: KnownSymbol "Cons" where
-  symbol _ = "Cons"
-
--- A proxy for a type-level string.
-data SProxy (sym :: Symbol) = SProxy
-
--- To write generic functions, we create a corresponding type class, and use the
--- type class machinery to infer the correct function based on the representation
--- type.
-class GShow a where
-  gShow :: a -> String
-
--- Now provide instances for GShow for the appropriate representation types.
--- Note: we don't have to implement all instances here.
-
-instance gShowU1 :: GShow U1 where
-  gShow _ = ""
-
-instance gShowSum :: (GShow a, GShow b) => GShow (a + b) where
-  gShow (Inl a) = gShow a
-  gShow (Inr b) = gShow b
-
-instance gShowProduct :: (GShow a, GShow b) => GShow (a * b) where
-  gShow (Product a b) = gShow a <> gShow b
-
-instance gShowCtor :: (KnownSymbol ctor, GShow a) => GShow (Ctor ctor a) where
-  gShow (Ctor a) = "(" <> symbol (SProxy :: SProxy ctor) <> gShow a <> ")"
-
-instance gShowK :: Show a => GShow (K a) where
-  gShow (K a) = " " <> show a
-
--- Now we can implement a generic show function which uses the GShow instance
--- on the representation type.
-genericShow :: forall a repr. (Generic a repr, GShow repr) => a -> String
-genericShow x = gShow (to x)
-
--- Note how the required instance here is Show a, and not Generic a.
--- This allows us to use generic programming on a wider variety of types
--- (including types which contain foreign types) than we can use now.
-instance showList :: Show a => Show (List a) where
-  show xs = genericShow xs -- (we need to eta expand here to avoid stack overflow
-                           -- due to recursion implicit in the instance lookup)
-
--- Another example: Eq
-
-class GEq a where
-  gEq :: a -> a -> Boolean
-
-instance gEqU1 :: GEq U1 where
-  gEq _ _ = true
-
-instance gEqSum :: (GEq a, GEq b) => GEq (a + b) where
-  gEq (Inl a1) (Inl a2) = gEq a1 a2
-  gEq (Inr b1) (Inr b2) = gEq b1 b2
-  gEq _ _ = false
-
-instance gEqProduct :: (GEq a, GEq b) => GEq (a * b) where
-  gEq (Product a1 b1) (Product a2 b2) = gEq a1 a2 && gEq b1 b2
-
-instance gEqCtor :: (KnownSymbol ctor, GEq a) => GEq (Ctor ctor a) where
-  gEq (Ctor a1) (Ctor a2) = gEq a1 a2
-
-instance gEqK :: Eq a => GEq (K a) where
-  gEq (K a1) (K a2) = a1 == a2
-
-genericEq :: forall a repr. (Generic a repr, GEq repr) => a -> a -> Boolean
-genericEq x y = gEq (to x) (to y)
-
-instance eqList :: Eq a => Eq (List a) where
-  eq xs ys = genericEq xs ys
-
-main :: Eff (console :: CONSOLE) Unit
-main = do
-  logShow (Cons 1 Nil)
-  logShow (Cons 1 (Cons 2 Nil))
-  logShow (Cons 'x' (Cons 'y' (Cons 'z' Nil)))
-
-  logShow (Cons 1 (Cons 2 Nil) == Cons 1 (Cons 2 Nil))
-  logShow (Cons 1 (Cons 2 Nil) == Cons 1 Nil)
-
-  log "Done"
diff --git a/examples/passing/GenericsRep.purs b/examples/passing/GenericsRep.purs
new file mode 100644
--- /dev/null
+++ b/examples/passing/GenericsRep.purs
@@ -0,0 +1,53 @@
+module Main where
+
+import Prelude
+import Control.Monad.Eff (Eff)
+import Control.Monad.Eff.Console (CONSOLE, log, logShow)
+import Data.Generic.Rep (class Generic)
+import Data.Generic.Rep.Eq (genericEq)
+
+data X a = X a 
+	 
+derive instance genericX :: Generic (X a) _
+
+instance eqX :: Eq a => Eq (X a) where
+  eq xs ys = genericEq xs ys
+
+data Y a = Y | Z a (Y a)
+
+derive instance genericY :: Generic (Y a) _
+
+instance eqY :: Eq a => Eq (Y a) where
+  eq xs ys = genericEq xs ys
+
+data Z
+
+derive instance genericZ :: Generic Z _
+
+instance eqZ :: Eq Z where
+  eq x y = genericEq x y
+
+newtype W = W { x :: Int, y :: String }
+
+derive instance genericW :: Generic W _
+
+instance eqW :: Eq W where
+  eq x y = genericEq x y
+
+data V = V { x :: Int } { x :: Int }
+ 
+derive instance genericV :: Generic V _
+
+instance eqV :: Eq V where
+  eq x y = genericEq x y
+
+main :: Eff (console :: CONSOLE) Unit
+main = do
+  logShow (X 0 == X 1)
+  logShow (X 1 == X 1)
+  logShow (Z 1 Y == Z 1 Y)
+  logShow (Z 1 Y == Y)
+  logShow (Y == Y :: Y Z)
+  logShow (W { x: 0, y: "A" } == W { x: 0, y: "A" })
+  logShow (V { x: 0 } { x: 0 } == V { x: 0 } { x: 0 })
+  log "Done"
diff --git a/examples/passing/RowsInInstanceContext.purs b/examples/passing/RowsInInstanceContext.purs
new file mode 100644
--- /dev/null
+++ b/examples/passing/RowsInInstanceContext.purs
@@ -0,0 +1,25 @@
+module Main where
+
+import Prelude
+import Control.Monad.Eff (Eff)
+import Control.Monad.Eff.Console (CONSOLE, log)
+import Data.Newtype (class Newtype, unwrap)
+
+class TypeEquals a b | a -> b, b -> a where
+  coerce :: a -> b
+  coerceBack :: b -> a
+
+instance refl :: TypeEquals a a where
+  coerce = id
+  coerceBack = id
+
+newtype RecordNewtype = RecordNewtype { x :: String }
+
+instance newtypeRecordNewtype ::
+  TypeEquals inner { x :: String }
+    => Newtype RecordNewtype inner where
+  wrap = RecordNewtype <<< coerce
+  unwrap (RecordNewtype rec) = coerceBack rec
+
+main :: Eff (console :: CONSOLE) Unit
+main = log (unwrap (RecordNewtype { x: "Done" })).x
diff --git a/examples/warning/2383.purs b/examples/warning/2383.purs
new file mode 100644
--- /dev/null
+++ b/examples/warning/2383.purs
@@ -0,0 +1,12 @@
+-- | This specifically shouldn't warn about `x` being shadowed in `main`
+-- | See https://github.com/purescript/purescript/issues/2383
+module Main where
+
+import Prelude
+
+import Control.Monad.Eff (Eff)
+
+main :: Eff () Unit
+main = do
+  x <- let x = pure unit in x
+  pure unit
diff --git a/examples/warning/2411.purs b/examples/warning/2411.purs
new file mode 100644
--- /dev/null
+++ b/examples/warning/2411.purs
@@ -0,0 +1,15 @@
+-- @shouldWarnWith ShadowedName
+module Main where
+
+import Prelude
+
+import Control.Monad.Eff (Eff)
+
+test :: forall m. Monad m => Int -> m Unit
+test x =
+  let x = unit
+  in pure x
+
+main :: Eff () Unit
+main = test 42
+
diff --git a/hierarchy/Main.hs b/hierarchy/Main.hs
--- a/hierarchy/Main.hs
+++ b/hierarchy/Main.hs
@@ -33,7 +33,7 @@
 import System.FilePath.Glob (glob)
 import System.Exit (exitFailure, exitSuccess)
 import System.IO (hPutStr, stderr)
-import System.IO.UTF8 (readUTF8File)
+import System.IO.UTF8 (readUTF8FileT)
 
 import qualified Language.PureScript as P
 import qualified Paths_purescript as Paths
@@ -60,7 +60,7 @@
 
 readInput :: [FilePath] -> IO (Either P.MultipleErrors [P.Module])
 readInput paths = do
-  content <- mapM (\path -> (path, ) <$> readUTF8File path) paths
+  content <- mapM (\path -> (path, ) <$> readUTF8FileT path) paths
   return $ map snd <$> P.parseModulesFromFiles id content
 
 compile :: HierarchyOptions -> IO ()
diff --git a/psc-docs/Main.hs b/psc-docs/Main.hs
--- a/psc-docs/Main.hs
+++ b/psc-docs/Main.hs
@@ -7,6 +7,7 @@
 import Control.Arrow (first, second)
 import Control.Category ((>>>))
 import Control.Monad.Writer
+import Data.Text (Text)
 import Data.Function (on)
 import Data.List
 import Data.Maybe (fromMaybe)
@@ -20,7 +21,7 @@
 import qualified Paths_purescript as Paths
 import System.Exit (exitFailure)
 import System.IO (hPutStrLn, hPrint, hSetEncoding, stderr, stdout, utf8)
-import System.IO.UTF8 (readUTF8File)
+import System.IO.UTF8 (readUTF8FileT)
 import System.Directory (createDirectoryIfMissing)
 import System.FilePath (takeDirectory)
 import System.FilePath.Glob (glob)
@@ -139,8 +140,8 @@
   ldump :: [String] -> IO ()
   ldump = mapM_ putStrLn
 
-parseFile :: FilePath -> IO (FilePath, String)
-parseFile input = (,) input <$> readUTF8File input
+parseFile :: FilePath -> IO (FilePath, Text)
+parseFile input = (,) input <$> readUTF8FileT input
 
 inputFile :: Parser FilePath
 inputFile = strArgument $
@@ -235,16 +236,16 @@
   PP.vcat $ map PP.text
     [ "Examples:"
     , "  print documentation for Data.List to stdout:"
-    , "    psc-docs \"src/**/*.purs\" \"bower_components/*/src/**/*.purs\" \\"
+    , "    psc-docs \"src/**/*.purs\" \".psc-package/*/*/*/src/**/*.purs\" \\"
     , "      --docgen Data.List"
     , ""
     , "  write documentation for Data.List to docs/Data.List.md:"
-    , "    psc-docs \"src/**/*.purs\" \"bower_components/*/src/**/*.purs\" \\"
+    , "    psc-docs \"src/**/*.purs\" \".psc-package/*/*/*/src/**/*.purs\" \\"
     , "      --docgen Data.List:docs/Data.List.md"
     , ""
     , "  write documentation for Data.List to docs/Data.List.md, and"
     , "  documentation for Data.List.Lazy to docs/Data.List.Lazy.md:"
-    , "    psc-docs \"src/**/*.purs\" \"bower_components/*/src/**/*.purs\" \\"
+    , "    psc-docs \"src/**/*.purs\" \".psc-package/*/*/*/src/**/*.purs\" \\"
     , "      --docgen Data.List:docs/Data.List.md \\"
     , "      --docgen Data.List.Lazy:docs/Data.List.Lazy.md"
     ]
diff --git a/psc-package/Main.hs b/psc-package/Main.hs
new file mode 100644
--- /dev/null
+++ b/psc-package/Main.hs
@@ -0,0 +1,259 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TupleSections #-}
+
+module Main where
+
+import qualified Data.Aeson as Aeson
+import           Data.Aeson.Encode.Pretty
+import           Data.Foldable (fold, for_, traverse_)
+import           Data.List (nub)
+import qualified Data.Map as Map
+import           Data.Maybe (mapMaybe)
+import qualified Data.Set as Set
+import           Data.Text (pack)
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.Builder as TB
+import           Data.Text.Encoding (encodeUtf8)
+import           Data.Traversable (for)
+import           Data.Version (showVersion)
+import qualified Filesystem.Path.CurrentOS as Path
+import           GHC.Generics (Generic)
+import qualified Options.Applicative as Opts
+import qualified Paths_purescript as Paths
+import qualified System.IO as IO
+import           Turtle hiding (fold)
+
+packageFile :: Path.FilePath
+packageFile = "psc-package.json"
+
+data PackageConfig = PackageConfig
+  { name    :: Text
+  , depends :: [Text]
+  , set     :: Text
+  , source  :: Text
+  } deriving (Show, Generic, Aeson.FromJSON, Aeson.ToJSON)
+
+pathToTextUnsafe :: Turtle.FilePath -> Text
+pathToTextUnsafe = either (error "Path.toText failed") id . Path.toText
+
+defaultPackage :: Text -> PackageConfig
+defaultPackage pkgName =
+  PackageConfig { name    = pkgName
+                , depends = [ "prelude" ]
+                , set     = "psc-" <> pack (showVersion Paths.version)
+                , source  = "https://github.com/purescript/package-sets.git"
+                }
+
+readPackageFile :: IO PackageConfig
+readPackageFile = do
+  exists <- testfile packageFile
+  unless exists $ do
+    echo "psc-package.json does not exist"
+    exit (ExitFailure 1)
+  mpkg <- Aeson.decodeStrict . encodeUtf8 <$> readTextFile packageFile
+  case mpkg of
+    Nothing -> do
+      echo "Unable to parse psc-package.json"
+      exit (ExitFailure 1)
+    Just pkg -> return pkg
+
+encodePrettyToText :: Aeson.ToJSON json => json -> Text
+encodePrettyToText =
+    TL.toStrict
+    . TB.toLazyText
+    . encodePrettyToTextBuilder' config
+  where
+    config = defConfig
+               { confCompare =
+                   keyOrder [ "name"
+                            , "set"
+                            , "source"
+                            , "depends"
+                            ]
+               }
+
+writePackageFile :: PackageConfig -> IO ()
+writePackageFile =
+  writeTextFile packageFile
+  . encodePrettyToText
+
+data PackageInfo = PackageInfo
+  { repo         :: Text
+  , version      :: Text
+  , dependencies :: [Text]
+  } deriving (Show, Eq, Generic, Aeson.FromJSON, Aeson.ToJSON)
+
+type PackageSet = Map.Map Text PackageInfo
+
+cloneShallow
+  :: Text
+  -- ^ repo
+  -> Text
+  -- ^ branch/tag
+  -> Turtle.FilePath
+  -- ^ target directory
+  -> IO ExitCode
+cloneShallow from ref into =
+  proc "git"
+       [ "clone"
+       , "-q"
+       , "-c", "advice.detachedHead=false"
+       , "--depth", "1"
+       , "-b", ref
+       , from
+       , pathToTextUnsafe into
+       ] empty .||. exit (ExitFailure 1)
+
+getPackageSet :: PackageConfig -> IO ()
+getPackageSet PackageConfig{ source, set } = do
+  let pkgDir = ".psc-package" </> fromText set </> ".set"
+  exists <- testdir pkgDir
+  unless exists . void $ cloneShallow source set pkgDir
+
+readPackageSet :: PackageConfig -> IO PackageSet
+readPackageSet PackageConfig{ set } = do
+  let dbFile = ".psc-package" </> fromText set </> ".set" </> "packages.json"
+  exists <- testfile dbFile
+  unless exists $ do
+    echo "packages.json does not exist"
+    exit (ExitFailure 1)
+  mdb <- Aeson.decodeStrict . encodeUtf8 <$> readTextFile dbFile
+  case mdb of
+    Nothing -> do
+      echo "Unable to parse packages.json"
+      exit (ExitFailure 1)
+    Just db -> return db
+
+installOrUpdate :: PackageConfig -> Text -> PackageInfo -> IO ()
+installOrUpdate PackageConfig{ set } pkgName PackageInfo{ repo, version } = do
+  let pkgDir = ".psc-package" </> fromText set </> fromText pkgName </> fromText version
+  exists <- testdir pkgDir
+  unless exists . void $ cloneShallow repo version pkgDir
+
+getTransitiveDeps :: PackageSet -> [Text] -> IO [(Text, PackageInfo)]
+getTransitiveDeps db depends = do
+  pkgs <- for depends $ \pkg ->
+    case Map.lookup pkg db of
+      Nothing -> do
+        echo ("Package " <> pkg <> " does not exist in package set")
+        exit (ExitFailure 1)
+      Just PackageInfo{ dependencies } -> return (pkg : dependencies)
+  let unique = Set.toList (foldMap Set.fromList pkgs)
+  return (mapMaybe (\name -> fmap (name, ) (Map.lookup name db)) unique)
+
+updateImpl :: PackageConfig -> IO ()
+updateImpl config@PackageConfig{ depends } = do
+  getPackageSet config
+  db <- readPackageSet config
+  trans <- getTransitiveDeps db depends
+  echo ("Updating " <> pack (show (length trans)) <> " packages...")
+  for_ trans $ \(pkgName, pkg) -> do
+    echo ("Updating " <> pkgName)
+    installOrUpdate config pkgName pkg
+
+initialize :: IO ()
+initialize = do
+  exists <- testfile "psc-package.json"
+  when exists $ do
+    echo "psc-package.json already exists"
+    exit (ExitFailure 1)
+  echo "Initializing new project in current directory"
+  pkgName <- pathToTextUnsafe . Path.filename <$> pwd
+  let pkg = defaultPackage pkgName
+  writePackageFile pkg
+  updateImpl pkg
+
+update :: IO ()
+update = do
+  pkg <- readPackageFile
+  updateImpl pkg
+  echo "Update complete"
+
+install :: String -> IO ()
+install pkgName = do
+  pkg <- readPackageFile
+  let pkg' = pkg { depends = nub (pack pkgName : depends pkg) }
+  updateImpl pkg'
+  writePackageFile pkg'
+  echo "psc-package.json file was updated"
+
+listDependencies :: IO ()
+listDependencies = do
+  pkg@PackageConfig{ depends } <- readPackageFile
+  db <- readPackageSet pkg
+  trans <- getTransitiveDeps db depends
+  traverse_ (echo . fst) trans
+
+getSourcePaths :: PackageConfig -> PackageSet -> [Text] -> IO [Turtle.FilePath]
+getSourcePaths PackageConfig{..} db pkgNames = do
+  trans <- getTransitiveDeps db pkgNames
+  let paths = [ ".psc-package"
+                </> fromText set
+                </> fromText pkgName
+                </> fromText version
+                </> "src" </> "**" </> "*.purs"
+              | (pkgName, PackageInfo{ version }) <- trans
+              ]
+  return paths
+
+listSourcePaths :: IO ()
+listSourcePaths = do
+  pkg@PackageConfig{ depends } <- readPackageFile
+  db <- readPackageSet pkg
+  paths <- getSourcePaths pkg db depends
+  traverse_ (echo . pathToTextUnsafe) paths
+
+exec :: Text -> IO ()
+exec exeName = do
+  pkg@PackageConfig{..} <- readPackageFile
+  db <- readPackageSet pkg
+  paths <- getSourcePaths pkg db depends
+  procs exeName
+        (map pathToTextUnsafe ("src" </> "**" </> "*.purs" : paths))
+        empty
+
+main :: IO ()
+main = do
+    IO.hSetEncoding IO.stdout IO.utf8
+    IO.hSetEncoding IO.stderr IO.utf8
+    cmd <- Opts.execParser opts
+    cmd
+  where
+    opts        = Opts.info (versionInfo <*> Opts.helper <*> commands) infoModList
+    infoModList = Opts.fullDesc <> headerInfo <> footerInfo
+    headerInfo  = Opts.progDesc "Manage package dependencies"
+    footerInfo  = Opts.footer $ "psc-package " ++ showVersion Paths.version
+
+    versionInfo :: Parser (a -> a)
+    versionInfo = Opts.abortOption (Opts.InfoMsg (showVersion Paths.version)) $
+      Opts.long "version" <> Opts.help "Show the version number" <> Opts.hidden
+
+    commands :: Parser (IO ())
+    commands = (Opts.subparser . fold)
+        [ Opts.command "init"
+            (Opts.info (pure initialize)
+            (Opts.progDesc "Initialize a new package"))
+        , Opts.command "update"
+            (Opts.info (pure update)
+            (Opts.progDesc "Update dependencies"))
+        , Opts.command "install"
+            (Opts.info (install <$> pkg)
+            (Opts.progDesc "Install the named package"))
+        , Opts.command "build"
+            (Opts.info (pure (exec "psc"))
+            (Opts.progDesc "Build the current package and dependencies"))
+        , Opts.command "dependencies"
+            (Opts.info (pure listDependencies)
+            (Opts.progDesc "List all (transitive) dependencies for the current package"))
+        , Opts.command "sources"
+            (Opts.info (pure listSourcePaths)
+            (Opts.progDesc "List all (active) source paths for dependencies"))
+        ]
+      where
+        pkg = Opts.strArgument $
+             Opts.metavar "PACKAGE"
+          <> Opts.help "The name of the package to install"
diff --git a/psc/Main.hs b/psc/Main.hs
--- a/psc/Main.hs
+++ b/psc/Main.hs
@@ -15,6 +15,7 @@
 import qualified Data.ByteString.Lazy as B
 import qualified Data.ByteString.UTF8 as BU8
 import qualified Data.Map as M
+import           Data.Text (Text)
 import           Data.Version (showVersion)
 
 import qualified Language.PureScript as P
@@ -29,7 +30,7 @@
 import           System.Exit (exitSuccess, exitFailure)
 import           System.FilePath.Glob (glob)
 import           System.IO (hSetEncoding, hPutStrLn, stdout, stderr, utf8)
-import           System.IO.UTF8
+import           System.IO.UTF8 (readUTF8FileT)
 
 data PSCMakeOptions = PSCMakeOptions
   { pscmInput        :: [FilePath]
@@ -85,8 +86,8 @@
     return paths
   concatMapM f = fmap concat . mapM f
 
-readInput :: [FilePath] -> IO [(FilePath, String)]
-readInput inputFiles = forM inputFiles $ \inFile -> (inFile, ) <$> readUTF8File inFile
+readInput :: [FilePath] -> IO [(FilePath, Text)]
+readInput inputFiles = forM inputFiles $ \inFile -> (inFile, ) <$> readUTF8FileT inFile
 
 inputFile :: Parser FilePath
 inputFile = strArgument $
diff --git a/psci/Main.hs b/psci/Main.hs
--- a/psci/Main.hs
+++ b/psci/Main.hs
@@ -63,17 +63,10 @@
 
 -- | Command line options
 data PSCiOptions = PSCiOptions
-  { psciMultiLineMode     :: Bool
-  , psciInputFile         :: [FilePath]
+  { psciInputFile         :: [FilePath]
   , psciBackend           :: Backend
   }
 
-multiLineMode :: Opts.Parser Bool
-multiLineMode = Opts.switch $
-     Opts.long "multi-line-mode"
-  <> Opts.short 'm'
-  <> Opts.help "Run in multi-line mode (use ^D to terminate commands)"
-
 inputFile :: Opts.Parser FilePath
 inputFile = Opts.strArgument $
      Opts.metavar "FILE"
@@ -100,8 +93,7 @@
   <|> (nodeBackend <$> nodeFlagsFlag)
 
 psciOptions :: Opts.Parser PSCiOptions
-psciOptions = PSCiOptions <$> multiLineMode
-                          <*> many inputFile
+psciOptions = PSCiOptions <$> many inputFile
                           <*> backend
 
 version :: Opts.Parser (a -> a)
@@ -119,17 +111,20 @@
       footerInfo  = Opts.footer $ "psci " ++ showVersion Paths.version
 
 -- | Parses the input and returns either a command, or an error as a 'String'.
-getCommand :: forall m. MonadException m => Bool -> InputT m (Either String (Maybe Command))
-getCommand singleLineMode = handleInterrupt (return (Right Nothing)) $ do
-  firstLine <- withInterrupt $ getInputLine "> "
-  case firstLine of
+getCommand :: forall m. MonadException m => InputT m (Either String (Maybe Command))
+getCommand = handleInterrupt (return (Right Nothing)) $ do
+  line <- withInterrupt $ getInputLine "> "
+  case line of
     Nothing -> return (Right (Just QuitPSCi)) -- Ctrl-D when input is empty
     Just "" -> return (Right Nothing)
-    Just s | singleLineMode || head s == ':' -> return . fmap Just $ parseCommand s
-    Just s -> fmap Just . parseCommand <$> go [s]
+    Just s  -> return . fmap Just $ parseCommand s
+
+pasteMode :: forall m. MonadException m => InputT m (Either String Command)
+pasteMode =
+    parseCommand <$> go []
   where
     go :: [String] -> InputT m String
-    go ls = maybe (return . unlines $ reverse ls) (go . (:ls)) =<< getInputLine "  "
+    go ls = maybe (return . unlines $ reverse ls) (go . (:ls)) =<< getInputLine "… "
 
 -- | Make a JavaScript bundle for the browser.
 bundle :: IO (Either Bundle.ErrorMessage String)
@@ -349,16 +344,28 @@
 
                     go :: state -> InputT (StateT PSCiState (ReaderT PSCiConfig IO)) ()
                     go state = do
-                      c <- getCommand (not psciMultiLineMode)
+                      c <- getCommand
                       case c of
                         Left err -> outputStrLn err >> go state
                         Right Nothing -> go state
+                        Right (Just PasteLines) -> do
+                          c' <- pasteMode
+                          case c' of
+                            Left err -> outputStrLn err >> go state
+                            Right c'' -> handleCommandWithInterrupts state c''
                         Right (Just QuitPSCi) -> do
                           outputStrLn quitMessage
                           liftIO $ shutdown state
-                        Right (Just c') -> do
-                          handleInterrupt (outputStrLn "Interrupted.")
-                                          (withInterrupt (lift (handleCommand (liftIO . eval state) (liftIO (reload state)) c')))
-                          go state
+                        Right (Just c') -> handleCommandWithInterrupts state c'
+
+                    handleCommandWithInterrupts
+                      :: state
+                      -> Command
+                      -> InputT (StateT PSCiState (ReaderT PSCiConfig IO)) ()
+                    handleCommandWithInterrupts state cmd = do
+                      handleInterrupt (outputStrLn "Interrupted.")
+                                      (withInterrupt (lift (handleCommand (liftIO . eval state) (liftIO (reload state)) cmd)))
+                      go state
+
                 putStrLn prologueMessage
                 setup >>= runner . go
diff --git a/purescript.cabal b/purescript.cabal
--- a/purescript.cabal
+++ b/purescript.cabal
@@ -1,5 +1,5 @@
 name: purescript
-version: 0.10.1
+version: 0.10.2
 cabal-version: >=1.8
 build-type: Simple
 license: BSD3
@@ -55,6 +55,8 @@
                   , examples/passing/TypeWithoutParens/*.purs
                   , examples/failing/*.purs
                   , examples/failing/1733/*.purs
+                  , examples/failing/2378/*.purs
+                  , examples/failing/2379/*.purs
                   , examples/failing/ConflictingExports/*.purs
                   , examples/failing/ConflictingImports/*.purs
                   , examples/failing/ConflictingImports2/*.purs
@@ -119,9 +121,10 @@
                    fsnotify >= 0.2.1,
                    Glob >= 0.7 && < 0.8,
                    haskeline >= 0.7.0.0,
-                   http-client >= 0.4.30 && <0.6,
+                   http-client >= 0.4.30 && <0.5,
                    http-types -any,
                    language-javascript == 0.6.*,
+                   lens == 4.*,
                    lifted-base >= 0.2.3 && < 0.2.4,
                    monad-control >= 1.0.0.0 && < 1.1,
                    monad-logger >= 0.3 && < 0.4,
@@ -237,6 +240,7 @@
                      Language.PureScript.TypeChecker.Subsumption
                      Language.PureScript.TypeChecker.Synonyms
                      Language.PureScript.TypeChecker.Types
+                     Language.PureScript.TypeChecker.TypeSearch
                      Language.PureScript.TypeChecker.Unify
                      Language.PureScript.TypeClassDictionaries
                      Language.PureScript.Types
@@ -330,6 +334,7 @@
                    mtl -any,
                    optparse-applicative >= 0.12.1,
                    parsec -any,
+                   text -any,
                    time -any,
                    transformers -any,
                    transformers-compat -any,
@@ -383,6 +388,7 @@
                    optparse-applicative >= 0.12.1,
                    process -any,
                    split -any,
+                   text -any,
                    transformers -any,
                    transformers-compat -any
     main-is: Main.hs
@@ -406,6 +412,24 @@
     hs-source-dirs: psc-publish
     ghc-options: -Wall -O2
 
+executable psc-package
+    build-depends: base >=4 && <5,
+                   purescript -any,
+                   aeson -any,
+                   aeson-pretty -any,
+                   bytestring -any,
+                   containers -any,
+                   foldl -any,
+                   optparse-applicative -any,
+                   system-filepath -any,
+                   text -any,
+                   turtle -any
+    main-is: Main.hs
+    other-modules: Paths_purescript
+    buildable: True
+    hs-source-dirs: psc-package
+    ghc-options: -Wall -O2
+
 executable psc-hierarchy
     build-depends: base >=4 && <5,
                    purescript -any,
@@ -415,7 +439,8 @@
                    mtl -any,
                    optparse-applicative >= 0.12.1,
                    parsec -any,
-                   process -any
+                   process -any,
+                   text -any
     main-is: Main.hs
     other-modules: Paths_purescript
     buildable: True
diff --git a/src/Control/Monad/Supply.hs b/src/Control/Monad/Supply.hs
--- a/src/Control/Monad/Supply.hs
+++ b/src/Control/Monad/Supply.hs
@@ -7,6 +7,7 @@
 
 import Prelude.Compat
 
+import Control.Applicative
 import Control.Monad.Error.Class (MonadError(..))
 import Control.Monad.Reader
 import Control.Monad.State
@@ -15,7 +16,7 @@
 import Data.Functor.Identity
 
 newtype SupplyT m a = SupplyT { unSupplyT :: StateT Integer m a }
-  deriving (Functor, Applicative, Monad, MonadTrans, MonadError e, MonadWriter w, MonadReader r)
+  deriving (Functor, Applicative, Monad, MonadTrans, MonadError e, MonadWriter w, MonadReader r, Alternative, MonadPlus)
 
 runSupplyT :: Integer -> SupplyT m a -> m (a, Integer)
 runSupplyT n = flip runStateT n . unSupplyT
diff --git a/src/Control/Monad/Supply/Class.hs b/src/Control/Monad/Supply/Class.hs
--- a/src/Control/Monad/Supply/Class.hs
+++ b/src/Control/Monad/Supply/Class.hs
@@ -1,6 +1,9 @@
 -- |
 -- A class for monads supporting a supply of fresh names
 --
+
+{-# LANGUAGE DefaultSignatures #-}
+
 module Control.Monad.Supply.Class where
 
 import Prelude.Compat
@@ -11,18 +14,21 @@
 
 class Monad m => MonadSupply m where
   fresh :: m Integer
+  peek :: m Integer
+  default fresh :: MonadTrans t => t m Integer
+  fresh = lift fresh
+  default peek :: MonadTrans t => t m Integer
+  peek = lift peek
 
 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
+  peek = SupplyT get
 
-instance (Monoid w, MonadSupply m) => MonadSupply (WriterT w m) where
-  fresh = lift fresh
+instance MonadSupply m => MonadSupply (StateT s m)
+instance (Monoid w, MonadSupply m) => MonadSupply (WriterT w m)
 
 freshName :: MonadSupply m => m String
 freshName = fmap (('$' :) . show) fresh
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
@@ -29,6 +29,15 @@
 -- | A map of locally-bound names in scope.
 type Context = [(Ident, Type)]
 
+-- | Holds the data necessary to do type directed search for typed holes
+data TypeSearch
+  = TSBefore Environment
+  -- ^ An Environment captured for later consumption by type directed search
+  | TSAfter [(Qualified Ident, Type)]
+  -- ^ Results of applying type directed search to the previously captured
+  -- Environment
+  deriving Show
+
 -- | A type of error messages
 data SimpleErrorMessage
   = ErrorParsingFFIModule FilePath (Maybe Bundle.ErrorMessage)
@@ -74,6 +83,8 @@
   | ConstrainedTypeUnified Type Type
   | OverlappingInstances (Qualified (ProperName 'ClassName)) [Type] [Qualified Ident]
   | NoInstanceFound Constraint
+  | AmbiguousTypeVariables Type Constraint
+  | UnknownClass (Qualified (ProperName 'ClassName))
   | PossiblyInfiniteInstance (Qualified (ProperName 'ClassName)) [Type]
   | CannotDerive (Qualified (ProperName 'ClassName)) [Type]
   | InvalidNewtypeInstance (Qualified (ProperName 'ClassName)) [Type]
@@ -99,7 +110,7 @@
   | ShadowedTypeVar String
   | UnusedTypeVar String
   | WildcardInferredType Type Context
-  | HoleInferredType String Type Context
+  | HoleInferredType String Type Context TypeSearch
   | MissingTypeDeclaration Ident Type
   | OverlappingPattern [[Binder]] Bool
   | IncompleteExhaustivityCheck
@@ -123,7 +134,7 @@
   | DeprecatedRequirePath
   | CannotGeneralizeRecursiveFunction Ident Type
   | CannotDeriveNewtypeForData (ProperName 'TypeName)
-  | NonWildcardNewtypeInstance (ProperName 'TypeName)
+  | ExpectedWildcard (ProperName 'TypeName)
   deriving (Show)
 
 -- | Error message hints, providing more detailed information about failure.
@@ -249,6 +260,31 @@
   (PositionedDeclarationRef _ _ r) == r' = r == r'
   r == (PositionedDeclarationRef _ _ r') = r == r'
   _ == _ = False
+
+-- enable sorting lists of explicitly imported refs when suggesting imports in linting, IDE, etc.
+-- not an Ord because this implementation is not consistent with its Eq instance.
+-- think of it as a notion of contextual, not inherent, ordering.
+compDecRef :: DeclarationRef -> DeclarationRef -> Ordering
+compDecRef (TypeRef name _) (TypeRef name' _) = compare name name'
+compDecRef (TypeOpRef name) (TypeOpRef name') = compare name name'
+compDecRef (ValueRef ident) (ValueRef ident') = compare ident ident'
+compDecRef (ValueOpRef name) (ValueOpRef name') = compare name name'
+compDecRef (TypeClassRef name) (TypeClassRef name') = compare name name'
+compDecRef (TypeInstanceRef ident) (TypeInstanceRef ident') = compare ident ident'
+compDecRef (ModuleRef name) (ModuleRef name') = compare name name'
+compDecRef (ReExportRef name _) (ReExportRef name' _) = compare name name'
+compDecRef (PositionedDeclarationRef _ _ ref) ref' = compDecRef ref ref'
+compDecRef ref (PositionedDeclarationRef _ _ ref') = compDecRef ref ref'
+compDecRef ref ref' = compare
+  (orderOf ref) (orderOf ref')
+    where
+      orderOf :: DeclarationRef -> Int
+      orderOf (TypeClassRef _) = 0
+      orderOf (TypeOpRef _) = 1
+      orderOf (TypeRef _ _) = 2
+      orderOf (ValueRef _) = 3
+      orderOf (ValueOpRef _) = 4
+      orderOf _ = 5
 
 getTypeRef :: DeclarationRef -> Maybe (ProperName 'TypeName, Maybe [ProperName 'ConstructorName])
 getTypeRef (TypeRef name dctors) = Just (name, dctors)
diff --git a/src/Language/PureScript/AST/Traversals.hs b/src/Language/PureScript/AST/Traversals.hs
--- a/src/Language/PureScript/AST/Traversals.hs
+++ b/src/Language/PureScript/AST/Traversals.hs
@@ -474,7 +474,8 @@
     in foldMap (f'' s') ds
   f' s (ValueDeclaration name _ bs (Right val)) =
     let s' = S.insert name s
-    in foldMap (h'' s') bs <> g'' s' val
+        s'' = S.union s' (S.fromList (concatMap binderNames bs))
+    in foldMap (h'' s') bs <> g'' s'' val
   f' s (ValueDeclaration name _ bs (Left gs)) =
     let s' = S.insert name s
         s'' = S.union s' (S.fromList (concatMap binderNames bs))
@@ -551,7 +552,7 @@
   j' s (DoNotationValue v) = (s, g'' s v)
   j' s (DoNotationBind b v) =
     let s' = S.union (S.fromList (binderNames b)) s
-    in (s', h'' s b <> g'' s' v)
+    in (s', h'' s b <> g'' s v)
   j' s (DoNotationLet ds) =
     let s' = S.union s (S.fromList (mapMaybe getDeclIdent ds))
     in (s', foldMap (f'' s') ds)
@@ -586,3 +587,15 @@
   forValues (DeferredDictionary _ tys) = mconcat (map f tys)
   forValues (TypedValue _ _ ty) = f ty
   forValues _ = mempty
+
+-- |
+-- Map a function over type annotations appearing inside a value
+--
+overTypes :: (Type -> Type) -> Expr -> Expr
+overTypes f = let (_, f', _) = everywhereOnValues id g id in f'
+  where
+  g :: Expr -> Expr
+  g (TypedValue checkTy val t) = TypedValue checkTy val (f t)
+  g (TypeClassDictionary c sco hints) = TypeClassDictionary (mapConstraintArgs (map f) c) sco hints
+  g other = other
+
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
@@ -244,23 +244,23 @@
     ret <- valueToJs val
     return $ JSApp Nothing (JSFunction Nothing Nothing [] (JSBlock Nothing (ds' ++ [JSReturn Nothing ret]))) []
   valueToJs' (Constructor (_, _, _, Just IsNewtype) _ (ProperName ctor) _) =
-    return $ JSVariableIntroduction Nothing ctor (Just $
+    return $ JSVariableIntroduction Nothing (properToJs ctor) (Just $
                 JSObjectLiteral Nothing [("create",
                   JSFunction Nothing Nothing ["value"]
                     (JSBlock Nothing [JSReturn Nothing $ JSVar Nothing "value"]))])
   valueToJs' (Constructor _ _ (ProperName ctor) []) =
-    return $ iife ctor [ JSFunction Nothing (Just ctor) [] (JSBlock Nothing [])
-           , JSAssignment Nothing (JSAccessor Nothing "value" (JSVar Nothing ctor))
-                (JSUnary Nothing JSNew $ JSApp Nothing (JSVar Nothing ctor) []) ]
+    return $ iife (properToJs ctor) [ JSFunction Nothing (Just (properToJs ctor)) [] (JSBlock Nothing [])
+           , JSAssignment Nothing (JSAccessor Nothing "value" (JSVar Nothing (properToJs ctor)))
+                (JSUnary Nothing JSNew $ JSApp Nothing (JSVar Nothing (properToJs ctor)) []) ]
   valueToJs' (Constructor _ _ (ProperName ctor) fields) =
     let constructor =
           let body = [ JSAssignment Nothing (JSAccessor Nothing (identToJs f) (JSVar Nothing "this")) (var f) | f <- fields ]
-          in JSFunction Nothing (Just ctor) (identToJs `map` fields) (JSBlock Nothing body)
+          in JSFunction Nothing (Just (properToJs ctor)) (identToJs `map` fields) (JSBlock Nothing body)
         createFn =
-          let body = JSUnary Nothing JSNew $ JSApp Nothing (JSVar Nothing ctor) (var `map` fields)
+          let body = JSUnary Nothing JSNew $ JSApp Nothing (JSVar Nothing (properToJs ctor)) (var `map` fields)
           in foldr (\f inner -> JSFunction Nothing Nothing [identToJs f] (JSBlock Nothing [JSReturn Nothing inner])) body fields
-    in return $ iife ctor [ constructor
-                          , JSAssignment Nothing (JSAccessor Nothing "create" (JSVar Nothing ctor)) createFn
+    in return $ iife (properToJs ctor) [ constructor
+                          , JSAssignment Nothing (JSAccessor Nothing "create" (JSVar Nothing (properToJs ctor))) createFn
                           ]
 
   iife :: String -> [JS] -> JS
diff --git a/src/Language/PureScript/CodeGen/JS/Common.hs b/src/Language/PureScript/CodeGen/JS/Common.hs
--- a/src/Language/PureScript/CodeGen/JS/Common.hs
+++ b/src/Language/PureScript/CodeGen/JS/Common.hs
@@ -26,10 +26,13 @@
 --  * Symbols are prefixed with '$' followed by a symbol name or their ordinal value.
 --
 identToJs :: Ident -> String
-identToJs (Ident name)
+identToJs (Ident name) = properToJs name
+identToJs (GenIdent _ _) = internalError "GenIdent in identToJs"
+
+properToJs :: String -> String
+properToJs name
   | nameIsJsReserved name || nameIsJsBuiltIn name = "$$" ++ name
   | otherwise = concatMap identCharToString name
-identToJs (GenIdent _ _) = internalError "GenIdent in identToJs"
 
 -- |
 -- Test if a string is a valid JS identifier without escaping.
diff --git a/src/Language/PureScript/CodeGen/JS/Optimizer/Inliner.hs b/src/Language/PureScript/CodeGen/JS/Optimizer/Inliner.hs
--- a/src/Language/PureScript/CodeGen/JS/Optimizer/Inliner.hs
+++ b/src/Language/PureScript/CodeGen/JS/Optimizer/Inliner.hs
@@ -99,15 +99,6 @@
   fnSubtract = (C.dataRing, C.sub)
   intOp ss op x y = JSBinary ss BitwiseOr (JSBinary ss op x y) (JSNumericLiteral ss (Left 0))
 
-inlineNonClassFunction :: (String, String) -> (JS -> JS -> JS) -> JS -> JS
-inlineNonClassFunction (m, op) f = everywhereOnJS convert
-  where
-  convert :: JS -> JS
-  convert (JSApp _ (JSApp _ op' [x]) [y]) | isOp op' = f x y
-  convert other = other
-  isOp (JSAccessor _ op' (JSVar _ m')) = m == m' && op == op'
-  isOp _ = False
-
 inlineCommonOperators :: JS -> JS
 inlineCommonOperators = applyAll $
   [ binary semiringNumber opAdd Add
@@ -159,17 +150,17 @@
   , binary heytingAlgebraBoolean opDisj Or
   , unary  heytingAlgebraBoolean opNot Not
 
-  , binary' C.dataIntBits (C..|.) BitwiseOr
-  , binary' C.dataIntBits (C..&.) BitwiseAnd
-  , binary' C.dataIntBits (C..^.) BitwiseXor
+  , binary' C.dataIntBits C.or BitwiseOr
+  , binary' C.dataIntBits C.and BitwiseAnd
+  , binary' C.dataIntBits C.xor BitwiseXor
   , binary' C.dataIntBits C.shl ShiftLeft
   , binary' C.dataIntBits C.shr ShiftRight
   , binary' C.dataIntBits C.zshr ZeroFillShiftRight
   , unary'  C.dataIntBits C.complement BitwiseNot
 
-  , inlineNonClassFunction (C.dataFunction, C.apply) $ \f x -> JSApp Nothing f [x]
-  , inlineNonClassFunction (C.dataFunction, C.applyFlipped) $ \x f -> JSApp Nothing f [x]
-  , inlineNonClassFunction (C.dataArrayUnsafe, C.unsafeIndex) $ flip (JSIndexer Nothing)
+  , inlineNonClassFunction (isModFn (C.dataFunction, C.apply)) $ \f x -> JSApp Nothing f [x]
+  , inlineNonClassFunction (isModFn (C.dataFunction, C.applyFlipped)) $ \x f -> JSApp Nothing f [x]
+  , inlineNonClassFunction (isModFnWithDict (C.dataArray, C.unsafeIndex)) $ flip (JSIndexer Nothing)
   ] ++
   [ fn | i <- [0..10], fn <- [ mkFn i, runFn i ] ]
   where
@@ -232,6 +223,21 @@
     go 0 acc (JSApp ss runFnN [fn]) | isNFn C.runFn n runFnN && length acc == n = Just (JSApp ss fn acc)
     go m acc (JSApp _ lhs [arg]) = go (m - 1) (arg : acc) lhs
     go _ _   _ = Nothing
+
+  inlineNonClassFunction :: (JS -> Bool) -> (JS -> JS -> JS) -> JS -> JS
+  inlineNonClassFunction p f = everywhereOnJS convert
+    where
+    convert :: JS -> JS
+    convert (JSApp _ (JSApp _ op' [x]) [y]) | p op' = f x y
+    convert other = other
+
+  isModFn :: (String, String) -> JS -> Bool
+  isModFn (m, op) (JSAccessor _ op' (JSVar _ m')) = m == m' && op == op'
+  isModFn _ _ = False
+
+  isModFnWithDict :: (String, String) -> JS -> Bool
+  isModFnWithDict (m, op) (JSApp _ (JSAccessor _ op' (JSVar _ m')) [(JSVar _ _)]) = m == m' && op == op'
+  isModFnWithDict _ _ = False
 
 -- (f <<< g $ x) = f (g x)
 -- (f <<< g)     = \x -> f (g x)
diff --git a/src/Language/PureScript/Constants.hs b/src/Language/PureScript/Constants.hs
--- a/src/Language/PureScript/Constants.hs
+++ b/src/Language/PureScript/Constants.hs
@@ -118,14 +118,14 @@
 unsafeIndex :: String
 unsafeIndex = "unsafeIndex"
 
-(.|.) :: String
-(.|.) = ".|."
+or :: String
+or = "or"
 
-(.&.) :: String
-(.&.) = ".&."
+and :: String
+and = "and"
 
-(.^.) :: String
-(.^.) = ".^."
+xor :: String
+xor = "xor"
 
 (<<<) :: String
 (<<<) = "<<<"
@@ -346,8 +346,8 @@
 prelude :: String
 prelude = "Prelude"
 
-dataArrayUnsafe :: String
-dataArrayUnsafe = "Data_Array_Unsafe"
+dataArray :: String
+dataArray = "Data_Array"
 
 eff :: String
 eff = "Control_Monad_Eff"
diff --git a/src/Language/PureScript/Docs/ParseAndBookmark.hs b/src/Language/PureScript/Docs/ParseAndBookmark.hs
--- a/src/Language/PureScript/Docs/ParseAndBookmark.hs
+++ b/src/Language/PureScript/Docs/ParseAndBookmark.hs
@@ -9,13 +9,12 @@
 import Control.Monad.IO.Class (MonadIO(..))
 
 import qualified Data.Map as M
+import Data.Text (Text)
 
 import Language.PureScript.Docs.Convert (collectBookmarks)
 import Language.PureScript.Docs.Types
 import qualified Language.PureScript as P
-
-import System.IO.UTF8 (readUTF8File)
-
+import System.IO.UTF8 (readUTF8FileT)
 import Web.Bower.PackageMeta (PackageName)
 
 -- |
@@ -45,7 +44,7 @@
 
 parseFiles ::
   (MonadError P.MultipleErrors m) =>
-  [(FileInfo, FilePath)]
+  [(FileInfo, Text)]
   -> m [(FileInfo, P.Module)]
 parseFiles =
   throwLeft . P.parseModulesFromFiles fileInfoToString
@@ -77,10 +76,10 @@
 fileInfoToString (Local fn) = fn
 fileInfoToString (FromDep _ fn) = fn
 
-parseFile :: FilePath -> IO (FilePath, String)
-parseFile input' = (,) input' <$> readUTF8File input'
+parseFile :: FilePath -> IO (FilePath, Text)
+parseFile input' = (,) input' <$> readUTF8FileT input'
 
-parseAs :: (MonadIO m) => (FilePath -> a) -> FilePath -> m (a, String)
+parseAs :: (MonadIO m) => (FilePath -> a) -> FilePath -> m (a, Text)
 parseAs g = fmap (first g) . liftIO . parseFile
 
 getDepsModuleNames :: [InPackage (FilePath, P.Module)] -> M.Map P.ModuleName PackageName
diff --git a/src/Language/PureScript/Environment.hs b/src/Language/PureScript/Environment.hs
--- a/src/Language/PureScript/Environment.hs
+++ b/src/Language/PureScript/Environment.hs
@@ -236,8 +236,8 @@
 
 -- |
 -- The primitive types in the external javascript environment with their
--- associated kinds. There is also a pseudo `Partial` type that corresponds to
--- the class with the same name.
+-- associated kinds. There are also pseudo `Fail` and `Partial` types
+-- that correspond to the classes with the same names.
 --
 primTypes :: M.Map (Qualified (ProperName 'TypeName)) (Kind, TypeKind)
 primTypes =
@@ -257,8 +257,9 @@
     ]
 
 -- |
--- The primitive class map. This just contains to `Partial` class, used as a
--- kind of magic constraint for partial functions.
+-- The primitive class map. This just contains the `Fail` and `Partial`
+-- classes. `Partial` is used as a kind of magic constraint for partial
+-- functions. `Fail` is used for user-defined type errors.
 --
 primClasses :: M.Map (Qualified (ProperName 'ClassName)) TypeClassData
 primClasses =
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
@@ -40,6 +40,7 @@
 import qualified Text.Parsec as P
 import qualified Text.Parsec.Error as PE
 import qualified Text.PrettyPrint.Boxes as Box
+import qualified Language.PureScript.Publish.BoxesHelpers as BoxHelpers
 import Text.Parsec.Error (Message(..))
 
 newtype ErrorSuggestion = ErrorSuggestion String
@@ -117,6 +118,8 @@
   ConstrainedTypeUnified{} -> "ConstrainedTypeUnified"
   OverlappingInstances{} -> "OverlappingInstances"
   NoInstanceFound{} -> "NoInstanceFound"
+  AmbiguousTypeVariables{} -> "AmbiguousTypeVariables"
+  UnknownClass{} -> "UnknownClass"
   PossiblyInfiniteInstance{} -> "PossiblyInfiniteInstance"
   CannotDerive{} -> "CannotDerive"
   InvalidNewtypeInstance{} -> "InvalidNewtypeInstance"
@@ -166,7 +169,7 @@
   DeprecatedRequirePath{} -> "DeprecatedRequirePath"
   CannotGeneralizeRecursiveFunction{} -> "CannotGeneralizeRecursiveFunction"
   CannotDeriveNewtypeForData{} -> "CannotDeriveNewtypeForData"
-  NonWildcardNewtypeInstance{} -> "NonWildcardNewtypeInstance"
+  ExpectedWildcard{} -> "ExpectedWildcard"
 
 -- |
 -- A stack trace for an error
@@ -259,6 +262,7 @@
   gSimple (ExprDoesNotHaveType e t) = ExprDoesNotHaveType e <$> f t
   gSimple (InvalidInstanceHead t) = InvalidInstanceHead <$> f t
   gSimple (NoInstanceFound con) = NoInstanceFound <$> overConstraintArgs (traverse f) con
+  gSimple (AmbiguousTypeVariables t con) = AmbiguousTypeVariables <$> (f t) <*> pure con
   gSimple (OverlappingInstances cl ts insts) = OverlappingInstances cl <$> traverse f ts <*> pure insts
   gSimple (PossiblyInfiniteInstance cl ts) = PossiblyInfiniteInstance cl <$> traverse f ts
   gSimple (CannotDerive cl ts) = CannotDerive cl <$> traverse f ts
@@ -266,7 +270,7 @@
   gSimple (ExpectedType ty k) = ExpectedType <$> f ty <*> pure k
   gSimple (OrphanInstance nm cl ts) = OrphanInstance nm cl <$> traverse f ts
   gSimple (WildcardInferredType ty ctx) = WildcardInferredType <$> f ty <*> traverse (sndM f) ctx
-  gSimple (HoleInferredType name ty ctx) = HoleInferredType name <$> f ty <*> traverse (sndM f) ctx
+  gSimple (HoleInferredType name ty ctx env) = HoleInferredType name <$> f ty <*> traverse (sndM f) ctx  <*> gTypeSearch env
   gSimple (MissingTypeDeclaration nm ty) = MissingTypeDeclaration nm <$> f ty
   gSimple (CannotGeneralizeRecursiveFunction nm ty) = CannotGeneralizeRecursiveFunction nm <$> f ty
   gSimple other = pure other
@@ -280,6 +284,9 @@
   gHint (ErrorSolvingConstraint con) = ErrorSolvingConstraint <$> overConstraintArgs (traverse f) con
   gHint other = pure other
 
+  gTypeSearch (TSBefore env) = pure (TSBefore env)
+  gTypeSearch (TSAfter result) = TSAfter <$> traverse (traverse f) result
+
 wikiUri :: ErrorMessage -> String
 wikiUri e = "https://github.com/purescript/purescript/wiki/Error-Code-" ++ errorCode e
 
@@ -287,19 +294,19 @@
 -- WildcardInferredType - source span not small enough
 -- DuplicateSelectiveImport - would require 2 ranges to remove and 1 insert
 errorSuggestion :: SimpleErrorMessage -> Maybe ErrorSuggestion
-errorSuggestion err = case err of
-  UnusedImport{} -> emptySuggestion
-  DuplicateImport{} -> emptySuggestion
-  UnusedExplicitImport mn _ qual refs -> suggest $ importSuggestion mn refs qual
-  UnusedDctorImport mn _ qual refs -> suggest $ importSuggestion mn refs qual
-  UnusedDctorExplicitImport mn _ _ qual refs -> suggest $ importSuggestion mn refs qual
-  ImplicitImport mn refs -> suggest $ importSuggestion mn refs Nothing
-  ImplicitQualifiedImport mn asModule refs -> suggest $ importSuggestion mn refs (Just asModule)
-  HidingImport mn refs -> suggest $ importSuggestion mn refs Nothing
-  MissingTypeDeclaration ident ty -> suggest $ showIdent ident ++ " :: " ++ prettyPrintType ty
-  WildcardInferredType ty _ -> suggest $ prettyPrintType ty
-  _ -> Nothing
-
+errorSuggestion err =
+    case err of
+      UnusedImport{} -> emptySuggestion
+      DuplicateImport{} -> emptySuggestion
+      UnusedExplicitImport mn _ qual refs -> suggest $ importSuggestion mn refs qual
+      UnusedDctorImport mn _ qual refs -> suggest $ importSuggestion mn refs qual
+      UnusedDctorExplicitImport mn _ _ qual refs -> suggest $ importSuggestion mn refs qual
+      ImplicitImport mn refs -> suggest $ importSuggestion mn refs Nothing
+      ImplicitQualifiedImport mn asModule refs -> suggest $ importSuggestion mn refs (Just asModule)
+      HidingImport mn refs -> suggest $ importSuggestion mn refs Nothing
+      MissingTypeDeclaration ident ty -> suggest $ showIdent ident ++ " :: " ++ prettyPrintSuggestedType ty
+      WildcardInferredType ty _ -> suggest $ prettyPrintSuggestedType ty
+      _ -> Nothing
   where
     emptySuggestion = Just $ ErrorSuggestion ""
     suggest = Just . ErrorSuggestion
@@ -595,6 +602,11 @@
             , line "They may be disallowed completely in a future version of the compiler."
             ]
     renderSimpleErrorMessage OverlappingInstances{} = internalError "OverlappingInstances: empty instance list"
+    renderSimpleErrorMessage (UnknownClass nm) =
+      paras [ line "No type class instance was found for class"
+            , markCodeBox $ indent $ line (showQualified runProperName nm)
+            , line "because the class was not in scope. Perhaps it was not exported."
+            ]
     renderSimpleErrorMessage (NoInstanceFound (Constraint C.Fail [ ty ] _)) | Just box <- toTypelevelString ty =
       paras [ line "A custom type error occurred while solving type class constraints:"
             , indent box
@@ -626,6 +638,11 @@
         where
         go TUnknown{} = True
         go _ = False
+    renderSimpleErrorMessage (AmbiguousTypeVariables t _) =
+      paras [ line "The inferred type"
+            , indent $ line $ markCode $ prettyPrintType t
+            , line "has type variables which are not mentioned in the body of the type. Consider adding a type annotation."
+            ]
     renderSimpleErrorMessage (PossiblyInfiniteInstance nm ts) =
       paras [ line "Type class instance for"
             , markCodeBox $ indent $ Box.hsep 1 Box.left
@@ -733,10 +750,31 @@
       paras $ [ line "Wildcard type definition has the inferred type "
               , markCodeBox $ indent $ typeAsBox ty
               ] ++ renderContext ctx
-    renderSimpleErrorMessage (HoleInferredType name ty ctx) =
-      paras $ [ line $ "Hole '" ++ markCode name ++ "' has the inferred type "
-              , markCodeBox $ indent $ typeAsBox ty
-              ] ++ renderContext ctx
+    renderSimpleErrorMessage (HoleInferredType name ty ctx ts) =
+      let
+        maxTSResults = 15
+        tsResult = case ts of
+          (TSAfter idents) | not (null idents) ->
+            let
+              formatTS (names, types) =
+                let
+                  idBoxes = Box.text . showQualified runIdent <$> names
+                  tyBoxes = (\t -> BoxHelpers.indented
+                              (Box.text ":: " Box.<> typeAsBox t)) <$> types
+                  longestId = maximum (map Box.cols idBoxes)
+                in
+                  Box.vcat Box.top $
+                      zipWith (Box.<>)
+                      (Box.alignHoriz Box.left longestId <$> idBoxes)
+                      tyBoxes
+            in [ line "You could substitute the hole with one of these values:"
+               , markCodeBox (indent (formatTS (unzip (take maxTSResults idents))))
+               ]
+          _ -> []
+      in
+        paras $ [ line $ "Hole '" ++ markCode name ++ "' has the inferred type "
+                , markCodeBox (indent (typeAsBox ty))
+                ] ++ tsResult ++ renderContext ctx
     renderSimpleErrorMessage (MissingTypeDeclaration ident ty) =
       paras [ line $ "No type declaration was provided for the top-level declaration of " ++ markCode (showIdent ident) ++ "."
             , line "It is good practice to provide type declarations as a form of documentation."
@@ -834,8 +872,8 @@
       paras [ line $ "Cannot derive an instance of the " ++ markCode "Newtype" ++ " class for non-newtype " ++ markCode (runProperName tyName) ++ "."
             ]
 
-    renderSimpleErrorMessage (NonWildcardNewtypeInstance tyName) =
-      paras [ line $ "A type wildcard (_) should be used for the inner type when deriving the " ++ markCode "Newtype" ++ " instance for " ++ markCode (runProperName tyName) ++ "."
+    renderSimpleErrorMessage (ExpectedWildcard tyName) =
+      paras [ line $ "Expected a type wildcard (_) when deriving an instance for " ++ markCode (runProperName tyName) ++ "."
             ]
 
     renderHint :: ErrorMessageHint -> Box.Box -> Box.Box
diff --git a/src/Language/PureScript/Ide/CaseSplit.hs b/src/Language/PureScript/Ide/CaseSplit.hs
--- a/src/Language/PureScript/Ide/CaseSplit.hs
+++ b/src/Language/PureScript/Ide/CaseSplit.hs
@@ -12,7 +12,7 @@
 -- Casesplitting and adding function clauses
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE OverloadedStrings     #-}
+{-# LANGUAGE OverloadedStrings #-}
 
 module Language.PureScript.Ide.CaseSplit
        ( WildcardAnnotations()
@@ -23,10 +23,10 @@
        , caseSplit
        ) where
 
-import           Protolude hiding (Constructor)
+import           Protolude                     hiding (Constructor)
 
-import qualified Data.Text                               as T
-import qualified Language.PureScript                     as P
+import qualified Data.Text                     as T
+import qualified Language.PureScript           as P
 
 import           Language.PureScript.Externs
 import           Language.PureScript.Ide.Error
@@ -34,8 +34,8 @@
 import           Language.PureScript.Ide.Types
 import           Language.PureScript.Ide.Util
 
-import           Text.Parsec                             as Parsec
-import qualified Text.PrettyPrint.Boxes                  as Box
+import           Text.Parsec                   as Parsec
+import qualified Text.PrettyPrint.Boxes        as Box
 
 type Constructor = (P.ProperName 'P.ConstructorName, [P.Type])
 
diff --git a/src/Language/PureScript/Ide/Command.hs b/src/Language/PureScript/Ide/Command.hs
--- a/src/Language/PureScript/Ide/Command.hs
+++ b/src/Language/PureScript/Ide/Command.hs
@@ -12,7 +12,8 @@
 -- Datatypes for the commands psc-ide accepts
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE OverloadedStrings     #-}
+{-# LANGUAGE OverloadedStrings #-}
+
 
 module Language.PureScript.Ide.Command where
 
diff --git a/src/Language/PureScript/Ide/Conversions.hs b/src/Language/PureScript/Ide/Conversions.hs
--- a/src/Language/PureScript/Ide/Conversions.hs
+++ b/src/Language/PureScript/Ide/Conversions.hs
@@ -14,16 +14,23 @@
 
 module Language.PureScript.Ide.Conversions where
 
-import           Protolude
-import           Data.Text           (unwords, lines, strip)
+import           Control.Lens.Iso
+import           Data.Text           (lines, strip, unwords)
 import qualified Language.PureScript as P
+import           Protolude
 
 runProperNameT :: P.ProperName a -> Text
 runProperNameT = toS . P.runProperName
 
+properNameT :: Iso' (P.ProperName a) Text
+properNameT = iso (toS . P.runProperName) (P.ProperName . toS)
+
 runIdentT :: P.Ident -> Text
 runIdentT = toS . P.runIdent
 
+identT :: Iso' P.Ident Text
+identT = iso (toS . P.runIdent) (P.Ident . toS)
+
 runOpNameT :: P.OpName a -> Text
 runOpNameT = toS . P.runOpName
 
@@ -32,4 +39,3 @@
 
 prettyTypeT :: P.Type -> Text
 prettyTypeT = unwords . map strip . lines . toS . P.prettyPrintType
-
diff --git a/src/Language/PureScript/Ide/Error.hs b/src/Language/PureScript/Ide/Error.hs
--- a/src/Language/PureScript/Ide/Error.hs
+++ b/src/Language/PureScript/Ide/Error.hs
@@ -17,11 +17,11 @@
        ( PscIdeError(..)
        ) where
 
-import           Protolude
 import           Data.Aeson
 import           Language.PureScript.Errors.JSON
-import           Language.PureScript.Ide.Types (ModuleIdent)
-import qualified Text.Parsec.Error             as P
+import           Language.PureScript.Ide.Types   (ModuleIdent)
+import           Protolude
+import qualified Text.Parsec.Error               as P
 
 data PscIdeError
     = GeneralError Text
diff --git a/src/Language/PureScript/Ide/Externs.hs b/src/Language/PureScript/Ide/Externs.hs
--- a/src/Language/PureScript/Ide/Externs.hs
+++ b/src/Language/PureScript/Ide/Externs.hs
@@ -14,7 +14,6 @@
 
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RecordWildCards   #-}
-{-# LANGUAGE FlexibleContexts  #-}
 
 module Language.PureScript.Ide.Externs
   ( readExternFile
@@ -24,10 +23,11 @@
 
 import           Protolude
 
+import           Control.Lens                  ((^.))
 import           Data.Aeson                    (decodeStrict)
+import qualified Data.ByteString               as BS
 import           Data.List                     (nub)
 import qualified Data.Map                      as Map
-import qualified Data.ByteString               as BS
 import           Language.PureScript.Ide.Error (PscIdeError (..))
 import           Language.PureScript.Ide.Types
 import           Language.PureScript.Ide.Util
@@ -58,14 +58,14 @@
     cleanDeclarations = nub $ appEndo typeClassFilter declarations
 
 removeTypeDeclarationsForClass :: IdeDeclaration -> Endo [IdeDeclaration]
-removeTypeDeclarationsForClass (IdeTypeClass n) = Endo (filter notDuplicate)
-  where notDuplicate (IdeType n' _) = runProperNameT n /= runProperNameT n'
-        notDuplicate (IdeTypeSynonym n' _) = runProperNameT n /= runProperNameT n'
+removeTypeDeclarationsForClass (IdeDeclTypeClass n) = Endo (filter notDuplicate)
+  where notDuplicate (IdeDeclType t) = n ^. properNameT /= t ^. ideTypeName . properNameT
+        notDuplicate (IdeDeclTypeSynonym s) = n ^. properNameT /= s ^. ideSynonymName . properNameT
         notDuplicate _ = True
 removeTypeDeclarationsForClass _ = mempty
 
 isTypeClassDeclaration :: IdeDeclaration -> Bool
-isTypeClassDeclaration IdeTypeClass{} = True
+isTypeClassDeclaration IdeDeclTypeClass{} = True
 isTypeClassDeclaration _ = False
 
 convertExport :: P.DeclarationRef -> Maybe (P.ModuleName, P.DeclarationRef)
@@ -73,19 +73,20 @@
 convertExport _ = Nothing
 
 convertDecl :: P.ExternsDeclaration -> Maybe IdeDeclaration
-convertDecl P.EDType{..} = Just (IdeType edTypeName edTypeKind)
-convertDecl P.EDTypeSynonym{..} =
-  Just (IdeTypeSynonym edTypeSynonymName edTypeSynonymType)
-convertDecl P.EDDataConstructor{..} = Just $
+convertDecl P.EDType{..} = Just $ IdeDeclType $
+  IdeType edTypeName edTypeKind
+convertDecl P.EDTypeSynonym{..} = Just $ IdeDeclTypeSynonym
+  (IdeSynonym edTypeSynonymName edTypeSynonymType)
+convertDecl P.EDDataConstructor{..} = Just $ IdeDeclDataConstructor $
   IdeDataConstructor edDataCtorName edDataCtorTypeCtor edDataCtorType
-convertDecl P.EDValue{..} = Just $
+convertDecl P.EDValue{..} = Just $ IdeDeclValue $
   IdeValue edValueName edValueType
-convertDecl P.EDClass{..} = Just (IdeTypeClass edClassName)
+convertDecl P.EDClass{..} = Just (IdeDeclTypeClass edClassName)
 convertDecl P.EDInstance{} = Nothing
 
 convertOperator :: P.ExternsFixity -> IdeDeclaration
 convertOperator P.ExternsFixity{..} =
-  IdeValueOperator
+  IdeDeclValueOperator $ IdeValueOperator
     efOperator
     efAlias
     efPrecedence
@@ -94,7 +95,7 @@
 
 convertTypeOperator :: P.ExternsTypeFixity -> IdeDeclaration
 convertTypeOperator P.ExternsTypeFixity{..} =
-  IdeTypeOperator
+  IdeDeclTypeOperator $ IdeTypeOperator
     efTypeOperator
     efTypeAlias
     efTypePrecedence
@@ -110,20 +111,20 @@
   where
     convertDeclaration :: IdeDeclarationAnn -> IdeDeclarationAnn
     convertDeclaration (IdeDeclarationAnn ann d) = case d of
-      IdeValue i t ->
-        annotateFunction i (IdeValue i t)
-      IdeType i k ->
-        annotateType (runProperNameT i) (IdeType i k)
-      IdeTypeSynonym i t ->
-        annotateType (runProperNameT i) (IdeTypeSynonym i t)
-      IdeDataConstructor i tn t ->
-        annotateValue (runProperNameT i) (IdeDataConstructor i tn t)
-      IdeTypeClass i ->
-        annotateType (runProperNameT i) (IdeTypeClass i)
-      IdeValueOperator n i p a t ->
-        annotateValue (valueOperatorAliasT i) (IdeValueOperator n i p a t)
-      IdeTypeOperator n i p a k ->
-        annotateType (typeOperatorAliasT i) (IdeTypeOperator n i p a k)
+      IdeDeclValue v ->
+        annotateFunction (v ^. ideValueIdent) (IdeDeclValue v)
+      IdeDeclType t ->
+        annotateType (t ^. ideTypeName . properNameT) (IdeDeclType t)
+      IdeDeclTypeSynonym s ->
+        annotateType (s ^. ideSynonymName . properNameT) (IdeDeclTypeSynonym s)
+      IdeDeclDataConstructor dtor ->
+        annotateValue (dtor ^. ideDtorName . properNameT) (IdeDeclDataConstructor dtor)
+      IdeDeclTypeClass i ->
+        annotateType (runProperNameT i) (IdeDeclTypeClass i)
+      IdeDeclValueOperator op ->
+        annotateValue (op ^. ideValueOpAlias & valueOperatorAliasT) (IdeDeclValueOperator op)
+      IdeDeclTypeOperator op ->
+        annotateType (op ^. ideTypeOpAlias & typeOperatorAliasT) (IdeDeclTypeOperator op)
       where
         annotateFunction x = IdeDeclarationAnn (ann { annLocation = Map.lookup (Left (runIdentT x)) defs
                                                     , annTypeAnnotation = Map.lookup x types
diff --git a/src/Language/PureScript/Ide/Filter.hs b/src/Language/PureScript/Ide/Filter.hs
--- a/src/Language/PureScript/Ide/Filter.hs
+++ b/src/Language/PureScript/Ide/Filter.hs
@@ -23,13 +23,13 @@
        , applyFilters
        ) where
 
-import           Protolude hiding (isPrefixOf)
+import           Protolude                     hiding (isPrefixOf)
 
 import           Data.Aeson
 import           Data.Text                     (isPrefixOf)
+import qualified Language.PureScript           as P
 import           Language.PureScript.Ide.Types
 import           Language.PureScript.Ide.Util
-import qualified Language.PureScript as P
 
 newtype Filter = Filter (Endo [Module]) deriving(Monoid)
 
@@ -65,8 +65,7 @@
   where
     filterModuleDecls :: Module -> Module
     filterModuleDecls (moduleIdent, decls) =
-        (moduleIdent, filter (flip predicate search . getDeclaration) decls)
-    getDeclaration (IdeDeclarationAnn _ d) = d
+        (moduleIdent, filter (flip predicate search . discardAnn) decls)
 
 runFilter :: Filter -> [Module] -> [Module]
 runFilter (Filter f) = appEndo f
diff --git a/src/Language/PureScript/Ide/Imports.hs b/src/Language/PureScript/Ide/Imports.hs
--- a/src/Language/PureScript/Ide/Imports.hs
+++ b/src/Language/PureScript/Ide/Imports.hs
@@ -30,9 +30,10 @@
        where
 
 import           Protolude
+
+import           Control.Lens                       ((^.))
+import           Data.List                          (findIndex, nubBy)
 import qualified Data.Text                          as T
-import           Data.List                          (nubBy, findIndex)
-import qualified Data.Text.IO                       as TIO
 import qualified Language.PureScript                as P
 import           Language.PureScript.Ide.Completion
 import           Language.PureScript.Ide.Error
@@ -40,6 +41,7 @@
 import           Language.PureScript.Ide.State
 import           Language.PureScript.Ide.Types
 import           Language.PureScript.Ide.Util
+import           System.IO.UTF8                     (readUTF8FileT, writeUTF8FileT)
 
 data Import = Import P.ModuleName P.ImportDeclarationType  (Maybe P.ModuleName)
               deriving (Eq, Show)
@@ -70,7 +72,7 @@
 parseImportsFromFile :: (MonadIO m, MonadError PscIdeError m) =>
                         FilePath -> m (P.ModuleName, [Text], [Import], [Text])
 parseImportsFromFile fp = do
-  file <- liftIO (TIO.readFile fp)
+  file <- liftIO (readUTF8FileT fp)
   case sliceImportSection (T.lines file) of
     Right res -> pure res
     Left err -> throwError (GeneralError err)
@@ -197,16 +199,16 @@
     then imports
     else updateAtFirstOrPrepend matches (insertDeclIntoImport decl) freshImport imports
   where
-    refFromDeclaration (IdeTypeClass n) =
+    refFromDeclaration (IdeDeclTypeClass n) =
       P.TypeClassRef n
-    refFromDeclaration (IdeDataConstructor n tn _) =
-      P.TypeRef tn (Just [n])
-    refFromDeclaration (IdeType n _) =
-      P.TypeRef n (Just [])
-    refFromDeclaration (IdeValueOperator op _ _ _ _) =
-      P.ValueOpRef op
-    refFromDeclaration (IdeTypeOperator op _ _ _ _) =
-      P.TypeOpRef op
+    refFromDeclaration (IdeDeclDataConstructor dtor) =
+      P.TypeRef (dtor ^. ideDtorTypeName) Nothing
+    refFromDeclaration (IdeDeclType t) =
+      P.TypeRef (t ^. ideTypeName) (Just [])
+    refFromDeclaration (IdeDeclValueOperator op) =
+      P.ValueOpRef (op ^. ideValueOpName)
+    refFromDeclaration (IdeDeclTypeOperator op) =
+      P.TypeOpRef (op ^. ideTypeOpName)
     refFromDeclaration d =
       P.ValueRef $ P.Ident $ T.unpack (identifierFromIdeDeclaration d)
 
@@ -214,20 +216,19 @@
     -- TypeDeclaration "Maybe" + Data.Maybe (maybe) -> Data.Maybe(Maybe, maybe)
     insertDeclIntoImport :: IdeDeclaration -> Import -> Import
     insertDeclIntoImport decl' (Import mn (P.Explicit refs) Nothing) =
-      Import mn (P.Explicit (insertDeclIntoRefs decl' refs)) Nothing
+      Import mn (P.Explicit (sortBy P.compDecRef (insertDeclIntoRefs decl' refs))) Nothing
     insertDeclIntoImport _ is = is
 
     insertDeclIntoRefs :: IdeDeclaration -> [P.DeclarationRef] -> [P.DeclarationRef]
-    insertDeclIntoRefs (IdeDataConstructor dtor tn _) refs =
-      updateAtFirstOrPrepend (matchType tn) (insertDtor dtor) (P.TypeRef tn (Just [dtor])) refs
+    insertDeclIntoRefs d@(IdeDeclDataConstructor dtor) refs =
+      updateAtFirstOrPrepend
+        (matchType (dtor ^. ideDtorTypeName))
+        (insertDtor (dtor ^. ideDtorName))
+        (refFromDeclaration d)
+        refs
     insertDeclIntoRefs dr refs = nubBy ((==) `on` P.prettyPrintRef) (refFromDeclaration dr : refs)
 
-    insertDtor dtor (P.TypeRef tn' dtors) =
-      case dtors of
-        Just dtors' -> P.TypeRef tn' (Just (ordNub (dtor : dtors')))
-        -- This means the import was opened. We don't add anything in this case
-        -- import Data.Maybe (Maybe(..)) -> import Data.Maybe (Maybe(Just))
-        Nothing -> P.TypeRef tn' Nothing
+    insertDtor _ (P.TypeRef tn' _) = P.TypeRef tn' Nothing
     insertDtor _ refs = refs
 
     matchType :: P.ProperName 'P.TypeName -> P.DeclarationRef -> Bool
@@ -294,9 +295,9 @@
     xs ->
       pure $ Left xs
     where
-      decideRedundantCase dtor@(IdeDataConstructor _ t _) (IdeType t' _) =
-        if t == t' then Just dtor else Nothing
-      decideRedundantCase IdeType{} ts@IdeTypeSynonym{} =
+      decideRedundantCase d@(IdeDeclDataConstructor dtor) (IdeDeclType t) =
+        if dtor ^. ideDtorTypeName == t ^. ideTypeName then Just d else Nothing
+      decideRedundantCase IdeDeclType{} ts@IdeDeclTypeSynonym{} =
         Just ts
       decideRedundantCase _ _ = Nothing
 
@@ -316,10 +317,10 @@
 answerRequest :: (MonadIO m) => Maybe FilePath -> [Text] -> m Success
 answerRequest outfp rs  =
   case outfp of
-    Nothing -> pure $ MultilineTextResult rs
+    Nothing -> pure (MultilineTextResult rs)
     Just outfp' -> do
-      liftIO $ TIO.writeFile outfp' (T.unlines rs)
-      pure $ TextResult $ "Written to " <> T.pack outfp'
+      liftIO (writeUTF8FileT outfp' (T.unlines rs))
+      pure (TextResult ("Written to " <> T.pack outfp'))
 
 -- | Test and ghci helper
 parseImport :: Text -> Maybe Import
diff --git a/src/Language/PureScript/Ide/Matcher.hs b/src/Language/PureScript/Ide/Matcher.hs
--- a/src/Language/PureScript/Ide/Matcher.hs
+++ b/src/Language/PureScript/Ide/Matcher.hs
@@ -12,9 +12,9 @@
 -- Matchers for psc-ide commands
 -----------------------------------------------------------------------------
 
+{-# LANGUAGE FlexibleInstances          #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE OverloadedStrings          #-}
-{-# LANGUAGE FlexibleInstances          #-}
 
 module Language.PureScript.Ide.Matcher
        ( Matcher
diff --git a/src/Language/PureScript/Ide/Pursuit.hs b/src/Language/PureScript/Ide/Pursuit.hs
--- a/src/Language/PureScript/Ide/Pursuit.hs
+++ b/src/Language/PureScript/Ide/Pursuit.hs
@@ -12,7 +12,7 @@
 -- Pursuit client for psc-ide
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE OverloadedStrings #-}
 
 module Language.PureScript.Ide.Pursuit
   ( searchPursuitForDeclarations
@@ -35,37 +35,41 @@
 -- TODO: remove this when the issue is fixed at Pursuit
 queryPursuit :: Text -> IO ByteString
 queryPursuit q = do
-    let qClean = T.dropWhileEnd (== '.') q
-    req' <- parseRequest "http://pursuit.purescript.org/search"
-    let req = req'
-          { queryString= "q=" <> (fromString . T.unpack) qClean
-          , requestHeaders=[(hAccept, "application/json")]
-          }
-    m <- newManager tlsManagerSettings
-    withHTTP req m $ \resp ->
-      P.fold (<>) "" identity (responseBody resp)
+  let qClean = T.dropWhileEnd (== '.') q
+  req' <- parseRequest "http://pursuit.purescript.org/search"
+  let req = req'
+        { queryString= "q=" <> (fromString . T.unpack) qClean
+        , requestHeaders=[(hAccept, "application/json")]
+        }
+  m <- newManager tlsManagerSettings
+  withHTTP req m $ \resp ->
+    P.fold (<>) "" identity (responseBody resp)
 
+
 handler :: HttpException -> IO [a]
+handler StatusCodeException{} = pure []
 handler _ = pure []
 
 searchPursuitForDeclarations :: Text -> IO [PursuitResponse]
-searchPursuitForDeclarations query = E.handle handler $ do
-    r <- queryPursuit query
-    let results' = decode (fromStrict r) :: Maybe Array
-    case results' of
-        Nothing -> pure []
-        Just results -> pure (mapMaybe (isDeclarationResponse . fromJSON) (toList results))
+searchPursuitForDeclarations query =
+    (do r <- queryPursuit query
+        let results' = decode (fromStrict r) :: Maybe Array
+        case results' of
+          Nothing -> pure []
+          Just results -> pure (mapMaybe (isDeclarationResponse . fromJSON) (toList results))) `E.catch`
+    handler
   where
     isDeclarationResponse (Success a@DeclarationResponse{}) = Just a
     isDeclarationResponse _ = Nothing
 
 findPackagesForModuleIdent :: Text -> IO [PursuitResponse]
-findPackagesForModuleIdent query = E.handle handler $ do
-    r <- queryPursuit query
-    let results' = decode (fromStrict r) :: Maybe Array
-    case results' of
+findPackagesForModuleIdent query =
+  (do r <- queryPursuit query
+      let results' = decode (fromStrict r) :: Maybe Array
+      case results' of
         Nothing -> pure []
-        Just results -> pure (mapMaybe (isModuleResponse . fromJSON) (toList results))
+        Just results -> pure (mapMaybe (isModuleResponse . fromJSON) (toList results))) `E.catch`
+  handler
   where
     isModuleResponse (Success a@ModuleResponse{}) = Just a
     isModuleResponse _ = Nothing
diff --git a/src/Language/PureScript/Ide/Rebuild.hs b/src/Language/PureScript/Ide/Rebuild.hs
--- a/src/Language/PureScript/Ide/Rebuild.hs
+++ b/src/Language/PureScript/Ide/Rebuild.hs
@@ -11,7 +11,7 @@
 import           Protolude
 
 import           "monad-logger" Control.Monad.Logger
-import qualified Data.List as List
+import qualified Data.List                       as List
 import qualified Data.Map.Lazy                   as M
 import           Data.Maybe                      (fromJust)
 import qualified Data.Set                        as S
@@ -21,7 +21,7 @@
 import           Language.PureScript.Ide.State
 import           Language.PureScript.Ide.Types
 import           Language.PureScript.Ide.Util
-import           System.IO.UTF8                  (readUTF8File)
+import           System.IO.UTF8                  (readUTF8FileT)
 
 -- | Given a filepath performs the following steps:
 --
@@ -44,7 +44,7 @@
   -> m Success
 rebuildFile path = do
 
-  input <- liftIO (readUTF8File path)
+  input <- liftIO (readUTF8FileT path)
 
   m <- case snd <$> P.parseModuleFromFile identity (path, input) of
     Left parseError -> throwError
diff --git a/src/Language/PureScript/Ide/Reexports.hs b/src/Language/PureScript/Ide/Reexports.hs
--- a/src/Language/PureScript/Ide/Reexports.hs
+++ b/src/Language/PureScript/Ide/Reexports.hs
@@ -25,16 +25,18 @@
 
 import           Protolude
 
+import           Control.Lens                  hiding ((&))
+
 import qualified Data.Map                      as Map
+import qualified Language.PureScript           as P
 import           Language.PureScript.Ide.Types
 import           Language.PureScript.Ide.Util
-import qualified Language.PureScript as P
 
 -- | Contains the module with resolved reexports, and eventual failures
 data ReexportResult a
   = ReexportResult
   { reResolved :: a
-  , reFailed :: [(P.ModuleName, P.DeclarationRef)]
+  , reFailed   :: [(P.ModuleName, P.DeclarationRef)]
   } deriving (Show, Eq, Functor)
 
 -- | Uses the passed formatter to format the resolved module, and adds eventual
@@ -79,7 +81,7 @@
   -> Either P.DeclarationRef [IdeDeclarationAnn]
 resolveRef decls ref = case ref of
   P.TypeRef tn mdtors ->
-    case findRef (\case IdeType name _ -> name == tn; _ -> False) of
+    case findRef (\x -> x ^? _IdeDeclType . ideTypeName <&> (== tn) & fromMaybe False) of
       Nothing -> Left ref
       Just d -> Right $ d : case mdtors of
           Nothing ->
@@ -89,28 +91,23 @@
             findDtors tn
           Just dtors -> mapMaybe lookupDtor dtors
   P.ValueRef i ->
-    findWrapped (\case IdeValue i' _ -> i' == i; _ -> False)
-  P.TypeOpRef name ->
-    findWrapped (\case IdeTypeOperator n _ _ _ _ -> n == name; _ -> False)
+    findWrapped (\x -> x ^? _IdeDeclValue . ideValueIdent <&> (== i) & fromMaybe False)
   P.ValueOpRef name ->
-    findWrapped (\case IdeValueOperator n _ _ _ _ -> n == name; _ -> False)
+    findWrapped (\x -> x ^? _IdeDeclValueOperator . ideValueOpName <&> (== name) & fromMaybe False)
+  P.TypeOpRef name ->
+    findWrapped (\x -> x ^? _IdeDeclTypeOperator . ideTypeOpName <&> (== name) & fromMaybe False)
   P.TypeClassRef name ->
-    findWrapped (\case IdeTypeClass n -> n == name; _ -> False)
+    findWrapped (\case IdeDeclTypeClass n -> n == name; _ -> False)
   _ ->
     Left ref
   where
-    findWrapped = wrapSingle . findRef
-    wrapSingle = maybe (Left ref) (Right . pure)
+    findWrapped = maybe (Left ref) (Right . pure) . findRef
     findRef f = find (f . discardAnn) decls
 
     lookupDtor name =
-      findRef (\case IdeDataConstructor name' _ _ -> name == name'
-                     _ -> False)
+      findRef (\x -> x ^? _IdeDeclDataConstructor . ideDtorName <&> (== name) & fromMaybe False)
 
     findDtors tn = filter (f . discardAnn) decls
       where
         f :: IdeDeclaration -> Bool
-        f decl
-          | (IdeDataConstructor _ tn' _) <- decl
-          , tn == tn' = True
-          | otherwise = False
+        f decl = decl ^? _IdeDeclDataConstructor . ideDtorTypeName <&> (== tn) & fromMaybe False
diff --git a/src/Language/PureScript/Ide/SourceFile.hs b/src/Language/PureScript/Ide/SourceFile.hs
--- a/src/Language/PureScript/Ide/SourceFile.hs
+++ b/src/Language/PureScript/Ide/SourceFile.hs
@@ -12,7 +12,7 @@
 -- Getting declarations from PureScript sourcefiles
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE OverloadedStrings     #-}
+{-# LANGUAGE OverloadedStrings #-}
 
 module Language.PureScript.Ide.SourceFile
   ( parseModule
@@ -25,19 +25,19 @@
 
 import           Protolude
 
-import qualified Data.Map as Map
-import qualified Language.PureScript                  as P
+import qualified Data.Map                      as Map
+import qualified Language.PureScript           as P
 import           Language.PureScript.Ide.Error
-import           Language.PureScript.Ide.Util
 import           Language.PureScript.Ide.Types
-import           System.IO.UTF8                       (readUTF8File)
+import           Language.PureScript.Ide.Util
+import           System.IO.UTF8                (readUTF8FileT)
 
 parseModule
   :: (MonadIO m)
   => FilePath
-  -> m (Either FilePath (FilePath, P.Module) )
+  -> m (Either FilePath (FilePath, P.Module))
 parseModule path = do
-  contents <- liftIO (readUTF8File path)
+  contents <- liftIO (readUTF8FileT path)
   case P.parseModuleFromFile identity (path, contents) of
     Left _ -> pure (Left path)
     Right m -> pure (Right m)
diff --git a/src/Language/PureScript/Ide/State.hs b/src/Language/PureScript/Ide/State.hs
--- a/src/Language/PureScript/Ide/State.hs
+++ b/src/Language/PureScript/Ide/State.hs
@@ -32,20 +32,20 @@
   , resolveOperatorsForModule
   ) where
 
-import           Protolude
 import qualified Prelude
+import           Protolude
 
 import           Control.Concurrent.STM
+import           Control.Lens                       hiding (op, (&))
 import           "monad-logger" Control.Monad.Logger
-import qualified Data.Map.Lazy                     as Map
-import qualified Data.List                         as List
+import qualified Data.Map.Lazy                      as Map
+import qualified Language.PureScript                as P
 import           Language.PureScript.Externs
 import           Language.PureScript.Ide.Externs
 import           Language.PureScript.Ide.Reexports
 import           Language.PureScript.Ide.SourceFile
 import           Language.PureScript.Ide.Types
 import           Language.PureScript.Ide.Util
-import qualified Language.PureScript as P
 import           System.Clock
 
 -- | Resets all State inside psc-ide
@@ -235,47 +235,39 @@
   :: Map P.ModuleName [IdeDeclarationAnn]
   -> [IdeDeclarationAnn]
   -> [IdeDeclarationAnn]
-resolveOperatorsForModule modules = map (mapIdeDeclaration resolveOperator)
+resolveOperatorsForModule modules = map ((over idaDeclaration) resolveOperator)
   where
-    resolveOperator (IdeValueOperator
-                    opName
-                    i@(P.Qualified (Just moduleName)
-                        (Left ident)) precedence assoc _) =
-      let t = do
-            sourceModule <- Map.lookup moduleName modules
-            IdeValue _ tP <-
-              List.find (\case
-                            IdeValue iP _ -> iP == ident
-                            _ -> False) (discardAnn <$> sourceModule)
-            pure tP
-
-      in IdeValueOperator opName i precedence assoc t
-    resolveOperator (IdeValueOperator
-                    opName
-                    i@(P.Qualified (Just moduleName)
-                        (Right ctor)) precedence assoc _) =
-      let t = do
-            sourceModule <- Map.lookup moduleName modules
-            IdeDataConstructor _ _ tP <-
-              List.find (\case
-                            IdeDataConstructor cname _ _ -> ctor == cname
-                            _ -> False) (discardAnn <$> sourceModule)
-            pure tP
+    hasName :: Eq b => Lens' a b -> b -> a -> Bool
+    hasName l a x = x ^. l == a
 
-      in IdeValueOperator opName i precedence assoc t
-    resolveOperator (IdeTypeOperator
-                    opName
-                    i@(P.Qualified (Just moduleName) properName) precedence assoc _)  =
-      let k = do
-            sourceModule <- Map.lookup moduleName modules
-            IdeType _ kP <-
-              List.find (\case
-                            IdeType name _ -> name == properName
-                            _ -> False) (discardAnn <$> sourceModule)
-            pure kP
+    getDeclarations :: P.ModuleName -> [IdeDeclaration]
+    getDeclarations moduleName =
+      Map.lookup moduleName modules
+      & fromMaybe []
+      & map discardAnn
 
-      in IdeTypeOperator opName i precedence assoc k
+    resolveOperator (IdeDeclValueOperator op)
+      | (P.Qualified (Just mn) (Left ident)) <- op ^. ideValueOpAlias =
+          let t = getDeclarations mn
+                  & mapMaybe (preview _IdeDeclValue)
+                  & filter (hasName ideValueIdent ident)
+                  & map (view ideValueType)
+                  & listToMaybe
+          in IdeDeclValueOperator (op & ideValueOpType .~ t)
+      | (P.Qualified (Just mn) (Right dtor)) <- op ^. ideValueOpAlias =
+          let t = getDeclarations mn
+                  & mapMaybe (preview _IdeDeclDataConstructor)
+                  & filter (hasName ideDtorName dtor)
+                  & map (view ideDtorType)
+                  & listToMaybe
+          in IdeDeclValueOperator (op & ideValueOpType .~ t)
+    resolveOperator (IdeDeclTypeOperator op)
+      | P.Qualified (Just mn) properName <- op ^. ideTypeOpAlias =
+          let k = getDeclarations mn
+                  & mapMaybe (preview _IdeDeclType)
+                  & filter (hasName ideTypeName properName)
+                  & map (view ideTypeKind)
+                  & listToMaybe
+          in IdeDeclTypeOperator (op & ideTypeOpKind .~ k)
     resolveOperator x = x
 
-mapIdeDeclaration :: (IdeDeclaration -> IdeDeclaration) -> IdeDeclarationAnn -> IdeDeclarationAnn
-mapIdeDeclaration f (IdeDeclarationAnn ann decl) = IdeDeclarationAnn ann (f decl)
diff --git a/src/Language/PureScript/Ide/Types.hs b/src/Language/PureScript/Ide/Types.hs
--- a/src/Language/PureScript/Ide/Types.hs
+++ b/src/Language/PureScript/Ide/Types.hs
@@ -12,42 +12,93 @@
 -- Type definitions for psc-ide
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE DeriveFoldable    #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
 
 module Language.PureScript.Ide.Types where
 
 import           Protolude
 
 import           Control.Concurrent.STM
+import           Control.Lens.TH
 import           Data.Aeson
-import qualified Data.Map.Lazy                        as M
-import qualified Language.PureScript.Errors.JSON      as P
-import qualified Language.PureScript as P
+import qualified Data.Map.Lazy                       as M
+import qualified Language.PureScript                 as P
+import qualified Language.PureScript.Errors.JSON     as P
 import           Language.PureScript.Ide.Conversions
 
 type ModuleIdent = Text
 
 data IdeDeclaration
-  = IdeValue P.Ident P.Type
-  | IdeType (P.ProperName 'P.TypeName) P.Kind
-  | IdeTypeSynonym (P.ProperName 'P.TypeName) P.Type
-  | IdeDataConstructor (P.ProperName 'P.ConstructorName) (P.ProperName 'P.TypeName) P.Type
-  | IdeTypeClass (P.ProperName 'P.ClassName)
-  | IdeValueOperator (P.OpName 'P.ValueOpName) (P.Qualified (Either P.Ident (P.ProperName 'P.ConstructorName))) P.Precedence P.Associativity (Maybe P.Type)
-  | IdeTypeOperator (P.OpName 'P.TypeOpName) (P.Qualified (P.ProperName 'P.TypeName)) P.Precedence P.Associativity (Maybe P.Kind)
+  = IdeDeclValue IdeValue
+  | IdeDeclType IdeType
+  | IdeDeclTypeSynonym IdeSynonym
+  | IdeDeclDataConstructor IdeDataConstructor
+  | IdeDeclTypeClass (P.ProperName 'P.ClassName)
+  | IdeDeclValueOperator IdeValueOperator
+  | IdeDeclTypeOperator IdeTypeOperator
   deriving (Show, Eq, Ord)
 
-data IdeDeclarationAnn = IdeDeclarationAnn Annotation IdeDeclaration
-  deriving (Show, Eq, Ord)
+data IdeValue = IdeValue
+  { _ideValueIdent :: P.Ident
+  , _ideValueType  :: P.Type
+  } deriving (Show, Eq, Ord)
 
+data IdeType = IdeType
+ { _ideTypeName :: P.ProperName 'P.TypeName
+ , _ideTypeKind :: P.Kind
+ } deriving (Show, Eq, Ord)
+
+data IdeSynonym = IdeSynonym
+  { _ideSynonymName :: P.ProperName 'P.TypeName
+  , _ideSynonymType :: P.Type
+  } deriving (Show, Eq, Ord)
+
+data IdeDataConstructor = IdeDataConstructor
+  { _ideDtorName     :: P.ProperName 'P.ConstructorName
+  , _ideDtorTypeName :: P.ProperName 'P.TypeName
+  , _ideDtorType     :: P.Type
+  } deriving (Show, Eq, Ord)
+
+data IdeValueOperator = IdeValueOperator
+  { _ideValueOpName          :: P.OpName 'P.ValueOpName
+  , _ideValueOpAlias         :: P.Qualified (Either P.Ident (P.ProperName 'P.ConstructorName))
+  , _ideValueOpPrecedence    :: P.Precedence
+  , _ideValueOpAssociativity :: P.Associativity
+  , _ideValueOpType          :: Maybe P.Type
+  } deriving (Show, Eq, Ord)
+
+data IdeTypeOperator = IdeTypeOperator
+  { _ideTypeOpName          :: P.OpName 'P.TypeOpName
+  , _ideTypeOpAlias         :: P.Qualified (P.ProperName 'P.TypeName)
+  , _ideTypeOpPrecedence    :: P.Precedence
+  , _ideTypeOpAssociativity :: P.Associativity
+  , _ideTypeOpKind          :: Maybe P.Kind
+  } deriving (Show, Eq, Ord)
+
+makePrisms ''IdeDeclaration
+makeLenses ''IdeValue
+makeLenses ''IdeType
+makeLenses ''IdeSynonym
+makeLenses ''IdeDataConstructor
+makeLenses ''IdeValueOperator
+makeLenses ''IdeTypeOperator
+
+data IdeDeclarationAnn = IdeDeclarationAnn
+  { _idaAnnotation  :: Annotation
+  , _idaDeclaration :: IdeDeclaration
+  } deriving (Show, Eq, Ord)
+
 data Annotation
   = Annotation
-  { annLocation     :: Maybe P.SourceSpan
-  , annExportedFrom :: Maybe P.ModuleName
+  { annLocation       :: Maybe P.SourceSpan
+  , annExportedFrom   :: Maybe P.ModuleName
   , annTypeAnnotation :: Maybe P.Type
   } deriving (Show, Eq, Ord)
 
+makeLenses ''IdeDeclarationAnn
+
 emptyAnn :: Annotation
 emptyAnn = Annotation Nothing Nothing Nothing
 
@@ -103,7 +154,7 @@
   }
 
 data Stage3 = Stage3
-  { s3Declarations :: M.Map P.ModuleName [IdeDeclarationAnn]
+  { s3Declarations  :: M.Map P.ModuleName [IdeDeclarationAnn]
   , s3CachedRebuild :: Maybe (P.ModuleName, P.ExternsFile)
   }
 
@@ -112,11 +163,11 @@
 
 -- | A completion as it gets sent to the editors
 data Completion = Completion
-  { complModule :: Text
-  , complIdentifier :: Text
-  , complType :: Text
-  , complExpandedType :: Text
-  , complLocation :: Maybe P.SourceSpan
+  { complModule        :: Text
+  , complIdentifier    :: Text
+  , complType          :: Text
+  , complExpandedType  :: Text
+  , complLocation      :: Maybe P.SourceSpan
   , complDocumentation :: Maybe Text
   } deriving (Show, Eq)
 
diff --git a/src/Language/PureScript/Ide/Util.hs b/src/Language/PureScript/Ide/Util.hs
--- a/src/Language/PureScript/Ide/Util.hs
+++ b/src/Language/PureScript/Ide/Util.hs
@@ -29,23 +29,25 @@
   , module Language.PureScript.Ide.Conversions
   ) where
 
-import           Protolude                     hiding (decodeUtf8, encodeUtf8)
+import           Control.Lens                        ((^.))
 import           Data.Aeson
-import qualified Data.Text                     as T
-import           Data.Text.Lazy.Encoding       (decodeUtf8, encodeUtf8)
-import qualified Language.PureScript           as P
-import           Language.PureScript.Ide.Types
+import qualified Data.Text                           as T
+import           Data.Text.Lazy.Encoding             (decodeUtf8, encodeUtf8)
+import qualified Language.PureScript                 as P
 import           Language.PureScript.Ide.Conversions
+import           Language.PureScript.Ide.Types
+import           Protolude                           hiding (decodeUtf8,
+                                                      encodeUtf8)
 
 identifierFromIdeDeclaration :: IdeDeclaration -> Text
 identifierFromIdeDeclaration d = case d of
-  IdeValue name _ -> runIdentT name
-  IdeType name _ -> runProperNameT name
-  IdeTypeSynonym name _ -> runProperNameT name
-  IdeDataConstructor name _ _ -> runProperNameT name
-  IdeTypeClass name -> runProperNameT name
-  IdeValueOperator op _ _ _ _ -> runOpNameT op
-  IdeTypeOperator op _ _ _ _ -> runOpNameT op
+  IdeDeclValue v -> v ^. ideValueIdent . identT
+  IdeDeclType t -> t ^. ideTypeName . properNameT
+  IdeDeclTypeSynonym s -> s ^. ideSynonymName . properNameT
+  IdeDeclDataConstructor dtor -> dtor ^. ideDtorName . properNameT
+  IdeDeclTypeClass name -> runProperNameT name
+  IdeDeclValueOperator op -> op ^. ideValueOpName & runOpNameT
+  IdeDeclTypeOperator op -> op ^. ideTypeOpName & runOpNameT
 
 discardAnn :: IdeDeclarationAnn -> IdeDeclaration
 discardAnn (IdeDeclarationAnn _ d) = d
@@ -61,24 +63,24 @@
   Completion {..}
   where
     (complIdentifier, complExpandedType) = case decl of
-      IdeValue name type' -> (runIdentT name, prettyTypeT type')
-      IdeType name kind -> (runProperNameT name, toS (P.prettyPrintKind kind))
-      IdeTypeSynonym name kind -> (runProperNameT name, prettyTypeT kind)
-      IdeDataConstructor name _ type' -> (runProperNameT name, prettyTypeT type')
-      IdeTypeClass name -> (runProperNameT name, "class")
-      IdeValueOperator op ref precedence associativity typeP ->
-        (runOpNameT op, maybe (showFixity precedence associativity (valueOperatorAliasT ref) op) prettyTypeT typeP) 
-      IdeTypeOperator op ref precedence associativity kind ->
+      IdeDeclValue v -> (v ^. ideValueIdent . identT, v ^. ideValueType & prettyTypeT)
+      IdeDeclType t -> (t ^. ideTypeName . properNameT, t ^. ideTypeKind & P.prettyPrintKind & toS )
+      IdeDeclTypeSynonym s -> (s ^. ideSynonymName . properNameT, s ^. ideSynonymType & prettyTypeT)
+      IdeDeclDataConstructor d -> (d ^. ideDtorName . properNameT, d ^. ideDtorType & prettyTypeT)
+      IdeDeclTypeClass name -> (runProperNameT name, "class")
+      IdeDeclValueOperator (IdeValueOperator op ref precedence associativity typeP) ->
+        (runOpNameT op, maybe (showFixity precedence associativity (valueOperatorAliasT ref) op) prettyTypeT typeP)
+      IdeDeclTypeOperator (IdeTypeOperator op ref precedence associativity kind) ->
         (runOpNameT op, maybe (showFixity precedence associativity (typeOperatorAliasT ref) op) (toS . P.prettyPrintKind) kind)
-            
+
     complModule = runModuleNameT m
 
     complType = maybe complExpandedType prettyTypeT (annTypeAnnotation ann)
-    
+
     complLocation = annLocation ann
 
     complDocumentation = Nothing
-    
+
     showFixity p a r o =
       let asso = case a of
             P.Infix -> "infix"
@@ -95,7 +97,7 @@
   :: P.Qualified (P.ProperName 'P.TypeName) -> Text
 typeOperatorAliasT i =
   toS (P.showQualified P.runProperName i)
-  
+
 encodeT :: (ToJSON a) => a -> Text
 encodeT = toS . decodeUtf8 . encode
 
diff --git a/src/Language/PureScript/Interactive.hs b/src/Language/PureScript/Interactive.hs
--- a/src/Language/PureScript/Interactive.hs
+++ b/src/Language/PureScript/Interactive.hs
@@ -104,7 +104,7 @@
 handleCommand _ _ (BrowseModule moduleName) = handleBrowse moduleName
 handleCommand _ _ (ShowInfo QueryLoaded)    = handleShowLoadedModules
 handleCommand _ _ (ShowInfo QueryImport)    = handleShowImportedModules
-handleCommand _ _ QuitPSCi                  = P.internalError "`handleCommand QuitPSCi` was called. This is a bug."
+handleCommand _ _ _                         = P.internalError "handleCommand: unexpected command"
 
 -- | Reset the application state
 handleResetState
diff --git a/src/Language/PureScript/Interactive/Completion.hs b/src/Language/PureScript/Interactive/Completion.hs
--- a/src/Language/PureScript/Interactive/Completion.hs
+++ b/src/Language/PureScript/Interactive/Completion.hs
@@ -122,6 +122,7 @@
 directiveArg _ Quit        = []
 directiveArg _ Reset       = []
 directiveArg _ Help        = []
+directiveArg _ Paste       = []
 directiveArg _ Show        = map CtxFixed replQueryStrings
 directiveArg _ Type        = [CtxIdentifier]
 directiveArg _ Kind        = [CtxType]
diff --git a/src/Language/PureScript/Interactive/Directive.hs b/src/Language/PureScript/Interactive/Directive.hs
--- a/src/Language/PureScript/Interactive/Directive.hs
+++ b/src/Language/PureScript/Interactive/Directive.hs
@@ -30,6 +30,7 @@
     , (Type   , ["type"])
     , (Kind   , ["kind"])
     , (Show   , ["show"])
+    , (Paste  , ["paste"])
     ]
 
 -- |
@@ -82,6 +83,7 @@
 hasArgument Help = False
 hasArgument Quit = False
 hasArgument Reset = False
+hasArgument Paste = False
 hasArgument _ = True
 
 -- |
@@ -97,4 +99,5 @@
   , (Kind,    "<type>",   "Show the kind of <type>")
   , (Show,    "import",   "Show all imported modules")
   , (Show,    "loaded",   "Show all loaded modules")
+  , (Paste,   "paste",    "Enter multiple lines, terminated by ^D")
   ]
diff --git a/src/Language/PureScript/Interactive/IO.hs b/src/Language/PureScript/Interactive/IO.hs
--- a/src/Language/PureScript/Interactive/IO.hs
+++ b/src/Language/PureScript/Interactive/IO.hs
@@ -1,11 +1,11 @@
-module Language.PureScript.Interactive.IO where
+module Language.PureScript.Interactive.IO (findNodeProcess, getHistoryFilename) where
 
 import Prelude.Compat
 
 import Control.Monad (msum)
 import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
 import System.Directory (createDirectoryIfMissing, getHomeDirectory, findExecutable)
-import System.FilePath (takeDirectory, (</>), isPathSeparator)
+import System.FilePath (takeDirectory, (</>))
 
 mkdirp :: FilePath -> IO ()
 mkdirp = createDirectoryIfMissing True . takeDirectory
@@ -32,10 +32,3 @@
   let filename = home </> ".purescript" </> "psci_history"
   mkdirp filename
   return filename
-
--- |
--- Expands tilde in path.
---
-expandTilde :: FilePath -> IO FilePath
-expandTilde ('~':p:rest) | isPathSeparator p = (</> rest) <$> getHomeDirectory
-expandTilde p = return p
diff --git a/src/Language/PureScript/Interactive/Message.hs b/src/Language/PureScript/Interactive/Message.hs
--- a/src/Language/PureScript/Interactive/Message.hs
+++ b/src/Language/PureScript/Interactive/Message.hs
@@ -39,10 +39,14 @@
 
 supportModuleMessage :: String
 supportModuleMessage = unlines
-  [ "PSCi requires the purescript-psci-support package to be installed."
+  [ "PSCi requires the psci-support package to be installed."
   , "You can install it using Bower as follows:"
   , ""
   , "  bower i purescript-psci-support --save-dev"
+  , ""
+  , "Or using psc-package:"
+  , ""
+  , "  psc-package install psci-support"
   , ""
   , "For help getting started, visit http://wiki.purescript.org/PSCi"
   ]
diff --git a/src/Language/PureScript/Interactive/Module.hs b/src/Language/PureScript/Interactive/Module.hs
--- a/src/Language/PureScript/Interactive/Module.hs
+++ b/src/Language/PureScript/Interactive/Module.hs
@@ -6,7 +6,7 @@
 import qualified Language.PureScript as P
 import           Language.PureScript.Interactive.Types
 import           System.FilePath (pathSeparator)
-import           System.IO.UTF8 (readUTF8File)
+import           System.IO.UTF8 (readUTF8FileT)
 
 -- * Support Module
 
@@ -25,7 +25,7 @@
 --
 loadModule :: FilePath -> IO (Either String [P.Module])
 loadModule filename = do
-  content <- readUTF8File filename
+  content <- readUTF8FileT filename
   return $ either (Left . P.prettyPrintMultipleErrors P.defaultPPEOptions) (Right . map snd) $ P.parseModulesFromFiles id [(filename, content)]
 
 -- |
@@ -34,7 +34,7 @@
 loadAllModules :: [FilePath] -> IO (Either P.MultipleErrors [(FilePath, P.Module)])
 loadAllModules files = do
   filesAndContent <- forM files $ \filename -> do
-    content <- readUTF8File filename
+    content <- readUTF8FileT filename
     return (filename, content)
   return $ P.parseModulesFromFiles id filesAndContent
 
diff --git a/src/Language/PureScript/Interactive/Parser.hs b/src/Language/PureScript/Interactive/Parser.hs
--- a/src/Language/PureScript/Interactive/Parser.hs
+++ b/src/Language/PureScript/Interactive/Parser.hs
@@ -62,6 +62,7 @@
     Help    -> return ShowHelp
     Quit    -> return QuitPSCi
     Reset   -> return ResetState
+    Paste   -> return PasteLines
     Browse  -> BrowseModule <$> parseRest P.moduleName arg
     Show    -> ShowInfo <$> parseReplQuery' (trim arg)
     Type    -> TypeOf <$> parseRest P.parseValue arg
diff --git a/src/Language/PureScript/Interactive/Types.hs b/src/Language/PureScript/Interactive/Types.hs
--- a/src/Language/PureScript/Interactive/Types.hs
+++ b/src/Language/PureScript/Interactive/Types.hs
@@ -72,46 +72,28 @@
 -- Valid Meta-commands for PSCI
 --
 data Command
-  -- |
-  -- A purescript expression
-  --
+  -- | A purescript expression
   = Expression P.Expr
-  -- |
-  -- Show the help (ie, list of directives)
-  --
+  -- | Show the help (ie, list of directives)
   | ShowHelp
-  -- |
-  -- Import a module from a loaded file
-  --
+  -- | Import a module from a loaded file
   | Import ImportedModule
-  -- |
-  -- Browse a module
-  --
+  -- | Browse a module
   | BrowseModule P.ModuleName
-  -- |
-  -- Exit PSCI
-  --
+  -- | Exit PSCI
   | QuitPSCi
-  -- |
-  -- Reset the state of the REPL
-  --
+  -- | Reset the state of the REPL
   | ResetState
-  -- |
-  -- Add some declarations to the current evaluation context.
-  --
+  -- | Add some declarations to the current evaluation context
   | Decls [P.Declaration]
-  -- |
-  -- Find the type of an expression
-  --
+  -- | Find the type of an expression
   | TypeOf P.Expr
-  -- |
-  -- Find the kind of an expression
-  --
+  -- | Find the kind of an expression
   | KindOf P.Type
-  -- |
-  -- Shows information about the current state of the REPL
-  --
+  -- | Shows information about the current state of the REPL
   | ShowInfo ReplQuery
+  -- | Paste multiple lines
+  | PasteLines
 
 data ReplQuery
   = QueryLoaded
@@ -142,4 +124,5 @@
   | Type
   | Kind
   | Show
+  | Paste
   deriving (Eq, Show)
diff --git a/src/Language/PureScript/Linter/Imports.hs b/src/Language/PureScript/Linter/Imports.hs
--- a/src/Language/PureScript/Linter/Imports.hs
+++ b/src/Language/PureScript/Linter/Imports.hs
@@ -308,7 +308,7 @@
     typesRefs
       = map (flip TypeRef (Just [])) typesWithoutDctors
       ++ map (\(ty, ds) -> TypeRef ty (Just ds)) (M.toList typesWithDctors)
-  in classRefs ++ typeOpRefs ++ typesRefs ++ valueRefs ++ valueOpRefs
+  in sortBy compDecRef $ classRefs ++ typeOpRefs ++ typesRefs ++ valueRefs ++ valueOpRefs
 
   where
 
diff --git a/src/Language/PureScript/Make.hs b/src/Language/PureScript/Make.hs
--- a/src/Language/PureScript/Make.hs
+++ b/src/Language/PureScript/Make.hs
@@ -149,7 +149,7 @@
   lint withPrim
   ((Module ss coms _ elaborated exps, env'), nextVar) <- runSupplyT 0 $ do
     [desugared] <- desugar externs [withPrim]
-    runCheck' env $ typeCheckModule desugared
+    runCheck' (emptyCheckState env) $ typeCheckModule desugared
   regrouped <- createBindingGroups moduleName . collapseBindingGroups $ elaborated
   let mod' = Module ss coms moduleName regrouped exps
       corefn = CF.moduleToCoreFn env' mod'
diff --git a/src/Language/PureScript/Parser/Common.hs b/src/Language/PureScript/Parser/Common.hs
--- a/src/Language/PureScript/Parser/Common.hs
+++ b/src/Language/PureScript/Parser/Common.hs
@@ -29,6 +29,12 @@
 typeName = ProperName <$> tyname
 
 -- |
+-- Parse a proper name for a data constructor.
+--
+dataConstructorName :: TokenParser (ProperName 'ConstructorName)
+dataConstructorName = ProperName <$> dconsname
+
+-- |
 -- Parse a module name
 --
 moduleName :: TokenParser ModuleName
@@ -107,23 +113,26 @@
 -- |
 -- Check that the current identation level matches a predicate
 --
-checkIndentation :: (P.Column -> P.Column -> Bool) -> P.Parsec s ParseState ()
-checkIndentation rel = do
+checkIndentation
+  :: (P.Column -> String)
+  -> (P.Column -> P.Column -> Bool)
+  -> P.Parsec s ParseState ()
+checkIndentation mkMsg rel = do
   col <- P.sourceColumn <$> P.getPosition
   current <- indentationLevel <$> P.getState
-  guard (col `rel` current)
+  guard (col `rel` current) P.<?> mkMsg current
 
 -- |
 -- Check that the current indentation level is past the current mark
 --
 indented :: P.Parsec s ParseState ()
-indented = checkIndentation (>) P.<?> "indentation"
+indented = checkIndentation (("indentation past column " ++) . show) (>)
 
 -- |
 -- Check that the current indentation level is at the same indentation as the current mark
 --
 same :: P.Parsec s ParseState ()
-same = checkIndentation (==) P.<?> "no indentation"
+same = checkIndentation (("indentation at column " ++) . show) (==)
 
 -- |
 -- Read the comments from the the next token, without consuming it
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
@@ -19,6 +19,7 @@
 
 import Data.Functor (($>))
 import Data.Maybe (fromMaybe)
+import Data.Text (Text)
 
 import Control.Applicative
 import Control.Arrow ((+++))
@@ -71,7 +72,7 @@
   tyArgs <- many (indented *> kindedIdent)
   ctors <- P.option [] $ do
     indented *> equals
-    P.sepBy1 ((,) <$> properName <*> P.many (indented *> noWildcards parseTypeAtom)) pipe
+    P.sepBy1 ((,) <$> dataConstructorName <*> P.many (indented *> noWildcards parseTypeAtom)) pipe
   return $ DataDeclaration dtype name tyArgs ctors
 
 parseTypeDeclaration :: TokenParser Declaration
@@ -281,7 +282,7 @@
   :: forall m k
    . MonadError MultipleErrors m
   => (k -> FilePath)
-  -> [(k, String)]
+  -> [(k, Text)]
   -> m [(k, Module)]
 parseModulesFromFiles toFilePath input =
   flip parU wrapError . inParallel . flip map input $ parseModuleFromFile toFilePath
@@ -298,11 +299,11 @@
 -- | Parses a single module with FilePath for eventual parsing errors
 parseModuleFromFile
   :: (k -> FilePath)
-  -> (k, String)
+  -> (k, Text)
   -> Either P.ParseError (k, Module)
 parseModuleFromFile toFilePath (k, content) = do
     let filename = toFilePath k
-    ts <- lex filename content
+    ts <- lex' filename content
     m <- runTokenParser filename parseModule ts
     pure (k, m)
 
@@ -360,7 +361,7 @@
 parseVar = Var <$> C.parseQualified C.parseIdent
 
 parseConstructor :: TokenParser Expr
-parseConstructor = Constructor <$> C.parseQualified C.properName
+parseConstructor = Constructor <$> C.parseQualified C.dataConstructorName
 
 parseCase :: TokenParser Expr
 parseCase = Case <$> P.between (reserved "case") (C.indented *> reserved "of") (commaSep1 parseValue)
@@ -494,10 +495,10 @@
          <|> return id
 
 parseNullaryConstructorBinder :: TokenParser Binder
-parseNullaryConstructorBinder = ConstructorBinder <$> C.parseQualified C.properName <*> pure []
+parseNullaryConstructorBinder = ConstructorBinder <$> C.parseQualified C.dataConstructorName <*> pure []
 
 parseConstructorBinder :: TokenParser Binder
-parseConstructorBinder = ConstructorBinder <$> C.parseQualified C.properName <*> many (C.indented *> parseBinderNoParens)
+parseConstructorBinder = ConstructorBinder <$> C.parseQualified C.dataConstructorName <*> many (C.indented *> parseBinderNoParens)
 
 parseObjectBinder:: TokenParser Binder
 parseObjectBinder = LiteralBinder <$> parseObjectLiteral (C.indented *> parseIdentifierAndBinder)
diff --git a/src/Language/PureScript/Parser/Lexer.hs b/src/Language/PureScript/Parser/Lexer.hs
--- a/src/Language/PureScript/Parser/Lexer.hs
+++ b/src/Language/PureScript/Parser/Lexer.hs
@@ -6,6 +6,7 @@
   , Token()
   , TokenParser()
   , lex
+  , lex'
   , anyToken
   , token
   , match
@@ -43,6 +44,7 @@
   , lname'
   , qualifier
   , tyname
+  , dconsname
   , uname
   , uname'
   , mname
@@ -68,6 +70,8 @@
 
 import Data.Char (isSpace, isAscii, isSymbol, isAlphaNum)
 import Data.Functor.Identity
+import Data.Text (Text)
+import qualified Data.Text as T
 
 import Language.PureScript.Comments
 import Language.PureScript.Parser.State
@@ -153,31 +157,36 @@
 instance Show PositionedToken where
   show = prettyPrintToken . ptToken
 
+type Lexer u a = P.Parsec Text u a
+
 lex :: FilePath -> String -> Either P.ParseError [PositionedToken]
-lex f s = updatePositions <$> P.parse parseTokens f s
+lex fp = lex' fp . T.pack
 
+lex' :: FilePath -> Text -> Either P.ParseError [PositionedToken]
+lex' f s = updatePositions <$> P.parse parseTokens f s
+
 updatePositions :: [PositionedToken] -> [PositionedToken]
 updatePositions [] = []
 updatePositions (x:xs) = x : zipWith update (x:xs) xs
   where
   update PositionedToken { ptEndPos = pos } pt = pt { ptPrevEndPos = Just pos }
 
-parseTokens :: P.Parsec String u [PositionedToken]
+parseTokens :: Lexer u [PositionedToken]
 parseTokens = whitespace *> P.many parsePositionedToken <* P.skipMany parseComment <* P.eof
 
-whitespace :: P.Parsec String u ()
+whitespace :: Lexer u ()
 whitespace = P.skipMany (P.satisfy isSpace)
 
-parseComment :: P.Parsec String u Comment
+parseComment :: Lexer u Comment
 parseComment = (BlockComment <$> blockComment <|> LineComment <$> lineComment) <* whitespace
   where
-  blockComment :: P.Parsec String u String
+  blockComment :: Lexer u String
   blockComment = P.try $ P.string "{-" *> P.manyTill P.anyChar (P.try (P.string "-}"))
 
-  lineComment :: P.Parsec String u String
+  lineComment :: Lexer u String
   lineComment = P.try $ P.string "--" *> P.manyTill P.anyChar (P.try (void (P.char '\n') <|> P.eof))
 
-parsePositionedToken :: P.Parsec String u PositionedToken
+parsePositionedToken :: Lexer u PositionedToken
 parsePositionedToken = P.try $ do
   comments <- P.many parseComment
   pos <- P.getPosition
@@ -186,7 +195,7 @@
   whitespace
   return $ PositionedToken pos pos' Nothing tok comments
 
-parseToken :: P.Parsec String u Token
+parseToken :: Lexer u Token
 parseToken = P.choice
   [ P.try $ P.string "<-" *> P.notFollowedBy symbolChar *> pure LArrow
   , P.try $ P.string "←"  *> P.notFollowedBy symbolChar *> pure LArrow
@@ -225,34 +234,34 @@
   ]
 
   where
-  parseLName :: P.Parsec String u String
+  parseLName :: Lexer u String
   parseLName = (:) <$> identStart <*> P.many identLetter
 
-  parseUName :: P.Parsec String u String
+  parseUName :: Lexer u String
   parseUName = (:) <$> P.upper <*> P.many identLetter
 
-  parseSymbol :: P.Parsec String u String
+  parseSymbol :: Lexer u String
   parseSymbol = P.many1 symbolChar
 
-  identStart :: P.Parsec String u Char
+  identStart :: Lexer u Char
   identStart = P.lower <|> P.oneOf "_"
 
-  identLetter :: P.Parsec String u Char
+  identLetter :: Lexer u Char
   identLetter = P.alphaNum <|> P.oneOf "_'"
 
-  symbolChar :: P.Parsec String u Char
+  symbolChar :: Lexer u Char
   symbolChar = P.satisfy isSymbolChar
 
-  parseCharLiteral :: P.Parsec String u Char
+  parseCharLiteral :: Lexer u Char
   parseCharLiteral = PT.charLiteral tokenParser
 
-  parseStringLiteral :: P.Parsec String u String
+  parseStringLiteral :: Lexer u String
   parseStringLiteral = blockString <|> PT.stringLiteral tokenParser
     where
     delimiter   = P.try (P.string "\"\"\"")
     blockString = delimiter >> P.manyTill P.anyChar delimiter
 
-  parseNumber :: P.Parsec String u (Either Integer Double)
+  parseNumber :: Lexer u (Either Integer Double)
   parseNumber = (consumeLeadingZero >> P.parserZero) <|>
                   (Right <$> P.try (PT.float tokenParser) <|>
                   Left <$> P.try (PT.natural tokenParser))
@@ -266,7 +275,7 @@
 -- |
 -- We use Text.Parsec.Token to implement the string and number lexemes
 --
-langDef :: PT.GenLanguageDef String u Identity
+langDef :: PT.GenLanguageDef Text u Identity
 langDef = PT.LanguageDef
   { PT.reservedNames   = []
   , PT.reservedOpNames = []
@@ -284,7 +293,7 @@
 -- |
 -- A token parser based on the language definition
 --
-tokenParser :: PT.GenTokenParser String u Identity
+tokenParser :: PT.GenTokenParser Text u Identity
 tokenParser = PT.makeTokenParser langDef
 
 type TokenParser a = P.Parsec [PositionedToken] ParseState a
@@ -448,6 +457,12 @@
 
 tyname :: TokenParser String
 tyname = token go P.<?> "type name"
+  where
+  go (UName s) = Just s
+  go _ = Nothing
+
+dconsname :: TokenParser String
+dconsname = token go P.<?> "data constructor name"
   where
   go (UName s) = Just s
   go _ = Nothing
diff --git a/src/Language/PureScript/Pretty/Types.hs b/src/Language/PureScript/Pretty/Types.hs
--- a/src/Language/PureScript/Pretty/Types.hs
+++ b/src/Language/PureScript/Pretty/Types.hs
@@ -3,7 +3,9 @@
 --
 module Language.PureScript.Pretty.Types
   ( typeAsBox
+  , suggestedTypeAsBox
   , prettyPrintType
+  , prettyPrintSuggestedType
   , typeAtomAsBox
   , prettyPrintTypeAtom
   , prettyPrintRowWith
@@ -27,23 +29,6 @@
 
 import Text.PrettyPrint.Boxes hiding ((<+>))
 
-typeLiterals :: Pattern () Type Box
-typeLiterals = mkPattern match
-  where
-  match TypeWildcard{} = Just $ text "_"
-  match (TypeVar var) = Just $ text var
-  match (TypeLevelString s) = Just . text $ show s
-  match (PrettyPrintObject row) = Just $ prettyPrintRowWith '{' '}' row
-  match (TypeConstructor ctor) = Just $ text $ runProperName $ disqualify ctor
-  match (TUnknown u) = Just $ text $ 't' : show u
-  match (Skolem name s _ _) = Just $ text $ name ++ show s
-  match REmpty = Just $ text "()"
-  match row@RCons{} = Just $ prettyPrintRowWith '(' ')' row
-  match (BinaryNoParensType op l r) =
-    Just $ typeAsBox l <> text " " <> typeAsBox op <> text " " <> typeAsBox r
-  match (TypeOp op) = Just $ text $ showQualified runOpName op
-  match _ = Nothing
-
 constraintsAsBox :: [Constraint] -> Box -> Box
 constraintsAsBox [con] ty = text "(" <> constraintAsBox con `before` (text ") => " <> ty)
 constraintsAsBox xs ty = vcat left (zipWith (\i con -> text (if i == 0 then "( " else ", ") <> constraintAsBox con) [0 :: Int ..] xs) `before` (text ") => " <> ty)
@@ -120,12 +105,32 @@
   match (ParensInType ty) = Just ((), ty)
   match _ = Nothing
 
-matchTypeAtom :: Pattern () Type Box
-matchTypeAtom = typeLiterals <+> fmap ((`before` (text ")")) . (text "(" <>)) matchType
-
-matchType :: Pattern () Type Box
-matchType = buildPrettyPrinter operators matchTypeAtom
+matchTypeAtom :: Bool -> Pattern () Type Box
+matchTypeAtom suggesting =
+    typeLiterals <+> fmap ((`before` (text ")")) . (text "(" <>)) (matchType suggesting)
   where
+    typeLiterals :: Pattern () Type Box
+    typeLiterals = mkPattern match where
+      match TypeWildcard{} = Just $ text "_"
+      match (TypeVar var) = Just $ text var
+      match (TypeLevelString s) = Just . text $ show s
+      match (PrettyPrintObject row) = Just $ prettyPrintRowWith '{' '}' row
+      match (TypeConstructor ctor) = Just $ text $ runProperName $ disqualify ctor
+      match (TUnknown u)
+        | suggesting = Just $ text "_"
+        | otherwise = Just $ text $ 't' : show u
+      match (Skolem name s _ _)
+        | suggesting =  Just $ text name
+        | otherwise = Just $ text $ name ++ show s
+      match REmpty = Just $ text "()"
+      match row@RCons{} = Just $ prettyPrintRowWith '(' ')' row
+      match (BinaryNoParensType op l r) =
+        Just $ typeAsBox l <> text " " <> typeAsBox op <> text " " <> typeAsBox r
+      match (TypeOp op) = Just $ text $ showQualified runOpName op
+      match _ = Nothing
+
+matchType :: Bool -> Pattern () Type Box
+matchType = buildPrettyPrinter operators . matchTypeAtom where
   operators :: OperatorTable () Type Box
   operators =
     OperatorTable [ [ AssocL typeApp $ \f x -> keepSingleLinesOr (moveRight 2) f x ]
@@ -152,7 +157,7 @@
 typeAtomAsBox :: Type -> Box
 typeAtomAsBox
   = fromMaybe (internalError "Incomplete pattern")
-  . PA.pattern matchTypeAtom ()
+  . PA.pattern (matchTypeAtom False) ()
   . insertPlaceholders
 
 -- | Generate a pretty-printed string representing a Type, as it should appear inside parentheses
@@ -160,11 +165,21 @@
 prettyPrintTypeAtom = render . typeAtomAsBox
 
 typeAsBox :: Type -> Box
-typeAsBox
+typeAsBox = typeAsBoxImpl False
+
+suggestedTypeAsBox :: Type -> Box
+suggestedTypeAsBox = typeAsBoxImpl True
+
+typeAsBoxImpl :: Bool -> Type -> Box
+typeAsBoxImpl suggesting
   = fromMaybe (internalError "Incomplete pattern")
-  . PA.pattern matchType ()
+  . PA.pattern (matchType suggesting) ()
   . insertPlaceholders
 
--- | Generate a pretty-printed string representing a Type
+-- | Generate a pretty-printed string representing a 'Type'
 prettyPrintType :: Type -> String
-prettyPrintType = render . typeAsBox
+prettyPrintType = render . typeAsBoxImpl False
+
+-- | Generate a pretty-printed string representing a suggested 'Type'
+prettyPrintSuggestedType :: Type -> String
+prettyPrintSuggestedType = render . typeAsBoxImpl True
diff --git a/src/Language/PureScript/Sugar/TypeClasses/Deriving.hs b/src/Language/PureScript/Sugar/TypeClasses/Deriving.hs
--- a/src/Language/PureScript/Sugar/TypeClasses/Deriving.hs
+++ b/src/Language/PureScript/Sugar/TypeClasses/Deriving.hs
@@ -13,7 +13,7 @@
 import Control.Monad.Error.Class (MonadError(..))
 import Control.Monad.Supply.Class (MonadSupply)
 
-import Data.List (foldl', find, sortBy)
+import Data.List (foldl', find, sortBy, unzip5)
 import Data.Maybe (fromMaybe)
 import Data.Ord (comparing)
 
@@ -58,9 +58,14 @@
   | className == Qualified (Just dataNewtype) (ProperName "Newtype")
   , Just (Qualified mn' tyCon, args) <- unwrapTypeConstructor wrappedTy
   , mn == fromMaybe mn mn'
-  = do
-    (inst, actualUnwrappedTy) <- deriveNewtype mn ds tyCon args unwrappedTy
-    return $ TypeInstanceDeclaration nm deps className [wrappedTy, actualUnwrappedTy] (ExplicitInstance inst)
+  = do (inst, actualUnwrappedTy) <- deriveNewtype mn ds tyCon args unwrappedTy
+       return $ TypeInstanceDeclaration nm deps className [wrappedTy, actualUnwrappedTy] (ExplicitInstance inst)
+deriveInstance mn ds (TypeInstanceDeclaration nm deps className [actualTy, repTy] DerivedInstance)
+  | className == Qualified (Just dataGenericRep) (ProperName C.generic)
+  , Just (Qualified mn' tyCon, args) <- unwrapTypeConstructor actualTy
+  , mn == fromMaybe mn mn'
+  = do (inst, inferredRepTy) <- deriveGenericRep mn ds tyCon args repTy
+       return $ TypeInstanceDeclaration nm deps className [actualTy, inferredRepTy] (ExplicitInstance inst)
 deriveInstance _ _ (TypeInstanceDeclaration _ _ className tys DerivedInstance)
   = throwError . errorMessage $ CannotDerive className tys
 deriveInstance mn ds (TypeInstanceDeclaration nm deps className tys@(_ : _) NewtypeInstance)
@@ -122,6 +127,9 @@
 dataGeneric :: ModuleName
 dataGeneric = ModuleName [ ProperName "Data", ProperName "Generic" ]
 
+dataGenericRep :: ModuleName
+dataGenericRep = ModuleName [ ProperName "Data", ProperName "Generic", ProperName "Rep" ]
+
 dataMaybe :: ModuleName
 dataMaybe = ModuleName [ ProperName "Data", ProperName "Maybe" ]
 
@@ -304,6 +312,187 @@
     mkGenVar :: Ident -> Expr
     mkGenVar = mkVarMn (Just (ModuleName [ProperName "Data", ProperName C.generic]))
 
+deriveGenericRep
+  :: forall m
+   . (MonadError MultipleErrors m, MonadSupply m)
+  => ModuleName
+  -> [Declaration]
+  -> ProperName 'TypeName
+  -> [Type]
+  -> Type
+  -> m ([Declaration], Type)
+deriveGenericRep mn ds tyConNm tyConArgs repTy = do
+    checkIsWildcard tyConNm repTy
+    go =<< findTypeDecl tyConNm ds
+  where
+    go :: Declaration -> m ([Declaration], Type)
+    go (DataDeclaration _ _ args dctors) = do
+      x <- freshIdent "x"
+      (reps, to, from) <- unzip3 <$> traverse makeInst dctors
+      let rep = toRepTy reps
+          inst | null reps =
+                   -- If there are no cases, spin
+                   [ ValueDeclaration (Ident "to") Public [] $ Right $
+                       lamCase x [ CaseAlternative [NullBinder]
+                                                   (Right (App toName (Var (Qualified Nothing x))))
+                                 ]
+                   , ValueDeclaration (Ident "from") Public [] $ Right $
+                       lamCase x [ CaseAlternative [NullBinder]
+                                                   (Right (App fromName (Var (Qualified Nothing x))))
+                                 ]
+                   ]
+               | otherwise =
+                   [ ValueDeclaration (Ident "to") Public [] $ Right $
+                       lamCase x (zipWith ($) (map underBinder (sumBinders (length dctors))) to)
+                   , ValueDeclaration (Ident "from") Public [] $ Right $
+                       lamCase x (zipWith ($) (map underExpr (sumExprs (length dctors))) from)
+                   ]
+
+          subst = zipWith ((,) . fst) args tyConArgs
+      return (inst, replaceAllTypeVars subst rep)
+    go (PositionedDeclaration _ _ d) = go d
+    go _ = internalError "deriveGenericRep go: expected DataDeclaration"
+
+    select :: (a -> a) -> (a -> a) -> Int -> [a -> a]
+    select _ _ 0 = []
+    select _ _ 1 = [id]
+    select l r n = take (n - 1) (iterate (r .) l) ++ [compN (n - 1) r]
+
+    sumBinders :: Int -> [Binder -> Binder]
+    sumBinders = select (ConstructorBinder inl . pure) (ConstructorBinder inr . pure)
+
+    sumExprs :: Int -> [Expr -> Expr]
+    sumExprs = select (App (Constructor inl)) (App (Constructor inr))
+
+    compN :: Int -> (a -> a) -> a -> a
+    compN 0 _ = id
+    compN n f = f . compN (n - 1) f
+
+    makeInst
+      :: (ProperName 'ConstructorName, [Type])
+      -> m (Type, CaseAlternative, CaseAlternative)
+    makeInst (ctorName, args) = do
+        (ctorTy, matchProduct, ctorArgs, matchCtor, mkProduct) <- makeProduct args
+        return ( TypeApp (TypeApp (TypeConstructor constructor)
+                                  (TypeLevelString (runProperName ctorName)))
+                         ctorTy
+               , CaseAlternative [ ConstructorBinder constructor [matchProduct] ]
+                                 (Right (foldl App (Constructor (Qualified (Just mn) ctorName)) ctorArgs))
+               , CaseAlternative [ ConstructorBinder (Qualified (Just mn) ctorName) matchCtor ]
+                                 (Right (constructor' mkProduct))
+               )
+
+    makeProduct
+      :: [Type]
+      -> m (Type, Binder, [Expr], [Binder], Expr)
+    makeProduct [] =
+      pure (noArgs, NullBinder, [], [], noArgs')
+    makeProduct args = do
+      (tys, bs1, es1, bs2, es2) <- unzip5 <$> traverse makeArg args
+      pure ( foldr1 (\f -> TypeApp (TypeApp (TypeConstructor productName) f)) tys
+           , foldr1 (\b1 b2 -> ConstructorBinder productName [b1, b2]) bs1
+           , es1
+           , bs2
+           , foldr1 (\e1 -> App (App (Constructor productName) e1)) es2
+           )
+
+    makeArg :: Type -> m (Type, Binder, Expr, Binder, Expr)
+    makeArg arg | Just rec <- objectType arg = do
+      let fields = decomposeRec rec
+      fieldNames <- traverse freshIdent (map fst fields)
+      pure ( TypeApp (TypeConstructor record)
+               (foldr1 (\f -> TypeApp (TypeApp (TypeConstructor productName) f))
+                 (map (\(name, ty) ->
+                   TypeApp (TypeApp (TypeConstructor field) (TypeLevelString name)) ty) fields))
+           , ConstructorBinder record
+               [ foldr1 (\b1 b2 -> ConstructorBinder productName [b1, b2])
+                   (map (\ident -> ConstructorBinder field [VarBinder ident]) fieldNames)
+               ]
+           , Literal . ObjectLiteral $
+                zipWith (\(name, _) ident -> (name, Var (Qualified Nothing ident))) fields fieldNames
+           , LiteralBinder . ObjectLiteral $
+                 zipWith (\(name, _) ident -> (name, VarBinder ident)) fields fieldNames
+           , record' $
+               foldr1 (\e1 -> App (App (Constructor productName) e1))
+                 (map (field' . Var . Qualified Nothing) fieldNames)
+           )
+    makeArg arg = do
+      argName <- freshIdent "arg"
+      pure ( TypeApp (TypeConstructor argument) arg
+           , ConstructorBinder argument [ VarBinder argName ]
+           , Var (Qualified Nothing argName)
+           , VarBinder argName
+           , argument' (Var (Qualified Nothing argName))
+           )
+
+    underBinder :: (Binder -> Binder) -> CaseAlternative -> CaseAlternative
+    underBinder f (CaseAlternative bs e) = CaseAlternative (map f bs) e
+
+    underExpr :: (Expr -> Expr) -> CaseAlternative -> CaseAlternative
+    underExpr f (CaseAlternative b (Right e)) = CaseAlternative b (Right (f e))
+    underExpr _ _ = internalError "underExpr: expected Right"
+
+    toRepTy :: [Type] -> Type
+    toRepTy [] = noCtors
+    toRepTy [only] = only
+    toRepTy ctors = foldr1 (\f -> TypeApp (TypeApp sumCtor f)) ctors
+
+    toName :: Expr
+    toName = Var (Qualified (Just dataGenericRep) (Ident "to"))
+
+    fromName :: Expr
+    fromName = Var (Qualified (Just dataGenericRep) (Ident "from"))
+
+    noCtors :: Type
+    noCtors = TypeConstructor (Qualified (Just dataGenericRep) (ProperName "NoConstructors"))
+
+    noArgs :: Type
+    noArgs = TypeConstructor (Qualified (Just dataGenericRep) (ProperName "NoArguments"))
+
+    noArgs' :: Expr
+    noArgs' = Constructor (Qualified (Just dataGenericRep) (ProperName "NoArguments"))
+
+    sumCtor :: Type
+    sumCtor = TypeConstructor (Qualified (Just dataGenericRep) (ProperName "Sum"))
+
+    inl :: Qualified (ProperName 'ConstructorName)
+    inl = Qualified (Just dataGenericRep) (ProperName "Inl")
+
+    inr :: Qualified (ProperName 'ConstructorName)
+    inr = Qualified (Just dataGenericRep) (ProperName "Inr")
+
+    productName :: Qualified (ProperName ty)
+    productName = Qualified (Just dataGenericRep) (ProperName "Product")
+
+    constructor :: Qualified (ProperName ty)
+    constructor = Qualified (Just dataGenericRep) (ProperName "Constructor")
+
+    constructor' :: Expr -> Expr
+    constructor' = App (Constructor constructor)
+
+    argument :: Qualified (ProperName ty)
+    argument = Qualified (Just dataGenericRep) (ProperName "Argument")
+
+    argument' :: Expr -> Expr
+    argument' = App (Constructor argument)
+
+    record :: Qualified (ProperName ty)
+    record = Qualified (Just dataGenericRep) (ProperName "Rec")
+
+    record' :: Expr -> Expr
+    record' = App (Constructor record)
+
+    field :: Qualified (ProperName ty)
+    field = Qualified (Just dataGenericRep) (ProperName "Field")
+
+    field' :: Expr -> Expr
+    field' = App (Constructor field)
+
+checkIsWildcard :: MonadError MultipleErrors m => ProperName 'TypeName -> Type -> m ()
+checkIsWildcard _ (TypeWildcard _) = return ()
+checkIsWildcard tyConNm _ =
+  throwError . errorMessage $ ExpectedWildcard tyConNm
+
 deriveEq ::
   forall m. (MonadError MultipleErrors m, MonadSupply m)
   => ModuleName
@@ -451,7 +640,7 @@
   -> Type
   -> m ([Declaration], Type)
 deriveNewtype mn ds tyConNm tyConArgs unwrappedTy = do
-    checkIsWildcard unwrappedTy
+    checkIsWildcard tyConNm unwrappedTy
     go =<< findTypeDecl tyConNm ds
   where
     go :: Declaration -> m ([Declaration], Type)
@@ -476,12 +665,6 @@
       return (inst, replaceAllTypeVars subst ty)
     go (PositionedDeclaration _ _ d) = go d
     go _ = internalError "deriveNewtype go: expected DataDeclaration"
-
-    checkIsWildcard :: Type -> m ()
-    checkIsWildcard (TypeWildcard _) =
-      return ()
-    checkIsWildcard _ =
-      throwError . errorMessage $ NonWildcardNewtypeInstance tyConNm
 
 findTypeDecl
   :: (MonadError MultipleErrors m)
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
@@ -11,7 +11,7 @@
 
 import Prelude.Compat
 
-import Control.Monad (when, unless, void, forM, forM_)
+import Control.Monad (when, unless, void, forM)
 import Control.Monad.Error.Class (MonadError(..))
 import Control.Monad.State.Class (MonadState(..), modify)
 import Control.Monad.Supply.Class (MonadSupply)
@@ -279,7 +279,6 @@
     return d
   go (d@(TypeInstanceDeclaration dictName deps className tys body)) = rethrow (addHint (ErrorInInstance className tys)) $ do
     traverse_ (checkTypeClassInstance moduleName) tys
-    forM_ deps $ traverse_ (checkTypeClassInstance moduleName) . constraintArgs
     checkOrphanInstance dictName className tys
     _ <- traverseTypeInstanceBody checkInstanceMembers body
     let dict = TypeClassDictionaryInScope (Qualified (Just moduleName) dictName) [] className tys (Just deps)
@@ -313,6 +312,7 @@
     where
     checkType :: Type -> Bool
     checkType (TypeVar _) = False
+    checkType (TypeLevelString _) = False
     checkType (TypeConstructor (Qualified (Just mn'') _)) = moduleName == mn''
     checkType (TypeConstructor (Qualified Nothing _)) = internalError "Unqualified type name in checkOrphanInstance"
     checkType (TypeApp t1 _) = checkType t1
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
@@ -5,8 +5,10 @@
 --
 module Language.PureScript.TypeChecker.Entailment
   ( InstanceContext
+  , SolverOptions(..)
   , replaceTypeClassDictionaries
   , newDictionaries
+  , entails
   ) where
 
 import Prelude.Compat
@@ -56,7 +58,7 @@
    . (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
   => Bool
   -> Expr
-  -> m (Expr, [(Ident, Constraint)])
+  -> m (Expr, [(Ident, InstanceContext, Constraint)])
 replaceTypeClassDictionaries shouldGeneralize expr = flip evalStateT M.empty $ do
     -- Loop, deferring any unsolved constraints, until there are no more
     -- constraints which can be solved, then make a generalization pass.
@@ -70,18 +72,18 @@
     -- This pass solves constraints where possible, deferring constraints if not.
     deferPass :: Expr -> StateT InstanceContext m (Expr, Any)
     deferPass = fmap (second fst) . runWriterT . f where
-      f :: Expr -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
+      f :: Expr -> WriterT (Any, [(Ident, InstanceContext, Constraint)]) (StateT InstanceContext m) Expr
       (_, f, _) = everywhereOnValuesTopDownM return (go True) return
 
     -- This pass generalizes any remaining constraints
-    generalizePass :: Expr -> StateT InstanceContext m (Expr, [(Ident, Constraint)])
+    generalizePass :: Expr -> StateT InstanceContext m (Expr, [(Ident, InstanceContext, Constraint)])
     generalizePass = fmap (second snd) . runWriterT . f where
-      f :: Expr -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
+      f :: Expr -> WriterT (Any, [(Ident, InstanceContext, Constraint)]) (StateT InstanceContext m) Expr
       (_, f, _) = everywhereOnValuesTopDownM return (go False) return
 
-    go :: Bool -> Expr -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
-    go deferErrors dict@(TypeClassDictionary _ _ hints) =
-      rethrow (addHints hints) $ entails shouldGeneralize deferErrors dict
+    go :: Bool -> Expr -> WriterT (Any, [(Ident, InstanceContext, Constraint)]) (StateT InstanceContext m) Expr
+    go deferErrors (TypeClassDictionary constraint context hints) =
+      rethrow (addHints hints) $ entails (SolverOptions shouldGeneralize deferErrors) constraint context hints
     go _ other = return other
 
 -- | Three options for how we can handle a constraint, depending on the mode we're in.
@@ -93,18 +95,29 @@
   | Deferred
   -- ^ We couldn't solve this constraint right now, so it has been deferred
 
--- |
--- Check that the current set of type class dictionaries entail the specified type class goal, and, if so,
+-- | Options for the constraint solver
+data SolverOptions = SolverOptions
+  { solverShouldGeneralize :: Bool
+  -- ^ Should the solver be allowed to generalize over unsolved constraints?
+  , solverDeferErrors      :: Bool
+  -- ^ Should the solver be allowed to defer errors by skipping constraints?
+  }
+
+-- | Check that the current set of type class dictionaries entail the specified type class goal, and, if so,
 -- return a type class dictionary reference.
---
 entails
   :: forall m
    . (MonadState CheckState m, MonadError MultipleErrors m, MonadWriter MultipleErrors m, MonadSupply m)
-  => Bool
-  -> Bool
-  -> Expr
-  -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
-entails shouldGeneralize deferErrors (TypeClassDictionary constraint context hints) =
+  => SolverOptions
+  -- ^ Solver options
+  -> Constraint
+  -- ^ The constraint to solve
+  -> InstanceContext
+  -- ^ The contexts in which to solve the constraint
+  -> [ErrorMessageHint]
+  -- ^ Error message hints to apply to any instance errors
+  -> WriterT (Any, [(Ident, InstanceContext, Constraint)]) (StateT InstanceContext m) Expr
+entails SolverOptions{..} constraint context hints =
     solve constraint
   where
     forClassName :: InstanceContext -> Qualified (ProperName 'ClassName) -> [Type] -> [TypeClassDictionaryInScope]
@@ -123,10 +136,10 @@
     valUndefined :: Expr
     valUndefined = Var (Qualified (Just (ModuleName [ProperName C.prim])) (Ident C.undefined))
 
-    solve :: Constraint -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
+    solve :: Constraint -> WriterT (Any, [(Ident, InstanceContext, Constraint)]) (StateT InstanceContext m) Expr
     solve con = go 0 con
       where
-        go :: Int -> Constraint -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) Expr
+        go :: Int -> Constraint -> WriterT (Any, [(Ident, InstanceContext, Constraint)]) (StateT InstanceContext m) Expr
         go work (Constraint className' tys' _) | work > 1000 = throwError . errorMessage $ PossiblyInfiniteInstance className' tys'
         go work con'@(Constraint className' tys' conInfo) = WriterT . StateT . (withErrorMessageHint (ErrorSolvingConstraint con') .) . runStateT . runWriterT $ do
             -- We might have unified types by solving other constraints, so we need to
@@ -137,8 +150,10 @@
             inferred <- lift get
             -- We need information about functional dependencies, so we have to look up the class
             -- name in the environment:
-            let findClass = fromMaybe (internalError "entails: type class not found in environment") . M.lookup className'
-            TypeClassData{ typeClassDependencies } <- lift . lift $ gets (findClass . typeClasses . checkEnv)
+            classesInScope <- lift . lift $ gets (typeClasses . checkEnv)
+            TypeClassData{ typeClassDependencies } <- case M.lookup className' classesInScope of
+              Nothing -> throwError . errorMessage $ UnknownClass className'
+              Just tcd -> pure tcd
             let instances =
                   [ (substs, tcd)
                   | tcd <- forClassName (combineContexts context inferred) className' tys''
@@ -179,7 +194,7 @@
                 let newContext = mkContext newDicts
                 modify (combineContexts newContext)
                 -- Mark this constraint for generalization
-                tell (mempty, [(ident, unsolved)])
+                tell (mempty, [(ident, context, unsolved)])
                 return (Var qident)
               Deferred ->
                 -- Constraint was deferred, just return the dictionary unchanged,
@@ -219,10 +234,10 @@
 
             unique :: [Type] -> [(a, TypeClassDictionaryInScope)] -> m (EntailsResult a)
             unique tyArgs []
-              | deferErrors = return Deferred
+              | solverDeferErrors = return Deferred
               -- We need a special case for nullary type classes, since we want
               -- to generalize over Partial constraints.
-              | shouldGeneralize && (null tyArgs || any canBeGeneralized tyArgs) = return (Unsolved (Constraint className' tyArgs conInfo))
+              | solverShouldGeneralize && (null tyArgs || any canBeGeneralized tyArgs) = return (Unsolved (Constraint className' tyArgs conInfo))
               | otherwise = throwError . errorMessage $ NoInstanceFound (Constraint className' tyArgs conInfo)
             unique _      [(a, dict)] = return $ Solved a dict
             unique tyArgs tcds
@@ -233,7 +248,6 @@
 
             canBeGeneralized :: Type -> Bool
             canBeGeneralized TUnknown{} = True
-            canBeGeneralized Skolem{} = True
             canBeGeneralized _ = False
 
             -- |
@@ -251,7 +265,7 @@
             -- Create dictionaries for subgoals which still need to be solved by calling go recursively
             -- E.g. the goal (Show a, Show b) => Show (Either a b) can be satisfied if the current type
             -- unifies with Either a b, and we can satisfy the subgoals Show a and Show b recursively.
-            solveSubgoals :: Matching Type -> Maybe [Constraint] -> WriterT (Any, [(Ident, Constraint)]) (StateT InstanceContext m) (Maybe [Expr])
+            solveSubgoals :: Matching Type -> Maybe [Constraint] -> WriterT (Any, [(Ident, InstanceContext, Constraint)]) (StateT InstanceContext m) (Maybe [Expr])
             solveSubgoals _ Nothing = return Nothing
             solveSubgoals subst (Just subgoals) =
               Just <$> traverse (go (work + 1) . mapConstraintArgs (map (replaceAllTypeVars (M.toList subst)))) subgoals
@@ -268,7 +282,6 @@
           App (Accessor (C.__superclass_ ++ showQualified runProperName superclassName ++ "_" ++ show index)
                         dict)
               valUndefined
-entails _ _ _ = internalError "entails: expected TypeClassDictionary"
 
 -- Check if an instance matches our list of types, allowing for types
 -- to be solved via functional dependencies. If the types match, we return a
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
@@ -257,11 +257,11 @@
 
 -- | Run a computation in the typechecking monad, starting with an empty @Environment@
 runCheck :: (Functor m) => StateT CheckState m a -> m (a, Environment)
-runCheck = runCheck' initEnvironment
+runCheck = runCheck' (emptyCheckState initEnvironment)
 
 -- | Run a computation in the typechecking monad, failing with an error, or succeeding with a return value and the final @Environment@.
-runCheck' :: (Functor m) => Environment -> StateT CheckState m a -> m (a, Environment)
-runCheck' env check = second checkEnv <$> runStateT check (emptyCheckState env)
+runCheck' :: (Functor m) => CheckState -> StateT CheckState m a -> m (a, Environment)
+runCheck' st check = second checkEnv <$> runStateT check st
 
 -- | Make an assertion, failing with an error message
 guardWith :: (MonadError e m) => e -> Bool -> m ()
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
@@ -1,6 +1,8 @@
--- |
--- Subsumption checking
---
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE TypeFamilies #-}
+
+-- | Subsumption checking
 module Language.PureScript.TypeChecker.Subsumption
   ( subsumes
   ) where
@@ -25,72 +27,120 @@
 import Language.PureScript.TypeChecker.Unify
 import Language.PureScript.Types
 
+-- | Subsumption can operate in two modes:
+--
+-- * Elaboration mode, in which we try to insert type class dictionaries
+-- * No-elaboration mode, in which we do not insert dictionaries
+--
+-- Some subsumption rules apply in both modes, and others are specific to
+-- certain modes.
+--
+-- The subsumption algorithm follows the structure of the types in question,
+-- and we can switch into no-elaboration mode when we move under a type
+-- constructor where we can no longer insert dictionaries, e.g. into the fields
+-- of a record.
+data Mode = Elaborate | NoElaborate
+
+-- | Value-level proxies for the two modes
+data ModeSing (mode :: Mode) where
+  SElaborate   :: ModeSing 'Elaborate
+  SNoElaborate :: ModeSing 'NoElaborate
+
+-- | This type family tracks what evidence we return from 'subsumes' for each
+-- mode.
+type family Coercion (mode :: Mode) where
+  -- When elaborating, we generate a coercion
+  Coercion 'Elaborate = Expr -> Expr
+  -- When we're not elaborating, we don't generate coercions
+  Coercion 'NoElaborate = ()
+
+-- | The default coercion for each mode.
+defaultCoercion :: ModeSing mode -> Coercion mode
+defaultCoercion SElaborate   = id
+defaultCoercion SNoElaborate = ()
+
 -- | Check that one type subsumes another, rethrowing errors to provide a better error message
-subsumes :: (MonadError MultipleErrors m, MonadState CheckState m) => Maybe Expr -> Type -> Type -> m (Maybe Expr)
-subsumes val ty1 ty2 = withErrorMessageHint (ErrorInSubsumption ty1 ty2) $ subsumes' val ty1 ty2
+subsumes
+  :: (MonadError MultipleErrors m, MonadState CheckState m)
+  => Type
+  -> Type
+  -> m (Expr -> Expr)
+subsumes ty1 ty2 =
+  withErrorMessageHint (ErrorInSubsumption ty1 ty2) $
+    subsumes' SElaborate ty1 ty2
 
 -- | Check that one type subsumes another
-subsumes' :: (MonadError MultipleErrors m, MonadState CheckState m) =>
-  Maybe Expr ->
-  Type ->
-  Type ->
-  m (Maybe Expr)
-subsumes' val (ForAll ident ty1 _) ty2 = do
+subsumes'
+  :: (MonadError MultipleErrors m, MonadState CheckState m)
+  => ModeSing mode
+  -> Type
+  -> Type
+  -> m (Coercion mode)
+subsumes' mode (ForAll ident ty1 _) ty2 = do
   replaced <- replaceVarWithUnknown ident ty1
-  subsumes val replaced ty2
-subsumes' val ty1 (ForAll ident ty2 sco) =
+  subsumes' mode replaced ty2
+subsumes' mode ty1 (ForAll ident ty2 sco) =
   case sco of
     Just sco' -> do
       sko <- newSkolemConstant
       let sk = skolemize ident sko sco' Nothing ty2
-      subsumes val ty1 sk
+      subsumes' mode ty1 sk
     Nothing -> internalError "subsumes: unspecified skolem scope"
-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
-  return val
-subsumes' val (KindedType ty1 _) ty2 =
-  subsumes val ty1 ty2
-subsumes' val ty1 (KindedType ty2 _) =
-  subsumes val ty1 ty2
-subsumes' (Just val) (ConstrainedType constraints ty1) ty2 = do
+subsumes' mode (TypeApp (TypeApp f1 arg1) ret1) (TypeApp (TypeApp f2 arg2) ret2) | f1 == tyFunction && f2 == tyFunction = do
+  subsumes' SNoElaborate arg2 arg1
+  subsumes' SNoElaborate ret1 ret2
+  -- Nothing was elaborated, return the default coercion
+  return (defaultCoercion mode)
+subsumes' mode (KindedType ty1 _) ty2 =
+  subsumes' mode ty1 ty2
+subsumes' mode ty1 (KindedType ty2 _) =
+  subsumes' mode ty1 ty2
+-- Only check subsumption for constrained types when elaborating.
+-- Otherwise fall back to unification.
+subsumes' SElaborate (ConstrainedType constraints ty1) ty2 = do
   dicts <- getTypeClassDictionaries
   hints <- gets checkHints
-  subsumes' (Just $ foldl App val (map (\cs -> TypeClassDictionary cs dicts hints) constraints)) ty1 ty2
-subsumes' val (TypeApp f1 r1) (TypeApp f2 r2) | f1 == tyRecord && f2 == tyRecord = do
-  let
-    (ts1, r1') = rowToList r1
-    (ts2, r2') = rowToList r2
-    ts1' = sortBy (comparing fst) ts1
-    ts2' = sortBy (comparing fst) ts2
-  -- For { ts1 | r1 } to subsume { ts2 | r2 } when r1 is empty (= we're working with a closed row),
-  -- every property in ts2 must appear in ts1. If not, then the candidate expression is missing a required property.
-  -- Conversely, when r2 is empty, every property in ts1 must appear in ts2, or else the expression has
-  -- an additional property which is not allowed.
-  when (r1' == REmpty)
-    (for_ (firstMissingProp ts2' ts1') (throwError . errorMessage . PropertyIsMissing . fst))
-  when (r2' == REmpty)
-    (for_ (firstMissingProp ts1' ts2') (throwError . errorMessage . AdditionalProperty . fst))
-  go ts1' ts2' r1' r2'
-  return val
+  elaborate <- subsumes' SElaborate ty1 ty2
+  let addDicts val = foldl App val (map (\cs -> TypeClassDictionary cs dicts hints) constraints)
+  return (elaborate . addDicts)
+subsumes' mode (TypeApp f1 r1) (TypeApp f2 r2) | f1 == tyRecord && f2 == tyRecord = do
+    let
+      (ts1, r1') = rowToList r1
+      (ts2, r2') = rowToList r2
+      ts1' = sortBy (comparing fst) ts1
+      ts2' = sortBy (comparing fst) ts2
+    -- For { ts1 | r1 } to subsume { ts2 | r2 } when r1 is empty (= we're working with a closed row),
+    -- every property in ts2 must appear in ts1. If not, then the candidate expression is missing a required property.
+    -- Conversely, when r2 is empty, every property in ts1 must appear in ts2, or else the expression has
+    -- an additional property which is not allowed.
+    when (r1' == REmpty)
+      (for_ (firstMissingProp ts2' ts1') (throwError . errorMessage . PropertyIsMissing . fst))
+    when (r2' == REmpty)
+      (for_ (firstMissingProp ts1' ts2') (throwError . errorMessage . AdditionalProperty . fst))
+    go ts1' ts2' r1' r2'
+    -- Nothing was elaborated, return the default coercion
+    return (defaultCoercion mode)
   where
-  go [] ts2 r1' r2' = unifyTypes r1' (rowFromList (ts2, r2'))
-  go ts1 [] r1' r2' = unifyTypes r2' (rowFromList (ts1, r1'))
-  go ((p1, ty1) : ts1) ((p2, ty2) : ts2) r1' r2'
-    | p1 == p2 = do _ <- subsumes Nothing ty1 ty2
-                    go ts1 ts2 r1' r2'
-    | p1 < p2 = do rest <- freshType
-                   -- What happens next is a bit of a hack.
-                   -- TODO: in the new type checker, object properties will probably be restricted to being monotypes
-                   -- in which case, this branch of the subsumes function should not even be necessary.
-                   unifyTypes r2' (RCons p1 ty1 rest)
-                   go ts1 ((p2, ty2) : ts2) r1' rest
-    | otherwise = do rest <- freshType
-                     unifyTypes r1' (RCons p2 ty2 rest)
-                     go ((p1, ty1) : ts1) ts2 rest r2'
-  -- Find the first property that's in the first list (of tuples) but not in the second
-  firstMissingProp t1 t2 = fst <$> uncons (minusBy' (comparing fst) t1 t2)
-subsumes' val ty1 ty2@(TypeApp obj _) | obj == tyRecord = subsumes val ty2 ty1
-subsumes' val ty1 ty2 = do
+    go [] ts2 r1' r2' = unifyTypes r1' (rowFromList (ts2, r2'))
+    go ts1 [] r1' r2' = unifyTypes r2' (rowFromList (ts1, r1'))
+    go ((p1, ty1) : ts1) ((p2, ty2) : ts2) r1' r2'
+      | p1 == p2 = do subsumes' SNoElaborate ty1 ty2
+                      go ts1 ts2 r1' r2'
+      | p1 < p2 = do rest <- freshType
+                     -- What happens next is a bit of a hack.
+                     -- TODO: in the new type checker, object properties will probably be restricted to being monotypes
+                     -- in which case, this branch of the subsumes function should not even be necessary.
+                     unifyTypes r2' (RCons p1 ty1 rest)
+                     go ts1 ((p2, ty2) : ts2) r1' rest
+      | otherwise = do rest <- freshType
+                       unifyTypes r1' (RCons p2 ty2 rest)
+                       go ((p1, ty1) : ts1) ts2 rest r2'
+
+    -- Find the first property that's in the first list (of tuples) but not in the second
+    firstMissingProp t1 t2 = fst <$> uncons (minusBy' (comparing fst) t1 t2)
+subsumes' mode ty1 ty2@(TypeApp obj _) | obj == tyRecord =
+  subsumes' mode ty2 ty1
+subsumes' mode ty1 ty2 = do
   unifyTypes ty1 ty2
-  return val
+  -- Nothing was elaborated, return the default coercion
+  return (defaultCoercion mode)
diff --git a/src/Language/PureScript/TypeChecker/TypeSearch.hs b/src/Language/PureScript/TypeChecker/TypeSearch.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/PureScript/TypeChecker/TypeSearch.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+
+module Language.PureScript.TypeChecker.TypeSearch
+  ( typeSearch
+  ) where
+
+import           Protolude
+
+import           Control.Monad.Writer
+import qualified Data.Map                                    as Map
+import qualified Language.PureScript.TypeChecker.Entailment  as Entailment
+
+import qualified Language.PureScript.TypeChecker.Monad       as TC
+import           Language.PureScript.TypeChecker.Subsumption
+import           Language.PureScript.TypeChecker.Unify       as P
+
+import           Control.Monad.Supply                        as P
+import           Language.PureScript.AST                     as P
+import           Language.PureScript.Environment             as P
+import           Language.PureScript.Errors                  as P
+import           Language.PureScript.Names                   as P
+import           Language.PureScript.TypeChecker.Skolems     as Skolem
+import           Language.PureScript.TypeChecker.Synonyms    as P
+import           Language.PureScript.Types                   as P
+
+checkInEnvironment
+  :: Environment
+  -> TC.CheckState
+  -> StateT TC.CheckState (SupplyT (WriterT b (Except P.MultipleErrors))) a
+  -> Maybe (a, Environment)
+checkInEnvironment env st =
+  either (const Nothing) Just
+  . runExcept
+  . evalWriterT
+  . P.evalSupplyT 0
+  . TC.runCheck' (st { TC.checkEnv = env })
+
+evalWriterT :: Monad m => WriterT b m r -> m r
+evalWriterT m = liftM fst (runWriterT m)
+
+checkSubsume
+  :: Maybe [(P.Ident, Entailment.InstanceContext, P.Constraint)]
+  -- ^ Additional constraints we need to satisfy
+  -> P.Environment
+  -- ^ The Environment which contains the relevant definitions and typeclasses
+  -> TC.CheckState
+  -- ^ The typechecker state
+  -> P.Type
+  -- ^ The user supplied type
+  -> P.Type
+  -- ^ The type supplied by the environment
+  -> Maybe ((P.Expr, [(P.Ident, Entailment.InstanceContext, P.Constraint)]), P.Environment)
+checkSubsume unsolved env st userT envT = checkInEnvironment env st $ do
+  let initializeSkolems =
+        Skolem.introduceSkolemScope
+        <=< P.replaceAllTypeSynonyms
+        <=< P.replaceTypeWildcards
+
+  userT' <- initializeSkolems userT
+  envT' <- initializeSkolems envT
+
+  let dummyExpression = P.Var (P.Qualified Nothing (P.Ident "x"))
+
+  elab <- subsumes envT' userT'
+  subst <- gets TC.checkSubstitution
+  let expP = P.overTypes (P.substituteType subst) (elab dummyExpression)
+
+  -- Now check that any unsolved constraints have not become impossible
+  (traverse_ . traverse_) (\(_, context, constraint) -> do
+    let constraint' = P.mapConstraintArgs (map (P.substituteType subst)) constraint
+    flip evalStateT Map.empty . evalWriterT $
+      Entailment.entails
+        (Entailment.SolverOptions
+          { solverShouldGeneralize = True
+          , solverDeferErrors      = False
+          }) constraint' context []) unsolved
+
+  -- Finally, check any constraints which were found during elaboration
+  Entailment.replaceTypeClassDictionaries (isJust unsolved) expP
+
+typeSearch
+  :: Maybe [(P.Ident, Entailment.InstanceContext, P.Constraint)]
+  -- ^ Additional constraints we need to satisfy
+  -> P.Environment
+  -- ^ The Environment which contains the relevant definitions and typeclasses
+  -> TC.CheckState
+  -- ^ The typechecker state
+  -> P.Type
+  -- ^ The type we are looking for
+  -> Map (P.Qualified P.Ident) P.Type
+typeSearch unsolved env st type' =
+  Map.mapMaybe (\(x, _, _) -> checkSubsume unsolved env st type' x $> x) (P.names env)
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
@@ -1,3 +1,4 @@
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE NamedFieldPuns #-}
 
@@ -27,13 +28,16 @@
 
 import Prelude.Compat
 
+import Control.Arrow (first, second, (***))
 import Control.Monad
 import Control.Monad.Error.Class (MonadError(..))
 import Control.Monad.State.Class (MonadState(..), gets)
 import Control.Monad.Supply.Class (MonadSupply)
 import Control.Monad.Writer.Class (MonadWriter(..))
 
+import Data.Bifunctor (bimap)
 import Data.Either (lefts, rights)
+import Data.Functor (($>))
 import Data.List (transpose, nub, (\\), partition, delete)
 import Data.Maybe (fromMaybe)
 import qualified Data.Map as M
@@ -53,9 +57,11 @@
 import Language.PureScript.TypeChecker.Skolems
 import Language.PureScript.TypeChecker.Subsumption
 import Language.PureScript.TypeChecker.Synonyms
+import Language.PureScript.TypeChecker.TypeSearch
 import Language.PureScript.TypeChecker.Unify
 import Language.PureScript.Types
 
+
 data BindingGroupType
   = RecursiveBindingGroup
   | NonRecursiveBindingGroup
@@ -70,13 +76,13 @@
   -> [(Ident, Expr)]
   -> m [(Ident, (Expr, Type))]
 typesOf bindingGroupType moduleName vals = withFreshSubstitution $ do
-    (tys, w) <- withoutWarnings . capturingSubstitution tidyUp $ do
+    tys <- capturingSubstitution tidyUp $ do
       (untyped, typed, dict, untypedDict) <- typeDictionaryForBindingGroup (Just moduleName) vals
-      ds1 <- parU typed $ \e -> checkTypedBindingGroupElement moduleName e dict
-      ds2 <- forM untyped $ \e -> typeForBindingGroupElement e dict untypedDict
-      return (map (\x -> (False, x)) ds1 ++ map (\x -> (True, x)) ds2)
+      ds1 <- parU typed $ \e -> withoutWarnings $ checkTypedBindingGroupElement moduleName e dict
+      ds2 <- forM untyped $ \e -> withoutWarnings $ typeForBindingGroupElement e dict untypedDict
+      return (map (False, ) ds1 ++ map (True, ) ds2)
 
-    inferred <- forM tys $ \(shouldGeneralize, (ident, (val, ty))) -> do
+    inferred <- forM tys $ \(shouldGeneralize, ((ident, (val, ty)), _)) -> do
       -- Replace type class dictionary placeholders with actual dictionaries
       (val', unsolved) <- replaceTypeClassDictionaries shouldGeneralize val
       -- Generalize and constrain the type
@@ -96,7 +102,7 @@
           $ CannotGeneralizeRecursiveFunction ident generalized
         -- Make sure any unsolved type constraints only use type variables which appear
         -- unknown in the inferred type.
-        forM_ unsolved $ \(_, con) -> do
+        forM_ unsolved $ \(_, _, con) -> do
           -- We need information about functional dependencies, since we allow
           -- ambiguous types to be inferred if they can be solved by some functional
           -- dependency.
@@ -104,33 +110,56 @@
           TypeClassData{ typeClassDependencies } <- gets (findClass . typeClasses . checkEnv)
           let solved = foldMap (S.fromList . fdDetermined) typeClassDependencies
           let constraintTypeVars = nub . foldMap (unknownsInType . fst) . filter ((`notElem` solved) . snd) $ zip (constraintArgs con) [0..]
-          when (any (`notElem` unsolvedTypeVars) constraintTypeVars) $
-            throwError . onErrorMessages (replaceTypes currentSubst) . errorMessage $ NoInstanceFound con
+          when (any (`notElem` unsolvedTypeVars) constraintTypeVars) $ do
+            throwError . onErrorMessages (replaceTypes currentSubst) . errorMessage $ AmbiguousTypeVariables generalized con
 
       -- Check skolem variables did not escape their scope
       skolemEscapeCheck val'
       -- Check rows do not contain duplicate labels
       checkDuplicateLabels val'
-      return (ident, (foldr (Abs . Left . fst) val' unsolved, generalized))
+      return ((ident, (foldr (Abs . Left . (\(x, _, _) -> x)) val' unsolved, generalized)), unsolved)
 
     -- Show warnings here, since types in wildcards might have been solved during
     -- instance resolution (by functional dependencies).
-    finalSubst <- gets checkSubstitution
-    escalateWarningWhen isHoleError . tell . onErrorMessages (replaceTypes finalSubst) $ w
+    finalState <- get
+    forM_ tys $ \(shouldGeneralize, ((_, (_, _)), w)) -> do
+      let replaceTypes' = replaceTypes (checkSubstitution finalState)
+          runTypeSearch' = runTypeSearch (guard shouldGeneralize $> foldMap snd inferred) finalState
+      (escalateWarningWhen isHoleError . tell . onErrorMessages (runTypeSearch' . replaceTypes')) w
 
-    return inferred
+    return (map fst inferred)
   where
+    replaceTypes
+      :: Substitution
+      -> ErrorMessage
+      -> ErrorMessage
     replaceTypes subst = onTypesInErrorMessage (substituteType subst)
 
+    -- | Run type search to complete any typed hole error messages
+    runTypeSearch
+      :: Maybe [(Ident, InstanceContext, Constraint)]
+      -- ^ Any unsolved constraints which we need to continue to satisfy
+      -> CheckState
+      -- ^ The final type checker state
+      -> ErrorMessage
+      -> ErrorMessage
+    runTypeSearch cons st = \case
+      ErrorMessage hints (HoleInferredType x ty y (TSBefore env)) ->
+        let subst = checkSubstitution st
+            searchResult = (fmap . fmap) (substituteType subst)
+                             (M.toList (typeSearch cons env st (substituteType subst ty)))
+        in ErrorMessage hints (HoleInferredType x ty y (TSAfter searchResult))
+      other -> other
+
     -- | Generalize type vars using forall and add inferred constraints
     generalize unsolved = varIfUnknown . constrain unsolved
 
     -- | Add any unsolved constraints
     constrain [] = id
-    constrain cs = ConstrainedType (map snd cs)
+    constrain cs = ConstrainedType (map (\(_, _, x) -> x) cs)
 
     -- Apply the substitution that was returned from runUnify to both types and (type-annotated) values
-    tidyUp ts sub = map (\(b, (i, (val, ty))) -> (b, (i, (overTypes (substituteType sub) val, substituteType sub ty)))) ts
+    tidyUp ts sub = map (second (first (second (overTypes (substituteType sub) *** substituteType sub)))) ts
 
     isHoleError :: ErrorMessage -> Bool
     isHoleError (ErrorMessage _ HoleInferredType{}) = True
@@ -199,19 +228,12 @@
 -- | Check if a value contains a type annotation
 isTyped :: (Ident, Expr) -> Either (Ident, Expr) (Ident, (Expr, Type, Bool))
 isTyped (name, TypedValue checkType value ty) = Right (name, (value, ty, checkType))
+isTyped (name, PositionedValue pos c value) =
+  bimap (second (PositionedValue pos c))
+        (second (\(e, t, b) -> (PositionedValue pos c e, t, b)))
+        (isTyped (name, value))
 isTyped (name, value) = Left (name, value)
 
--- |
--- Map a function over type annotations appearing inside a value
---
-overTypes :: (Type -> Type) -> Expr -> Expr
-overTypes f = let (_, f', _) = everywhereOnValues id g id in f'
-  where
-  g :: Expr -> Expr
-  g (TypedValue checkTy val t) = TypedValue checkTy val (f t)
-  g (TypeClassDictionary c sco hints) = TypeClassDictionary (mapConstraintArgs (map f) c) sco hints
-  g other = other
-
 -- | Check the kind of a type, failing if it is not of kind *.
 checkTypeKind ::
   (MonadError MultipleErrors m) =>
@@ -339,7 +361,8 @@
 infer' (Hole name) = do
   ty <- freshType
   ctx <- getLocalContext
-  tell . errorMessage $ HoleInferredType name ty ctx
+  env <- getEnv
+  tell . errorMessage $ HoleInferredType name ty ctx (TSBefore env)
   return $ TypedValue True (Hole name) ty
 infer' (PositionedValue pos c val) = warnAndRethrowWithPositionTC pos $ do
   TypedValue t v ty <- infer' val
@@ -568,18 +591,14 @@
 check' (App f arg) ret = do
   f'@(TypedValue _ _ ft) <- infer f
   (retTy, app) <- checkFunctionApplication f' ft arg
-  v' <- subsumes (Just app) retTy ret
-  case v' of
-    Nothing -> internalError "check: unable to check the subsumes relation."
-    Just app' -> return $ TypedValue True app' ret
+  elaborate <- subsumes retTy ret
+  return $ TypedValue True (elaborate app) ret
 check' v@(Var var) ty = do
   checkVisibility var
   repl <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< lookupVariable $ var
   ty' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ ty
-  v' <- subsumes (Just v) repl ty'
-  case v' of
-    Nothing -> internalError "check: unable to check the subsumes relation."
-    Just v'' -> return $ TypedValue True v'' ty'
+  elaborate <- subsumes repl ty'
+  return $ TypedValue True (elaborate v) ty'
 check' (DeferredDictionary className tys) _ = do
   {-
   -- Here, we replace a placeholder for a superclass dictionary with a regular
@@ -596,12 +615,11 @@
   checkTypeKind ty1 kind
   ty1' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ ty1
   ty2' <- introduceSkolemScope <=< replaceAllTypeSynonyms <=< replaceTypeWildcards $ ty2
-  val' <- subsumes (Just val) ty1' ty2'
-  case val' of
-    Nothing -> internalError "check: unable to check the subsumes relation."
-    Just _ -> do
-      val''' <- if checkType then withScopedTypeVars moduleName args (check val ty2') else return val
-      return $ TypedValue checkType val''' ty2'
+  _ <- subsumes ty1' ty2'
+  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', ts) <- instantiateForBinders vals binders
   binders' <- checkBinders ts ret binders
@@ -638,10 +656,8 @@
     Nothing -> throwError . errorMessage . UnknownName . fmap DctorName $ c
     Just (_, _, ty1, _) -> do
       repl <- introduceSkolemScope <=< replaceAllTypeSynonyms $ ty1
-      mv <- subsumes (Just v) repl ty
-      case mv of
-        Nothing -> internalError "check: unable to check the subsumes relation."
-        Just v' -> return $ TypedValue True v' ty
+      elaborate <- subsumes repl ty
+      return $ TypedValue True (elaborate v) ty
 check' (Let ds val) ty = do
   (ds', val') <- inferLetBinding [] ds val (`check` ty)
   return $ TypedValue True (Let ds' val') ty
@@ -654,10 +670,8 @@
   return $ TypedValue t (PositionedValue pos c v) ty'
 check' val ty = do
   TypedValue _ val' ty' <- infer val
-  mt <- subsumes (Just val') ty' ty
-  case mt of
-    Nothing -> internalError "check: unable to check the subsumes relation."
-    Just v' -> return $ TypedValue True v' ty
+  elaborate <- subsumes ty' ty
+  return $ TypedValue True (elaborate val') ty
 
 -- |
 -- Check the type of a collection of named record fields
diff --git a/src/System/IO/UTF8.hs b/src/System/IO/UTF8.hs
--- a/src/System/IO/UTF8.hs
+++ b/src/System/IO/UTF8.hs
@@ -4,11 +4,21 @@
 
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.UTF8 as UTF8
+import           Data.Text (Text)
+import qualified Data.Text.Encoding as TE
 
+readUTF8FileT :: FilePath -> IO Text
+readUTF8FileT inFile =
+  fmap TE.decodeUtf8 (BS.readFile inFile)
+
+writeUTF8FileT :: FilePath -> Text -> IO ()
+writeUTF8FileT inFile text =
+  BS.writeFile inFile (TE.encodeUtf8 text)
+
 readUTF8File :: FilePath -> IO String
-readUTF8File inFile = do
+readUTF8File inFile =
   fmap UTF8.toString (BS.readFile inFile)
 
 writeUTF8File :: FilePath -> String -> IO ()
-writeUTF8File inFile text = do
+writeUTF8File inFile text =
   BS.writeFile inFile (UTF8.fromString text)
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,12 +1,3 @@
-resolver: lts-6.13
+resolver: lts-6.10
 packages:
 - '.'
-extra-deps:
-# - aeson-1.0.0.0
-- http-client-0.5.1
-- http-client-tls-0.3.0
-- pipes-http-1.0.4
-# - semigroups-0.18.2
-# flags:
-#   semigroups:
-#     bytestring-builder: false
diff --git a/tests/Language/PureScript/Ide/FilterSpec.hs b/tests/Language/PureScript/Ide/FilterSpec.hs
--- a/tests/Language/PureScript/Ide/FilterSpec.hs
+++ b/tests/Language/PureScript/Ide/FilterSpec.hs
@@ -9,7 +9,7 @@
 import           Test.Hspec
 
 value :: Text -> IdeDeclarationAnn
-value s = IdeDeclarationAnn emptyAnn (IdeValue (P.Ident (toS s)) P.REmpty)
+value s = IdeDeclarationAnn emptyAnn (IdeDeclValue (IdeValue (P.Ident (toS s)) P.REmpty))
 
 moduleA, moduleB :: Module
 moduleA = (P.moduleNameFromString "Module.A", [value "function1"])
diff --git a/tests/Language/PureScript/Ide/Imports/IntegrationSpec.hs b/tests/Language/PureScript/Ide/Imports/IntegrationSpec.hs
--- a/tests/Language/PureScript/Ide/Imports/IntegrationSpec.hs
+++ b/tests/Language/PureScript/Ide/Imports/IntegrationSpec.hs
@@ -6,12 +6,12 @@
 import           Protolude
 
 import qualified Data.Text                           as T
-import qualified Data.Text.IO                        as TIO
 import qualified Language.PureScript.Ide.Integration as Integration
 import           Test.Hspec
 
 import           System.Directory
 import           System.FilePath
+import           System.IO.UTF8                      (readUTF8FileT)
 
 setup :: IO ()
 setup = void (Integration.reset *> Integration.loadAll)
@@ -27,7 +27,7 @@
 outputFileShouldBe :: [Text] -> IO ()
 outputFileShouldBe expectation = do
   outFp <- (</> "src" </> "ImportsSpecOut.tmp") <$> Integration.projectDirectory
-  outRes <- TIO.readFile outFp
+  outRes <- readUTF8FileT outFp
   shouldBe (T.lines outRes) expectation
 
 spec :: Spec
@@ -57,10 +57,10 @@
     outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (class ATypeClass)"])
   it "adds an explicit unqualified import (dataconstructor)" $ do
     withSupportFiles (Integration.addImport "MyJust")
-    outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (MyMaybe(MyJust))"])
+    outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (MyMaybe(..))"])
   it "adds an explicit unqualified import (newtype)" $ do
     withSupportFiles (Integration.addImport "MyNewtype")
-    outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (MyNewtype(MyNewtype))"])
+    outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (MyNewtype(..))"])
   it "adds an explicit unqualified import (typeclass member function)" $ do
     withSupportFiles (Integration.addImport "typeClassFun")
     outputFileShouldBe (sourceFileSkeleton ["import ImportsSpec1 (typeClassFun)"])
diff --git a/tests/Language/PureScript/Ide/ImportsSpec.hs b/tests/Language/PureScript/Ide/ImportsSpec.hs
--- a/tests/Language/PureScript/Ide/ImportsSpec.hs
+++ b/tests/Language/PureScript/Ide/ImportsSpec.hs
@@ -68,11 +68,13 @@
   describe "import commands" $ do
     let simpleFileImports = let (_, _, i, _) = splitSimpleFile in i
         addValueImport i mn is =
-          prettyPrintImportSection (addExplicitImport' (IdeValue (P.Ident i) wildcard) mn is)
+          prettyPrintImportSection (addExplicitImport' (IdeDeclValue (IdeValue (P.Ident i) wildcard)) mn is)
         addOpImport op mn is =
-          prettyPrintImportSection (addExplicitImport' (IdeValueOperator op (P.Qualified Nothing (Left (P.Ident ""))) 2 P.Infix Nothing) mn is)
+          prettyPrintImportSection (addExplicitImport' (IdeDeclValueOperator (IdeValueOperator op (P.Qualified Nothing (Left (P.Ident ""))) 2 P.Infix Nothing)) mn is)
         addDtorImport i t mn is =
-          prettyPrintImportSection (addExplicitImport' (IdeDataConstructor (P.ProperName i) t wildcard) mn is)
+          prettyPrintImportSection (addExplicitImport' (IdeDeclDataConstructor (IdeDataConstructor (P.ProperName i) t wildcard)) mn is)
+        addTypeImport i mn is =
+          prettyPrintImportSection (addExplicitImport' (IdeDeclType (IdeType (P.ProperName i) P.Star)) mn is)
     it "adds an implicit unqualified import" $
       shouldBe
         (addImplicitImport' simpleFileImports (P.moduleNameFromString "Data.Map"))
@@ -100,20 +102,26 @@
       shouldBe
         (addOpImport (P.OpName "<~>") (P.moduleNameFromString "Data.Array") explicitImports)
         [ "import Prelude"
-        , "import Data.Array ((<~>), tail)"
+        , "import Data.Array (tail, (<~>))"
         ]
+    it "adds a type with constructors without automatically adding an open import of said constructors " $
+        shouldBe
+          (addTypeImport "Maybe" (P.moduleNameFromString "Data.Maybe") simpleFileImports)
+          [ "import Prelude"
+          , "import Data.Maybe (Maybe)"
+          ]
     it "adds the type for a given DataConstructor" $
         shouldBe
           (addDtorImport "Just" (P.ProperName "Maybe") (P.moduleNameFromString "Data.Maybe") simpleFileImports)
           [ "import Prelude"
-          , "import Data.Maybe (Maybe(Just))"
+          , "import Data.Maybe (Maybe(..))"
           ]
     it "adds a dataconstructor to an existing type import" $ do
       let Right (_, _, typeImports, _) = sliceImportSection (withImports ["import Data.Maybe (Maybe)"])
       shouldBe
         (addDtorImport "Just" (P.ProperName "Maybe") (P.moduleNameFromString "Data.Maybe") typeImports)
         [ "import Prelude"
-        , "import Data.Maybe (Maybe(Just))"
+        , "import Data.Maybe (Maybe(..))"
         ]
     it "doesn't add a dataconstructor to an existing type import with open dtors" $ do
       let Right (_, _, typeImports, _) = sliceImportSection (withImports ["import Data.Maybe (Maybe(..))"])
@@ -128,3 +136,38 @@
       [ "import Prelude"
       , "import Data.Array (tail)"
       ]
+
+  describe "explicit import sorting" $ do
+    -- given some basic import skeleton
+    let Right (_, _, baseImports, _) = sliceImportSection $ withImports ["import Control.Monad (ap)"]
+        moduleName = (P.moduleNameFromString "Control.Monad")
+        addImport imports import' = addExplicitImport' import' moduleName imports
+        valueImport ident = (IdeDeclValue (IdeValue (P.Ident ident) wildcard))
+        typeImport name = (IdeDeclType (IdeType (P.ProperName name) P.Star))
+        classImport name = (IdeDeclTypeClass (P.ProperName name))
+        dtorImport name typeName = (IdeDeclDataConstructor (IdeDataConstructor (P.ProperName name) (P.ProperName typeName) wildcard))
+        -- expect any list of provided identifiers, when imported, to come out as specified
+        expectSorted imports expected = shouldBe
+          (ordNub $ map
+            (prettyPrintImportSection . foldl addImport baseImports)
+            (permutations imports))
+          [expected]
+    it "sorts class" $
+      expectSorted (map classImport ["Applicative", "Bind"])
+        ["import Prelude", "import Control.Monad (class Applicative, class Bind, ap)"]
+    it "sorts value" $
+      expectSorted (map valueImport ["unless", "where"])
+        ["import Prelude", "import Control.Monad (ap, unless, where)"]
+    it "sorts type, value" $
+      expectSorted
+        ((map valueImport ["unless", "where"]) ++ (map typeImport ["Foo", "Bar"]))
+        ["import Prelude", "import Control.Monad (Bar, Foo, ap, unless, where)"]
+    it "sorts class, type, value" $
+      expectSorted
+        ((map valueImport ["unless", "where"]) ++ (map typeImport ["Foo", "Bar"]) ++ (map classImport ["Applicative", "Bind"]))
+        ["import Prelude", "import Control.Monad (class Applicative, class Bind, Bar, Foo, ap, unless, where)"]
+    it "sorts types with constructors, using open imports for the constructors" $
+      expectSorted
+        -- the imported names don't actually have to exist!
+        (map (uncurry dtorImport) [("Just", "Maybe"), ("Nothing", "Maybe"), ("SomeOtherConstructor", "SomeDataType")])
+        ["import Prelude", "import Control.Monad (Maybe(..), SomeDataType(..), ap)"]
diff --git a/tests/Language/PureScript/Ide/MatcherSpec.hs b/tests/Language/PureScript/Ide/MatcherSpec.hs
--- a/tests/Language/PureScript/Ide/MatcherSpec.hs
+++ b/tests/Language/PureScript/Ide/MatcherSpec.hs
@@ -13,7 +13,7 @@
 import           Test.Hspec
 
 value :: Text -> IdeDeclarationAnn
-value s = withEmptyAnn (IdeValue (P.Ident (toS s)) P.REmpty)
+value s = withEmptyAnn (IdeDeclValue (IdeValue (P.Ident (toS s)) P.REmpty))
 
 firstResult, secondResult, fiult :: Match IdeDeclarationAnn
 firstResult = Match (P.moduleNameFromString "Match", value "firstResult")
diff --git a/tests/Language/PureScript/Ide/ReexportsSpec.hs b/tests/Language/PureScript/Ide/ReexportsSpec.hs
--- a/tests/Language/PureScript/Ide/ReexportsSpec.hs
+++ b/tests/Language/PureScript/Ide/ReexportsSpec.hs
@@ -18,11 +18,11 @@
 d = IdeDeclarationAnn emptyAnn
 
 valueA, typeA, classA, dtorA1, dtorA2 :: IdeDeclarationAnn
-valueA = d (IdeValue (P.Ident "valueA") P.REmpty)
-typeA = d (IdeType (P.ProperName "TypeA") P.Star)
-classA = d (IdeTypeClass (P.ProperName "ClassA"))
-dtorA1 = d (IdeDataConstructor (P.ProperName "DtorA1") (P.ProperName "TypeA") P.REmpty)
-dtorA2 = d (IdeDataConstructor (P.ProperName "DtorA2") (P.ProperName "TypeA") P.REmpty)
+valueA = d (IdeDeclValue (IdeValue (P.Ident "valueA") P.REmpty))
+typeA = d (IdeDeclType (IdeType(P.ProperName "TypeA") P.Star))
+classA = d (IdeDeclTypeClass (P.ProperName "ClassA"))
+dtorA1 = d (IdeDeclDataConstructor (IdeDataConstructor (P.ProperName "DtorA1") (P.ProperName "TypeA") P.REmpty))
+dtorA2 = d (IdeDeclDataConstructor (IdeDataConstructor (P.ProperName "DtorA2") (P.ProperName "TypeA") P.REmpty))
 
 env :: Map P.ModuleName [IdeDeclarationAnn]
 env = Map.fromList
diff --git a/tests/Language/PureScript/Ide/StateSpec.hs b/tests/Language/PureScript/Ide/StateSpec.hs
--- a/tests/Language/PureScript/Ide/StateSpec.hs
+++ b/tests/Language/PureScript/Ide/StateSpec.hs
@@ -11,20 +11,20 @@
 
 valueOperator :: Maybe P.Type -> IdeDeclarationAnn
 valueOperator =
-  d . IdeValueOperator (P.OpName "<$>") (P.Qualified (Just (mn "Test")) (Left (P.Ident "function"))) 2 P.Infix
+  d . IdeDeclValueOperator . IdeValueOperator (P.OpName "<$>") (P.Qualified (Just (mn "Test")) (Left (P.Ident "function"))) 2 P.Infix
 
 ctorOperator :: Maybe P.Type -> IdeDeclarationAnn
 ctorOperator =
-  d . IdeValueOperator (P.OpName ":") (P.Qualified (Just (mn "Test")) (Right (P.ProperName "Cons"))) 2 P.Infix
+  d . IdeDeclValueOperator . IdeValueOperator (P.OpName ":") (P.Qualified (Just (mn "Test")) (Right (P.ProperName "Cons"))) 2 P.Infix
 
 typeOperator :: Maybe P.Kind -> IdeDeclarationAnn
 typeOperator =
-  d . IdeTypeOperator (P.OpName ":") (P.Qualified (Just (mn "Test")) (P.ProperName "List")) 2 P.Infix
+  d . IdeDeclTypeOperator . IdeTypeOperator (P.OpName ":") (P.Qualified (Just (mn "Test")) (P.ProperName "List")) 2 P.Infix
 
 testModule :: Module
-testModule = (mn "Test", [ d (IdeValue (P.Ident "function") P.REmpty)
-                         , d (IdeDataConstructor (P.ProperName "Cons") (P.ProperName "List") (P.REmpty))
-                         , d (IdeType (P.ProperName "List") P.Star)
+testModule = (mn "Test", [ d (IdeDeclValue (IdeValue (P.Ident "function") P.REmpty))
+                         , d (IdeDeclDataConstructor (IdeDataConstructor (P.ProperName "Cons") (P.ProperName "List") (P.REmpty)))
+                         , d (IdeDeclType (IdeType (P.ProperName "List") P.Star))
                          , valueOperator Nothing
                          , ctorOperator Nothing
                          , typeOperator Nothing
diff --git a/tests/TestCompiler.hs b/tests/TestCompiler.hs
--- a/tests/TestCompiler.hs
+++ b/tests/TestCompiler.hs
@@ -31,6 +31,7 @@
 import Data.Maybe (mapMaybe)
 import Data.Time.Clock (UTCTime())
 import Data.Tuple (swap)
+import qualified Data.Text as T
 
 import qualified Data.Map as M
 
@@ -71,7 +72,7 @@
     supportPurs <- supportFiles "purs"
     supportPursFiles <- readInput supportPurs
     supportExterns <- runExceptT $ do
-      modules <- ExceptT . return $ P.parseModulesFromFiles id supportPursFiles
+      modules <- ExceptT . return $ P.parseModulesFromFiles id (map (fmap T.pack) supportPursFiles)
       foreigns <- inferForeignModules modules
       externs <- ExceptT . fmap fst . runTest $ P.make (makeActions foreigns) (map snd modules)
       return (zip (map snd modules) externs)
@@ -194,7 +195,7 @@
   -> IO (Either P.MultipleErrors [P.ExternsFile], P.MultipleErrors)
 compile supportExterns inputFiles check = silence $ runTest $ do
   fs <- liftIO $ readInput inputFiles
-  ms <- P.parseModulesFromFiles id fs
+  ms <- P.parseModulesFromFiles id (map (fmap T.pack) fs)
   foreigns <- inferForeignModules ms
   liftIO (check (map snd ms))
   let actions = makeActions foreigns
diff --git a/tests/TestUtils.hs b/tests/TestUtils.hs
--- a/tests/TestUtils.hs
+++ b/tests/TestUtils.hs
@@ -53,16 +53,24 @@
 --
 supportModules :: [String]
 supportModules =
-  [ "Control.Applicative"
+  [ "Control.Alt"
+  , "Control.Alternative"
+  , "Control.Applicative"
   , "Control.Apply"
   , "Control.Bind"
   , "Control.Category"
+  , "Control.Comonad"
+  , "Control.Extend"
+  , "Control.Lazy"
+  , "Control.Monad"
+  , "Control.Monad.Eff"
   , "Control.Monad.Eff.Class"
   , "Control.Monad.Eff.Console"
   , "Control.Monad.Eff.Unsafe"
-  , "Control.Monad.Eff"
   , "Control.Monad.ST"
-  , "Control.Monad"
+  , "Control.MonadPlus"
+  , "Control.MonadZero"
+  , "Control.Plus"
   , "Control.Semigroupoid"
   , "Data.Boolean"
   , "Data.BooleanAlgebra"
@@ -71,14 +79,27 @@
   , "Data.Eq"
   , "Data.EuclideanRing"
   , "Data.Field"
-  , "Data.Function.Uncurried"
   , "Data.Function"
+  , "Data.Function.Uncurried"
   , "Data.Functor"
+  , "Data.Functor.Invariant"
+  , "Data.Generic.Rep"
+  , "Data.Generic.Rep.Monoid"
+  , "Data.Generic.Rep.Eq"
+  , "Data.Generic.Rep.Ord"
+  , "Data.Generic.Rep.Semigroup"
   , "Data.HeytingAlgebra"
+  , "Data.Monoid"
+  , "Data.Monoid.Additive"
+  , "Data.Monoid.Conj"
+  , "Data.Monoid.Disj"
+  , "Data.Monoid.Dual"
+  , "Data.Monoid.Endo"
+  , "Data.Monoid.Multiplicative"
   , "Data.NaturalTransformation"
   , "Data.Newtype"
-  , "Data.Ord.Unsafe"
   , "Data.Ord"
+  , "Data.Ord.Unsafe"
   , "Data.Ordering"
   , "Data.Ring"
   , "Data.Semigroup"
@@ -90,6 +111,7 @@
   , "Partial.Unsafe"
   , "Prelude"
   , "Test.Assert"
+  , "Test.Main"
   ]
 
 pushd :: forall a. FilePath -> IO a -> IO a
diff --git a/tests/support/bower.json b/tests/support/bower.json
--- a/tests/support/bower.json
+++ b/tests/support/bower.json
@@ -8,6 +8,7 @@
     "purescript-prelude": "1.1.0",
     "purescript-st": "1.0.0-rc.1",
     "purescript-partial": "1.1.2",
-    "purescript-newtype": "0.1.0"
+    "purescript-newtype": "0.1.0",
+    "purescript-generics-rep": "2.0.0"
   }
 }
