diff --git a/purescript.cabal b/purescript.cabal
--- a/purescript.cabal
+++ b/purescript.cabal
@@ -1,5 +1,5 @@
 name: purescript
-version: 0.2.2
+version: 0.2.3
 cabal-version: >=1.8
 build-type: Simple
 license: MIT
@@ -17,10 +17,11 @@
     build-depends: base >=4 && <5, cmdtheline -any, containers -any,
                    directory -any, filepath -any, mtl -any, parsec -any, syb -any,
                    transformers -any, utf8-string -any
-    exposed-modules: Language.PureScript.Scope Data.Generics.Extras
-                     Language.PureScript Language.PureScript.CodeGen
-                     Language.PureScript.CodeGen.Externs Language.PureScript.CodeGen.JS
-                     Language.PureScript.CodeGen.JS.AST
+    exposed-modules: Language.PureScript.TypeDeclarations
+                     Language.PureScript.BindingGroups Language.PureScript.Scope
+                     Data.Generics.Extras Language.PureScript
+                     Language.PureScript.CodeGen Language.PureScript.CodeGen.Externs
+                     Language.PureScript.CodeGen.JS Language.PureScript.CodeGen.JS.AST
                      Language.PureScript.CodeGen.Monad Language.PureScript.Declarations
                      Language.PureScript.Kinds Language.PureScript.Names
                      Language.PureScript.Operators Language.PureScript.Optimize
diff --git a/src/Language/PureScript.hs b/src/Language/PureScript.hs
--- a/src/Language/PureScript.hs
+++ b/src/Language/PureScript.hs
@@ -26,15 +26,17 @@
 import Language.PureScript.Optimize as P
 import Language.PureScript.Operators as P
 import Language.PureScript.CaseDeclarations as P
+import Language.PureScript.TypeDeclarations as P
+import Language.PureScript.BindingGroups as P
 
 import Data.List (intercalate)
-import Data.Maybe (mapMaybe)
+import Control.Monad (forM_, (>=>))
 
-compile :: [Declaration] -> Either String (String, String, Environment)
-compile decls = do
-  bracketted <- rebracket decls
-  desugared <- desugarCases bracketted
-  (_, env) <- runCheck (typeCheckAll desugared)
-  let js = prettyPrintJS . map optimize . concat . mapMaybe (\decl -> declToJs Nothing global decl env) $ desugared
-  let exts = intercalate "\n" . mapMaybe (externToPs 0 global env) $ desugared
+compile :: [Module] -> Either String (String, String, Environment)
+compile ms = do
+  bracketted <- rebracket ms
+  desugared <- desugarCasesModule >=> desugarTypeDeclarationsModule >=> (return . createBindingGroupsModule) $ bracketted
+  (_, env) <- runCheck $ forM_ desugared $ \(Module moduleName decls) -> typeCheckAll (ModuleName moduleName) decls
+  let js = prettyPrintJS . map optimize . concatMap (flip moduleToJs env) $ desugared
+  let exts = intercalate "\n" . map (flip moduleToPs env) $ desugared
   return (js, exts, env)
diff --git a/src/Language/PureScript/BindingGroups.hs b/src/Language/PureScript/BindingGroups.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/PureScript/BindingGroups.hs
@@ -0,0 +1,98 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  Language.PureScript.BindingGroups
+-- Copyright   :  (c) Phil Freeman 2013
+-- License     :  MIT
+--
+-- Maintainer  :  Phil Freeman <paf31@cantab.net>
+-- Stability   :  experimental
+-- Portability :
+--
+-- |
+--
+-----------------------------------------------------------------------------
+
+module Language.PureScript.BindingGroups (
+    createBindingGroups,
+    createBindingGroupsModule
+) where
+
+import Data.Data
+import Data.Graph
+import Data.Generics
+import Data.List (nub, intersect)
+
+import Language.PureScript.Declarations
+import Language.PureScript.Names
+import Language.PureScript.Values
+import Language.PureScript.Types
+
+createBindingGroupsModule :: [Module] -> [Module]
+createBindingGroupsModule = map $ \(Module name ds) -> Module name (createBindingGroups ds)
+
+createBindingGroups :: [Declaration] -> [Declaration]
+createBindingGroups ds =
+  let
+    values = filter isValueDecl ds
+    dataDecls = filter isDataDecl ds
+    nonValues = filter (\d -> not (isValueDecl d) && not (isDataDecl d)) ds
+    allProperNames = map getProperName dataDecls
+    dataVerts = map (\d -> (d, getProperName d, usedProperNames d `intersect` allProperNames)) dataDecls
+    dataBindingGroupDecls = map toDataBindingGroup $ stronglyConnComp dataVerts
+    allIdents = map getIdent values
+    valueVerts = map (\d -> (d, getIdent d, usedIdents d `intersect` allIdents)) values
+    bindingGroupDecls = map toBindingGroup $ stronglyConnComp valueVerts
+  in
+    dataBindingGroupDecls ++ nonValues ++ bindingGroupDecls
+
+usedIdents :: (Data d) => d -> [Ident]
+usedIdents = nub . everything (++) (mkQ [] namesV `extQ` namesS)
+  where
+  namesV :: Value -> [Ident]
+  namesV (Var (Qualified Nothing name)) = [name]
+  namesV _ = []
+  namesS :: Statement -> [Ident]
+  namesS (VariableIntroduction name _) = [name]
+  namesS _ = []
+
+usedProperNames :: (Data d) => d -> [ProperName]
+usedProperNames = nub . everything (++) (mkQ [] names)
+  where
+  names :: Type -> [ProperName]
+  names (TypeConstructor (Qualified Nothing name)) = [name]
+  names _ = []
+
+isValueDecl :: Declaration -> Bool
+isValueDecl (ValueDeclaration _ _ _ _) = True
+isValueDecl _ = False
+
+isDataDecl :: Declaration -> Bool
+isDataDecl (DataDeclaration _ _ _) = True
+isDataDecl _ = False
+
+getIdent :: Declaration -> Ident
+getIdent (ValueDeclaration ident _ _ _) = ident
+getIdent _ = error "Expected ValueDeclaration"
+
+getProperName :: Declaration -> ProperName
+getProperName (DataDeclaration pn _ _) = pn
+getProperName _ = error "Expected DataDeclaration"
+
+toBindingGroup :: SCC Declaration -> Declaration
+toBindingGroup (AcyclicSCC d) = d
+toBindingGroup (CyclicSCC [d]) = d
+toBindingGroup (CyclicSCC ds') = BindingGroupDeclaration $ map fromValueDecl ds'
+
+toDataBindingGroup :: SCC Declaration -> Declaration
+toDataBindingGroup (AcyclicSCC d) = d
+toDataBindingGroup (CyclicSCC [d]) = d
+toDataBindingGroup (CyclicSCC ds') = DataBindingGroupDeclaration $ map fromDataDecl ds'
+
+fromValueDecl :: Declaration -> (Ident, Value)
+fromValueDecl (ValueDeclaration ident [] Nothing val) = (ident, val)
+fromValueDecl (ValueDeclaration _ _ _ _) = error "Binders should have been desugared"
+fromValueDecl _ = error "Expected ValueDeclaration"
+
+fromDataDecl :: Declaration -> (ProperName, [String], [(ProperName, Maybe PolyType)])
+fromDataDecl (DataDeclaration pn args ctors) = (pn, args, ctors)
+fromDataDecl _ = error "Expected DataDeclaration"
diff --git a/src/Language/PureScript/CaseDeclarations.hs b/src/Language/PureScript/CaseDeclarations.hs
--- a/src/Language/PureScript/CaseDeclarations.hs
+++ b/src/Language/PureScript/CaseDeclarations.hs
@@ -13,11 +13,13 @@
 -----------------------------------------------------------------------------
 
 module Language.PureScript.CaseDeclarations (
-    desugarCases
+    desugarCases,
+    desugarCasesModule
 ) where
 
 import Data.List (groupBy)
-import Control.Monad (join, unless)
+import Control.Applicative ((<$>))
+import Control.Monad (forM, join, unless)
 import Control.Monad.Error.Class
 
 import Language.PureScript.Names
@@ -25,6 +27,9 @@
 import Language.PureScript.Declarations
 import Language.PureScript.Scope
 
+desugarCasesModule :: [Module] -> Either String [Module]
+desugarCasesModule ms = forM ms $ \(Module name ds) -> Module name <$> desugarCases ds
+
 desugarCases :: [Declaration] -> Either String [Declaration]
 desugarCases = fmap join . mapM toDecls . groupBy inSameGroup
 
@@ -39,9 +44,6 @@
   unless (all ((== map length bs) . map length . fst) tuples) $
       throwError $ "Argument list lengths differ in declaration " ++ show ident
   return [makeCaseDeclaration ident tuples]
-toDecls [ModuleDeclaration name decls] = do
-  desugared <- desugarCases decls
-  return [ModuleDeclaration name desugared]
 toDecls ds = return ds
 
 toTuple :: Declaration -> ([[Binder]], (Maybe Guard, Value))
@@ -53,7 +55,7 @@
   let
     argPattern = map length . fst . head $ alternatives
     args = take (sum argPattern) $ unusedNames (ident, alternatives)
-    vars = map (\arg -> Var (Qualified global arg)) args
+    vars = map (\arg -> Var (Qualified Nothing arg)) args
     binders = [ (join bs, g, val) | (bs, (g, val)) <- alternatives ]
     value = foldr (\args' ret -> Abs args' ret) (Case vars binders) (rearrange argPattern args)
   in
diff --git a/src/Language/PureScript/CodeGen/Externs.hs b/src/Language/PureScript/CodeGen/Externs.hs
--- a/src/Language/PureScript/CodeGen/Externs.hs
+++ b/src/Language/PureScript/CodeGen/Externs.hs
@@ -13,29 +13,37 @@
 -----------------------------------------------------------------------------
 
 module Language.PureScript.CodeGen.Externs (
-    externToPs
+    moduleToPs
 ) where
 
-import Data.Maybe (mapMaybe)
+import Data.Maybe (maybeToList, mapMaybe)
 import qualified Data.Map as M
 import Language.PureScript.Declarations
 import Language.PureScript.TypeChecker.Monad
 import Language.PureScript.Pretty
 import Language.PureScript.Names
+import Data.List (intercalate)
 
-externToPs :: Int -> ModulePath -> Environment -> Declaration -> Maybe String
-externToPs indent path env (ValueDeclaration name _ _ _) = do
+moduleToPs :: Module -> Environment -> String
+moduleToPs (Module pname@(ProperName moduleName) decls) env =
+  "module " ++ moduleName ++ " where\n" ++
+  (intercalate "\n" . map ("  " ++) . concatMap (declToPs (ModuleName pname) env) $ decls)
+
+declToPs :: ModuleName -> Environment -> Declaration -> [String]
+declToPs path env (ValueDeclaration name _ _ _) = maybeToList $ do
   (ty, _) <- M.lookup (path, name) $ names env
-  return $ replicate indent ' ' ++ "foreign import " ++ show name ++ " :: " ++ prettyPrintType ty
-externToPs indent path env (DataDeclaration name _ _) = do
+  return $ "foreign import " ++ show name ++ " :: " ++ prettyPrintType ty
+declToPs path env (BindingGroupDeclaration vals) = do
+  flip mapMaybe vals $ \(name, _) -> do
+    (ty, _) <- M.lookup (path, name) $ names env
+    return $ "foreign import " ++ show name ++ " :: " ++ prettyPrintType ty
+declToPs path env (DataDeclaration name _ _) = maybeToList $ do
   (kind, _) <- M.lookup (path, name) $ types env
-  return $ replicate indent ' ' ++ "foreign import data " ++ show name ++ " :: " ++ prettyPrintKind kind
-externToPs indent _ _ (ExternMemberDeclaration member name ty) =
-  return $ replicate indent ' ' ++ "foreign import member " ++ show member ++ " " ++ show name ++ " :: " ++ prettyPrintType ty
-externToPs indent _ _ (ExternDataDeclaration name kind) =
-  return $ replicate indent ' ' ++ "foreign import data " ++ show name ++ " :: " ++ prettyPrintKind kind
-externToPs indent _ _ (TypeSynonymDeclaration name args ty) =
-  return $ replicate indent ' ' ++ "type " ++ show name ++ " " ++ unwords args ++ " = " ++ prettyPrintType ty
-externToPs indent path env (ModuleDeclaration name decls) =
-  return $ replicate indent ' ' ++ "module " ++ show name ++ " where\n" ++ unlines (mapMaybe (externToPs (indent + 2) (subModule path name) env) decls)
-externToPs _ _ _ _ = Nothing
+  return $ "foreign import data " ++ show name ++ " :: " ++ prettyPrintKind kind
+declToPs _ _ (ExternMemberDeclaration member name ty) =
+  return $ "foreign import member " ++ show member ++ " " ++ show name ++ " :: " ++ prettyPrintType ty
+declToPs _ _ (ExternDataDeclaration name kind) =
+  return $ "foreign import data " ++ show name ++ " :: " ++ prettyPrintKind kind
+declToPs _ _ (TypeSynonymDeclaration name args ty) =
+  return $ "type " ++ show name ++ " " ++ unwords args ++ " = " ++ prettyPrintType ty
+declToPs _ _ _ = []
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
@@ -14,7 +14,8 @@
 
 module Language.PureScript.CodeGen.JS (
     module AST,
-    declToJs
+    declToJs,
+    moduleToJs
 ) where
 
 import Data.Maybe (mapMaybe)
@@ -32,40 +33,47 @@
 import Language.PureScript.CodeGen.JS.AST as AST
 import Language.PureScript.TypeChecker.Monad (NameKind(..))
 
-declToJs :: Maybe Ident -> ModulePath -> Declaration -> Environment -> Maybe [JS]
-declToJs curMod mp (ValueDeclaration ident _ _ (Abs args ret)) e =
-  Just $ JSFunction (Just ident) args (JSBlock [JSReturn (valueToJs mp e ret)]) :
-         maybe [] (return . setProperty (identToJs ident) (JSVar ident)) curMod
-declToJs curMod mp (ValueDeclaration ident _ _ val) e =
-  Just $ JSVariableIntroduction ident (Just (valueToJs mp e val)) :
-         maybe [] (return . setProperty (identToJs ident) (JSVar ident)) curMod
-declToJs curMod _ (ExternMemberDeclaration member ident _) _ =
-  Just $ JSFunction (Just ident) [Ident "value"] (JSBlock [JSReturn (JSAccessor member (JSVar (Ident "value")))]) :
-         maybe [] (return . setProperty (show ident) (JSVar ident)) curMod
-declToJs curMod mp (DataDeclaration _ _ ctors) _ =
+moduleToJs :: Module -> Environment -> [JS]
+moduleToJs (Module pname@(ProperName name) decls) env =
+  [ JSVariableIntroduction (Ident name) Nothing
+  , JSApp (JSFunction Nothing [Ident name]
+                      (JSBlock (concat $ mapMaybe (\decl -> declToJs (ModuleName pname) decl env) decls)))
+          [JSAssignment (JSAssignVariable (Ident name))
+                         (JSBinary Or (JSVar (Ident name)) (JSObjectLiteral []))]
+  ]
+
+declToJs :: ModuleName -> Declaration -> Environment -> Maybe [JS]
+declToJs mp (ValueDeclaration ident _ _ (Abs args ret)) e =
+  Just [ JSFunction (Just ident) args (JSBlock [JSReturn (valueToJs mp e ret)]),
+         setProperty (identToJs ident) (JSVar ident) mp ]
+declToJs mp (ValueDeclaration ident _ _ val) e =
+  Just [ JSVariableIntroduction ident (Just (valueToJs mp e val)),
+         setProperty (identToJs ident) (JSVar ident) mp ]
+declToJs mp (BindingGroupDeclaration vals) e =
+  Just $ concatMap (\(ident, val) ->
+           [ JSVariableIntroduction ident (Just (valueToJs mp e val)),
+             setProperty (identToJs ident) (JSVar ident) mp ]
+         ) vals
+declToJs mp (ExternMemberDeclaration member ident _) _ =
+  Just [ JSFunction (Just ident) [Ident "value"] (JSBlock [JSReturn (JSAccessor member (JSVar (Ident "value")))]),
+         setProperty (show ident) (JSVar ident) mp ]
+declToJs mp (DataDeclaration _ _ ctors) _ =
   Just $ flip concatMap ctors $ \(pn@(ProperName ctor), maybeTy) ->
     let
       ctorJs =
         case maybeTy of
-          Nothing -> JSVariableIntroduction (Ident ctor) (Just (JSObjectLiteral [ ("ctor", JSStringLiteral (show (Qualified mp pn))) ]))
+          Nothing -> JSVariableIntroduction (Ident ctor) (Just (JSObjectLiteral [ ("ctor", JSStringLiteral (show (Qualified (Just mp) pn))) ]))
           Just _ -> JSFunction (Just (Ident ctor)) [Ident "value"]
                       (JSBlock [JSReturn
-                        (JSObjectLiteral [ ("ctor", JSStringLiteral (show (Qualified mp pn)))
+                        (JSObjectLiteral [ ("ctor", JSStringLiteral (show (Qualified (Just mp) pn)))
                                          , ("value", JSVar (Ident "value")) ])])
-    in ctorJs : maybe [] (return . setProperty ctor (JSVar (Ident ctor))) curMod
-declToJs curMod mp (ModuleDeclaration pn@(ProperName name) decls) env =
-  Just $ [ JSVariableIntroduction (Ident name) Nothing
-         , JSApp (JSFunction Nothing [Ident name]
-                             (JSBlock (concat $ mapMaybe (\decl -> declToJs (Just (Ident name)) (subModule mp pn) decl env) decls)))
-                 [JSAssignment (JSAssignVariable (Ident name))
-                               (JSBinary Or (JSVar (Ident name)) (JSObjectLiteral []))]] ++
-         maybe [] (return . setProperty name (JSVar (Ident name))) curMod
-declToJs _ _ _ _ = Nothing
+    in [ ctorJs, setProperty ctor (JSVar (Ident ctor)) mp ]
+declToJs _ _ _ = Nothing
 
-setProperty :: String -> JS -> Ident -> JS
-setProperty prop val curMod = JSAssignment (JSAssignProperty prop (JSAssignVariable curMod)) val
+setProperty :: String -> JS -> ModuleName -> JS
+setProperty prop val (ModuleName (ProperName moduleName)) = JSAssignment (JSAssignProperty prop (JSAssignVariable (Ident moduleName))) val
 
-valueToJs :: ModulePath -> Environment -> Value -> JS
+valueToJs :: ModuleName -> Environment -> Value -> JS
 valueToJs _ _ (NumericLiteral n) = JSNumericLiteral n
 valueToJs _ _ (StringLiteral s) = JSStringLiteral s
 valueToJs _ _ (BooleanLiteral b) = JSBooleanLiteral b
@@ -82,20 +90,27 @@
 valueToJs m e (Abs args val) = JSFunction Nothing args (JSBlock [JSReturn (valueToJs m e val)])
 valueToJs m e (Unary op val) = JSUnary op (valueToJs m e val)
 valueToJs m e (Binary op v1 v2) = JSBinary op (valueToJs m e v1) (valueToJs m e v2)
-valueToJs m e (Var ident) = case M.lookup (qualify m ident) (names e) of
-  Just (_, Alias aliasModule aliasIdent) -> qualifiedToJS identToJs (Qualified aliasModule aliasIdent)
-  _ -> qualifiedToJS identToJs ident
+valueToJs m e (Var ident) = varToJs m e ident
 valueToJs m e (TypedValue val _) = valueToJs m e val
 valueToJs _ _ _ = error "Invalid argument to valueToJs"
 
+varToJs :: ModuleName -> Environment -> Qualified Ident -> JS
+varToJs m e qual@(Qualified _ ident) = case M.lookup (qualify m qual) (names e) of
+  Just (_, ty) | isExtern ty -> JSVar ident
+  Just (_, Alias aliasModule aliasIdent) -> qualifiedToJS identToJs (Qualified (Just aliasModule) aliasIdent)
+  _ -> qualifiedToJS identToJs qual
+  where
+  isExtern Extern = True
+  isExtern (Alias m' ident') = case M.lookup (m', ident') (names e) of
+    Just (_, ty') -> isExtern ty'
+    Nothing -> error "Undefined alias in varToJs"
+  isExtern _ = False
+
 qualifiedToJS :: (a -> String) -> Qualified a -> JS
-qualifiedToJS f (Qualified (ModulePath parts) a) =
-  delimited (f a : reverse (map show parts))
- where delimited [part]       = JSVar (Ident (part))
-       delimited (part:parts') = JSAccessor part (delimited parts')
-       delimited _            = error "Invalid argument to delimited"
+qualifiedToJS f (Qualified (Just (ModuleName (ProperName m))) a) = JSAccessor (f a) (JSVar (Ident m))
+qualifiedToJS f (Qualified Nothing a) = JSVar (Ident (f a))
 
-bindersToJs :: ModulePath -> Environment -> [([Binder], Maybe Guard, Value)] -> [JS] -> Gen JS
+bindersToJs :: ModuleName -> Environment -> [([Binder], Maybe Guard, Value)] -> [JS] -> Gen JS
 bindersToJs m e binders vals = do
   setNextName $ firstUnusedName (binders, vals)
   valNames <- replicateM (length vals) fresh
@@ -111,7 +126,7 @@
       binderToJs m e v done'' b
     go _ _ _ _ = error "Invalid arguments to bindersToJs"
 
-binderToJs :: ModulePath -> Environment -> String -> [JS] -> Binder -> Gen [JS]
+binderToJs :: ModuleName -> Environment -> String -> [JS] -> Binder -> Gen [JS]
 binderToJs _ _ _ done NullBinder = return done
 binderToJs _ _ varName done (StringBinder str) =
   return [JSIfElse (JSBinary EqualTo (JSVar (Ident varName)) (JSStringLiteral str)) (JSBlock done) Nothing]
@@ -124,11 +139,11 @@
 binderToJs _ _ varName done (VarBinder ident) =
   return (JSVariableIntroduction ident (Just (JSVar (Ident varName))) : done)
 binderToJs m _ varName done (NullaryBinder ctor) =
-  return [JSIfElse (JSBinary EqualTo (JSAccessor "ctor" (JSVar (Ident varName))) (JSStringLiteral (show (uncurry Qualified $ qualify m ctor)))) (JSBlock done) Nothing]
+  return [JSIfElse (JSBinary EqualTo (JSAccessor "ctor" (JSVar (Ident varName))) (JSStringLiteral (show ((\(mp, nm) -> Qualified (Just mp) nm) $ qualify m ctor)))) (JSBlock done) Nothing]
 binderToJs m e varName done (UnaryBinder ctor b) = do
   value <- fresh
   js <- binderToJs m e value done b
-  return [JSIfElse (JSBinary EqualTo (JSAccessor "ctor" (JSVar (Ident varName))) (JSStringLiteral (show (uncurry Qualified $ qualify m ctor)))) (JSBlock (JSVariableIntroduction (Ident value) (Just (JSAccessor "value" (JSVar (Ident varName)))) : js)) Nothing]
+  return [JSIfElse (JSBinary EqualTo (JSAccessor "ctor" (JSVar (Ident varName))) (JSStringLiteral (show ((\(mp, nm) -> Qualified (Just mp) nm) $ qualify m ctor)))) (JSBlock (JSVariableIntroduction (Ident value) (Just (JSAccessor "value" (JSVar (Ident varName)))) : js)) Nothing]
 binderToJs m e varName done (ObjectBinder bs) = go done bs
   where
   go :: [JS] -> [(String, Binder)] -> Gen [JS]
@@ -163,7 +178,7 @@
   js <- binderToJs m e varName done binder
   return (JSVariableIntroduction ident (Just (JSVar (Ident varName))) : js)
 
-statementToJs :: ModulePath -> Environment -> Statement -> JS
+statementToJs :: ModuleName -> Environment -> Statement -> JS
 statementToJs m e (VariableIntroduction ident value) = JSVariableIntroduction ident (Just (valueToJs m e value))
 statementToJs m e (Assignment target value) = JSAssignment (JSAssignVariable target) (valueToJs m e value)
 statementToJs m e (While cond sts) = JSWhile (valueToJs m e cond) (JSBlock (map (statementToJs m e) sts))
diff --git a/src/Language/PureScript/Declarations.hs b/src/Language/PureScript/Declarations.hs
--- a/src/Language/PureScript/Declarations.hs
+++ b/src/Language/PureScript/Declarations.hs
@@ -29,15 +29,18 @@
 
 data Fixity = Fixity Associativity Precedence deriving (Show, D.Data, D.Typeable)
 
+data Module = Module ProperName [Declaration] deriving (Show, D.Data, D.Typeable)
+
 data Declaration
   = DataDeclaration ProperName [String] [(ProperName, Maybe PolyType)]
+  | DataBindingGroupDeclaration [(ProperName, [String], [(ProperName, Maybe PolyType)])]
   | TypeSynonymDeclaration ProperName [String] PolyType
   | TypeDeclaration Ident PolyType
   | ValueDeclaration Ident [[Binder]] (Maybe Guard) Value
+  | BindingGroupDeclaration [(Ident, Value)]
   | ExternDeclaration Ident PolyType
   | ExternMemberDeclaration String Ident PolyType
   | ExternDataDeclaration ProperName Kind
   | FixityDeclaration Fixity String
-  | ModuleDeclaration ProperName [Declaration]
-  | ImportDeclaration ModulePath (Maybe [Ident])
+  | ImportDeclaration ModuleName (Maybe [Either Ident ProperName])
   deriving (Show, D.Data, D.Typeable)
diff --git a/src/Language/PureScript/Names.hs b/src/Language/PureScript/Names.hs
--- a/src/Language/PureScript/Names.hs
+++ b/src/Language/PureScript/Names.hs
@@ -17,7 +17,6 @@
 module Language.PureScript.Names where
 
 import Data.Data
-import Data.List (inits, intercalate)
 
 data Ident = Ident String | Op String deriving (Eq, Ord, Data, Typeable)
 
@@ -30,26 +29,17 @@
 instance Show ProperName where
   show = runProperName
 
-data ModulePath = ModulePath [ProperName] deriving (Eq, Ord, Data, Typeable)
-
-instance Show ModulePath where
-  show (ModulePath segments) = intercalate "." $ map show segments
-
-subModule :: ModulePath -> ProperName -> ModulePath
-subModule (ModulePath mp) name = ModulePath (mp ++ [name])
+data ModuleName = ModuleName ProperName deriving (Eq, Ord, Data, Typeable)
 
-global :: ModulePath
-global = ModulePath []
+instance Show ModuleName where
+  show (ModuleName name) = show name
 
-data Qualified a = Qualified ModulePath a deriving (Eq, Ord, Data, Typeable)
+data Qualified a = Qualified (Maybe ModuleName) a deriving (Eq, Ord, Data, Typeable)
 
 instance (Show a) => Show (Qualified a) where
-  show (Qualified (ModulePath names) a) = intercalate "." (map show names ++ [show a])
-
-qualify :: ModulePath -> Qualified a -> (ModulePath, a)
-qualify mp (Qualified (ModulePath []) a) = (mp, a)
-qualify _ (Qualified mp a) = (mp, a)
+  show (Qualified Nothing a) = show a
+  show (Qualified (Just (ModuleName name)) a) = show name ++ "." ++ show a
 
-nameResolution :: ModulePath -> Qualified a -> [(ModulePath, a)]
-nameResolution (ModulePath mp) (Qualified (ModulePath []) a) = [ (ModulePath mp', a) | mp' <- reverse $ inits mp ]
-nameResolution _ (Qualified mp a) = [(mp, a)]
+qualify :: ModuleName -> Qualified a -> (ModuleName, a)
+qualify m (Qualified Nothing a) = (m, a)
+qualify _ (Qualified (Just m) a) = (m, a)
diff --git a/src/Language/PureScript/Operators.hs b/src/Language/PureScript/Operators.hs
--- a/src/Language/PureScript/Operators.hs
+++ b/src/Language/PureScript/Operators.hs
@@ -33,12 +33,12 @@
 import qualified Text.Parsec.Pos as P
 import qualified Text.Parsec.Expr as P
 
-rebracket :: [Declaration] -> Either String [Declaration]
-rebracket ds = do
-  m <- collectFixities ds
+rebracket :: [Module] -> Either String [Module]
+rebracket ms = forM ms $ \(Module name ds) -> do
+  m <- collectFixities (ModuleName name) ds
   let opTable = customOperatorTable m
-  ds' <- G.everywhereM' (G.mkM (matchOperators opTable)) ds
-  return $ G.everywhere (G.mkT removeParens) ds'
+  ds' <- G.everywhereM' (G.mkM (matchOperators (ModuleName name) opTable)) ds
+  return $ Module name $ G.everywhere (G.mkT removeParens) ds'
 
 removeParens :: Value -> Value
 removeParens (Parens val) = val
@@ -56,8 +56,8 @@
 
 type Chain = [Either Value (Qualified Ident)]
 
-matchOperators :: [[(Qualified Ident, Value -> Value -> Value, Associativity)]] -> Value -> Either String Value
-matchOperators ops val = G.everywhereM' (G.mkM parseChains) val
+matchOperators :: ModuleName -> [[(Qualified Ident, Value -> Value -> Value, Associativity)]] -> Value -> Either String Value
+matchOperators moduleName ops val = G.everywhereM' (G.mkM parseChains) val
   where
   parseChains :: Value -> Either String Value
   parseChains b@(BinaryNoParens _ _ _) = bracketChain (extendChain b)
@@ -67,7 +67,7 @@
   extendChain other = [Left other]
   bracketChain :: Chain -> Either String Value
   bracketChain = either (Left . show) Right . P.parse (P.buildExpressionParser opTable parseValue <* P.eof) "operator expression"
-  opTable = map (map (\(name, f, a) -> P.Infix (P.try (matchOp name) >> return f) (toAssoc a))) ops
+  opTable = map (map (\(name, f, a) -> P.Infix (P.try (matchOp moduleName name) >> return f) (toAssoc a))) ops
     ++ [[P.Infix (P.try (parseOp >>= \ident -> return (\t1 t2 -> App (App (Var ident) [t1]) [t2]))) P.AssocLeft]]
 
 toAssoc :: Associativity -> P.Assoc
@@ -80,27 +80,24 @@
 parseOp :: P.Parsec Chain () (Qualified Ident)
 parseOp = P.token show (const (P.initialPos "")) (either (const Nothing) Just) P.<?> "operator"
 
-matchOp :: Qualified Ident -> P.Parsec Chain () ()
-matchOp op = do
+matchOp :: ModuleName -> Qualified Ident -> P.Parsec Chain () ()
+matchOp moduleName op = do
   ident <- parseOp
-  guard (ident == op)
+  guard (qualify moduleName ident == qualify moduleName op)
 
-collectFixities :: [Declaration] -> Either String (M.Map (Qualified Ident) Fixity)
-collectFixities = go M.empty global
+collectFixities :: ModuleName -> [Declaration] -> Either String (M.Map (Qualified Ident) Fixity)
+collectFixities = go M.empty
   where
-  go :: M.Map (Qualified Ident) Fixity -> ModulePath -> [Declaration] -> Either String (M.Map (Qualified Ident) Fixity)
+  go :: M.Map (Qualified Ident) Fixity -> ModuleName -> [Declaration] -> Either String (M.Map (Qualified Ident) Fixity)
   go m _ [] = return m
-  go m p (FixityDeclaration fixity name : rest) = do
-    let qual = Qualified p (Op name)
+  go m moduleName (FixityDeclaration fixity name : rest) = do
+    let qual = Qualified (Just moduleName) (Op name)
     when (qual `M.member` m) (Left $ "redefined fixity for " ++ show name)
-    go (M.insert qual fixity m) p rest
-  go m p (ModuleDeclaration name decls : rest) = do
-    m' <- go m (subModule p name) decls
-    go m' p rest
-  go m p (_:ds) = go m p ds
+    go (M.insert qual fixity m) moduleName rest
+  go m moduleName (_:ds) = go m moduleName ds
 
 globalOp :: String -> Qualified Ident
-globalOp = Qualified global . Op
+globalOp = Qualified Nothing . Op
 
 builtIns :: [(Qualified Ident, Value -> Value -> Value, Precedence, Associativity)]
 builtIns = [ (globalOp "<", Binary LessThan, 3, Infixl)
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
@@ -188,11 +188,10 @@
 properName = lexeme $ ProperName <$> P.try ((:) <$> P.upper <*> many (PT.identLetter langDef) P.<?> "name")
 
 parseQualified :: P.Parsec String ParseState a -> P.Parsec String ParseState (Qualified a)
-parseQualified parser = part global
+parseQualified parser = qual
   where
-  part path = (do name <- P.try (properName <* delimiter)
-                  part (subModule path name))
-              <|> (Qualified path <$> P.try parser)
+  qual = (Qualified <$> (Just . ModuleName <$> P.try (properName <* delimiter)) <*> parser)
+     <|> (Qualified Nothing <$> P.try parser)
   delimiter = indented *> dot
 
 integerOrFloat :: P.Parsec String u (Either Integer Double)
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
@@ -14,7 +14,8 @@
 
 module Language.PureScript.Parser.Declarations (
     parseDeclaration,
-    parseDeclarations
+    parseModule,
+    parseModules
 ) where
 
 import Control.Applicative
@@ -84,25 +85,13 @@
   name <- operator
   return $ FixityDeclaration fixity name
 
-parseModuleDeclaration :: P.Parsec String ParseState Declaration
-parseModuleDeclaration = do
-  reserved "module"
-  indented
-  name <- properName
-  lexeme $ P.string "where"
-  decls <- mark (P.many (same *> parseDeclaration))
-  return $ ModuleDeclaration name decls
-
-parseModulePath :: P.Parsec String ParseState ModulePath
-parseModulePath = ModulePath <$> properName `sepBy1` dot
-
 parseImportDeclaration :: P.Parsec String ParseState Declaration
 parseImportDeclaration = do
   reserved "import"
   indented
-  modulePath <- parseModulePath
-  idents <- P.optionMaybe $ parens $ commaSep1 parseIdent
-  return $ ImportDeclaration modulePath idents
+  moduleName <- ModuleName <$> properName
+  idents <- P.optionMaybe $ parens $ commaSep1 (Left <$> parseIdent <|> Right <$> properName)
+  return $ ImportDeclaration moduleName idents
 
 parseDeclaration :: P.Parsec String ParseState Declaration
 parseDeclaration = P.choice
@@ -112,8 +101,16 @@
                    , parseValueDeclaration
                    , parseExternDeclaration
                    , parseFixityDeclaration
-                   , parseModuleDeclaration
                    , parseImportDeclaration ] P.<?> "declaration"
 
-parseDeclarations :: P.Parsec String ParseState [Declaration]
-parseDeclarations = whiteSpace *> mark (P.many (same *> parseDeclaration)) <* P.eof
+parseModule :: P.Parsec String ParseState Module
+parseModule = do
+  reserved "module"
+  indented
+  name <- properName
+  lexeme $ P.string "where"
+  decls <- mark (P.many (same *> parseDeclaration))
+  return $ Module name decls
+
+parseModules :: P.Parsec String ParseState [Module]
+parseModules = whiteSpace *> mark (P.many (same *> parseModule)) <* P.eof
diff --git a/src/Language/PureScript/Scope.hs b/src/Language/PureScript/Scope.hs
--- a/src/Language/PureScript/Scope.hs
+++ b/src/Language/PureScript/Scope.hs
@@ -32,7 +32,7 @@
   where
   namesV :: Value -> [Ident]
   namesV (Abs args _) = args
-  namesV (Var (Qualified (ModulePath []) name)) = [name]
+  namesV (Var (Qualified Nothing name)) = [name]
   namesV _ = []
   namesS :: Statement -> [Ident]
   namesS (VariableIntroduction name _) = [name]
diff --git a/src/Language/PureScript/TypeChecker.hs b/src/Language/PureScript/TypeChecker.hs
--- a/src/Language/PureScript/TypeChecker.hs
+++ b/src/Language/PureScript/TypeChecker.hs
@@ -26,114 +26,173 @@
 
 import Data.Maybe
 import qualified Data.Map as M
+import Control.Monad.State
+import Control.Monad.Error
+import Data.Either (rights, lefts)
 
-import Language.PureScript.Values
 import Language.PureScript.Types
 import Language.PureScript.Names
 import Language.PureScript.Kinds
 import Language.PureScript.Declarations
 
-import Control.Monad.State
-import Control.Monad.Error
+addDataType :: ModuleName -> ProperName -> [String] -> [(ProperName, Maybe Type)] -> Kind -> Check ()
+addDataType moduleName name args dctors ctorKind = do
+  env <- getEnv
+  putEnv $ env { types = M.insert (moduleName, name) (ctorKind, Data) (types env) }
+  forM_ dctors $ \(dctor, maybeTy) ->
+    rethrow (("Error in data constructor " ++ show name ++ ":\n") ++) $
+      addDataConstructor moduleName name args dctor maybeTy
 
-typeCheckAll :: [Declaration] -> Check ()
-typeCheckAll [] = return ()
-typeCheckAll (DataDeclaration name args dctors : rest) = do
+addDataConstructor :: ModuleName -> ProperName -> [String] -> ProperName -> Maybe Type -> Check ()
+addDataConstructor moduleName name args dctor maybeTy = do
+  env <- getEnv
+  dataConstructorIsNotDefined moduleName dctor
+  let retTy = foldl TypeApp (TypeConstructor (Qualified (Just moduleName) name)) (map TypeVar args)
+  let dctorTy = maybe retTy (\ty -> Function [ty] retTy) maybeTy
+  let polyType = mkForAll args dctorTy
+  putEnv $ env { dataConstructors = M.insert (moduleName, dctor) polyType (dataConstructors env) }
+
+addTypeSynonym :: ModuleName -> ProperName -> [String] -> Type -> Kind -> Check ()
+addTypeSynonym moduleName name args ty kind = do
+  env <- getEnv
+  putEnv $ env { types = M.insert (moduleName, name) (kind, TypeSynonym) (types env)
+               , typeSynonyms = M.insert (moduleName, name) (args, ty) (typeSynonyms env) }
+
+typeIsNotDefined :: ModuleName -> ProperName -> Check ()
+typeIsNotDefined moduleName name = do
+  env <- getEnv
+  guardWith (show name ++ " is already defined") $
+    not $ M.member (moduleName, name) (types env)
+
+dataConstructorIsNotDefined :: ModuleName -> ProperName -> Check ()
+dataConstructorIsNotDefined moduleName dctor = do
+  env <- getEnv
+  guardWith (show dctor ++ " is already defined") $
+    not $ M.member (moduleName, dctor) (dataConstructors env)
+
+valueIsNotDefined :: ModuleName -> Ident -> Check ()
+valueIsNotDefined moduleName name = do
+  env <- getEnv
+  case M.lookup (moduleName, name) (names env) of
+    Just _ -> throwError $ show name ++ " is already defined"
+    Nothing -> return ()
+
+addValue :: ModuleName -> Ident -> Type -> Check ()
+addValue moduleName name ty = do
+  env <- getEnv
+  putEnv (env { names = M.insert (moduleName, name) (ty, Value) (names env) })
+
+typeCheckAll :: ModuleName -> [Declaration] -> Check ()
+typeCheckAll _ [] = return ()
+typeCheckAll moduleName (DataDeclaration name args dctors : rest) = do
   rethrow (("Error in type constructor " ++ show name ++ ":\n") ++) $ do
-    env <- getEnv
-    modulePath <- checkModulePath `fmap` get
-    guardWith (show name ++ " is already defined") $ not $ M.member (modulePath, name) (types env)
-    ctorKind <- kindsOf (Just name) args (mapMaybe snd dctors)
-    putEnv $ env { types = M.insert (modulePath, name) (ctorKind, Data) (types env) }
-    forM_ dctors $ \(dctor, maybeTy) ->
-      rethrow (("Error in data constructor " ++ show name ++ ":\n") ++) $ do
-        env' <- getEnv
-        guardWith (show dctor ++ " is already defined") $ not $ M.member (modulePath, dctor) (dataConstructors env')
-        let retTy = foldl TypeApp (TypeConstructor (Qualified modulePath name)) (map TypeVar args)
-        let dctorTy = maybe retTy (\ty -> Function [ty] retTy) maybeTy
-        let polyType = mkForAll args dctorTy
-        putEnv $ env' { dataConstructors = M.insert (modulePath, dctor) polyType (dataConstructors env') }
-  typeCheckAll rest
-typeCheckAll (TypeSynonymDeclaration name args ty : rest) = do
+    typeIsNotDefined moduleName name
+    ctorKind <- kindsOf moduleName name args (mapMaybe snd dctors)
+    addDataType moduleName name args dctors ctorKind
+  typeCheckAll moduleName rest
+typeCheckAll moduleName (DataBindingGroupDeclaration tys : rest) = do
+  rethrow (("Error in data binding group " ++ show (map (\(name, _, _) -> name) tys) ++ ":\n") ++) $ do
+    forM_ tys $ \(name, _, _) ->
+      typeIsNotDefined moduleName name
+    ks <- kindsOfAll moduleName (map (\(name, args, dctors) -> (name, args, mapMaybe snd dctors)) tys)
+    forM (zip tys ks) $ \((name, args, dctors), ctorKind) ->
+      addDataType moduleName name args dctors ctorKind
+  typeCheckAll moduleName rest
+typeCheckAll moduleName (TypeSynonymDeclaration name args ty : rest) = do
   rethrow (("Error in type synonym " ++ show name ++ ":\n") ++) $ do
-    env <- getEnv
-    modulePath <- checkModulePath `fmap` get
-    guardWith (show name ++ " is already defined") $ not $ M.member (modulePath, name) (types env)
-    kind <- kindsOf (Just name) args [ty]
-    putEnv $ env { types = M.insert (modulePath, name) (kind, TypeSynonym) (types env)
-                 , typeSynonyms = M.insert (modulePath, name) (args, ty) (typeSynonyms env) }
-  typeCheckAll rest
-typeCheckAll (TypeDeclaration name ty : ValueDeclaration name' [] Nothing val : rest) | name == name' =
-  typeCheckAll (ValueDeclaration name [] Nothing (TypedValue val ty) : rest)
-typeCheckAll (TypeDeclaration name _ : _) = throwError $ "Orphan type declaration for " ++ show name
-typeCheckAll (ValueDeclaration name [] Nothing val : rest) = do
+    typeIsNotDefined moduleName name
+    kind <- kindsOf moduleName name args [ty]
+    addTypeSynonym moduleName name args ty kind
+  typeCheckAll moduleName rest
+typeCheckAll _ (TypeDeclaration _ _ : _) = error "Type declarations should have been removed"
+typeCheckAll moduleName (ValueDeclaration name [] Nothing val : rest) = do
   rethrow (("Error in declaration " ++ show name ++ ":\n") ++) $ do
-    env <- getEnv
-    modulePath <- checkModulePath `fmap` get
-    case M.lookup (modulePath, name) (names env) of
-      Just _ -> throwError $ show name ++ " is already defined"
-      Nothing -> do
-        ty <- typeOf (Just name) val
-        putEnv (env { names = M.insert (modulePath, name) (ty, Value) (names env) })
-  typeCheckAll rest
-typeCheckAll (ValueDeclaration _ _ _ _ : _) = error "Binders were not desugared"
-typeCheckAll (ExternDataDeclaration name kind : rest) = do
+    valueIsNotDefined moduleName name
+    [ty] <- typesOf moduleName [(name, val)]
+    addValue moduleName name ty
+  typeCheckAll moduleName rest
+typeCheckAll _ (ValueDeclaration _ _ _ _ : _) = error "Binders were not desugared"
+typeCheckAll moduleName (BindingGroupDeclaration vals : rest) = do
+  rethrow (("Error in binding group " ++ show (map fst vals) ++ ":\n") ++) $ do
+    forM_ (map fst vals) $ \name ->
+      valueIsNotDefined moduleName name
+    tys <- typesOf moduleName vals
+    forM (zip (map fst vals) tys) $ \(name, ty) ->
+      addValue moduleName name ty
+  typeCheckAll moduleName rest
+typeCheckAll moduleName (ExternDataDeclaration name kind : rest) = do
   env <- getEnv
-  modulePath <- checkModulePath `fmap` get
-  guardWith (show name ++ " is already defined") $ not $ M.member (modulePath, name) (types env)
-  putEnv $ env { types = M.insert (modulePath, name) (kind, TypeSynonym) (types env) }
-  typeCheckAll rest
-typeCheckAll (ExternMemberDeclaration member name ty : rest) = do
+  guardWith (show name ++ " is already defined") $ not $ M.member (moduleName, name) (types env)
+  putEnv $ env { types = M.insert (moduleName, name) (kind, TypeSynonym) (types env) }
+  typeCheckAll moduleName rest
+typeCheckAll moduleName (ExternMemberDeclaration member name ty : rest) = do
   rethrow (("Error in foreign import member declaration " ++ show name ++ ":\n") ++) $ do
     env <- getEnv
-    modulePath <- checkModulePath `fmap` get
-    kind <- kindOf ty
+    kind <- kindOf moduleName ty
     guardWith "Expected kind *" $ kind == Star
-    case M.lookup (modulePath, name) (names env) of
+    case M.lookup (moduleName, name) (names env) of
       Just _ -> throwError $ show name ++ " is already defined"
       Nothing -> case ty of
         _ | isSingleArgumentFunction ty -> do
-          putEnv (env { names = M.insert (modulePath, name) (ty, Extern) (names env)
-                      , members = M.insert (modulePath, name) member (members env) })
+          putEnv (env { names = M.insert (moduleName, name) (ty, Extern) (names env)
+                      , members = M.insert (moduleName, name) member (members env) })
           | otherwise -> throwError "Foreign member declarations must have function types, with an single argument."
-  typeCheckAll rest
+  typeCheckAll moduleName rest
   where
     isSingleArgumentFunction (Function [_] _) = True
     isSingleArgumentFunction (ForAll _ t) = isSingleArgumentFunction t
     isSingleArgumentFunction _ = False
-typeCheckAll (ExternDeclaration name ty : rest) = do
+typeCheckAll moduleName (ExternDeclaration name ty : rest) = do
   rethrow (("Error in foreign import declaration " ++ show name ++ ":\n") ++) $ do
     env <- getEnv
-    modulePath <- checkModulePath `fmap` get
-    kind <- kindOf ty
+    kind <- kindOf moduleName ty
     guardWith "Expected kind *" $ kind == Star
-    case M.lookup (modulePath, name) (names env) of
+    case M.lookup (moduleName, name) (names env) of
       Just _ -> throwError $ show name ++ " is already defined"
-      Nothing -> putEnv (env { names = M.insert (modulePath, name) (ty, Extern) (names env) })
-  typeCheckAll rest
-typeCheckAll (FixityDeclaration _ name : rest) = do
-  typeCheckAll rest
+      Nothing -> putEnv (env { names = M.insert (moduleName, name) (ty, Extern) (names env) })
+  typeCheckAll moduleName rest
+typeCheckAll moduleName (FixityDeclaration _ name : rest) = do
+  typeCheckAll moduleName rest
   env <- getEnv
-  modulePath <- checkModulePath `fmap` get
-  guardWith ("Fixity declaration with no binding: " ++ name) $ M.member (modulePath, Op name) $ names env
-typeCheckAll (ModuleDeclaration name decls : rest) = do
-  withModule name $ typeCheckAll decls
-  typeCheckAll rest
-typeCheckAll (ImportDeclaration modulePath idents : rest) = do
+  guardWith ("Fixity declaration with no binding: " ++ name) $ M.member (moduleName, Op name) $ names env
+typeCheckAll currentModule (ImportDeclaration moduleName idents : rest) = do
   env <- getEnv
-  currentModule <- checkModulePath `fmap` get
   rethrow errorMessage $ do
-    guardWith ("Module " ++ show modulePath ++ " does not exist") $ moduleExists env
+    guardWith ("Module " ++ show moduleName ++ " does not exist") $ moduleExists env
     case idents of
-      Nothing     -> bindIdents (map snd $ filterModule env) currentModule env
-      Just idents' -> bindIdents idents' currentModule env
-  typeCheckAll rest
- where errorMessage = (("Error in import declaration " ++ show modulePath ++ ":\n") ++)
-       filterModule = filter ((== modulePath) . fst) . M.keys . names
-       moduleExists env = not $ null $ filterModule env
-       bindIdents idents' currentModule env =
+      Nothing -> do
+        shadowIdents (map snd $ filterModule (names env)) env
+        shadowTypes (map snd $ filterModule (types env)) env
+      Just idents' -> do
+        shadowIdents (lefts idents') env
+        shadowTypes (rights idents') env
+  typeCheckAll currentModule rest
+ where errorMessage = (("Error in import declaration " ++ show moduleName ++ ":\n") ++)
+       filterModule = filter ((== moduleName) . fst) . M.keys
+       moduleExists env = not (null (filterModule (names env))) || not (null (filterModule (types env)))
+       shadowIdents idents' env =
          forM_ idents' $ \ident -> do
            guardWith (show currentModule ++ "." ++ show ident ++ " is already defined") $ (currentModule, ident) `M.notMember` names env
-           case (modulePath, ident) `M.lookup` names env of
-             Just (pt, _) -> modifyEnv (\e -> e { names = M.insert (currentModule, ident) (pt, Alias modulePath ident) (names e) })
-             Nothing -> throwError (show modulePath ++ "." ++ show ident ++ " is undefined")
+           case (moduleName, ident) `M.lookup` names env of
+             Just (pt, _) -> modifyEnv (\e -> e { names = M.insert (currentModule, ident) (pt, Alias moduleName ident) (names e) })
+             Nothing -> throwError (show moduleName ++ "." ++ show ident ++ " is undefined")
+       shadowTypes pns env =
+         forM_ pns $ \pn -> do
+           guardWith (show currentModule ++ "." ++ show pn ++ " is already defined") $ (currentModule, pn) `M.notMember` types env
+           case (moduleName, pn) `M.lookup` types env of
+             Nothing -> throwError (show moduleName ++ "." ++ show pn ++ " is undefined")
+             Just (k, _) -> do
+               modifyEnv (\e -> e { types = M.insert (currentModule, pn) (k, DataAlias moduleName pn) (types e) })
+               let keys = map (snd . fst) . filter (\(_, fn) -> fn `constructs` pn) . M.toList . dataConstructors $ env
+               forM_ keys $ \dctor -> do
+                 guardWith (show currentModule ++ "." ++ show dctor ++ " is already defined") $ (currentModule, dctor) `M.notMember` dataConstructors env
+                 case (moduleName, dctor) `M.lookup` dataConstructors env of
+                   Just ctorTy -> modifyEnv (\e -> e { dataConstructors = M.insert (currentModule, dctor) ctorTy (dataConstructors e) })
+                   Nothing -> throwError (show moduleName ++ "." ++ show dctor ++ " is undefined")
+       constructs (TypeConstructor (Qualified (Just mn) pn')) pn
+         = mn == moduleName && pn' == pn
+       constructs (ForAll _ ty) pn = ty `constructs` pn
+       constructs (Function _ ty) pn = ty `constructs` pn
+       constructs (TypeApp ty _) pn = ty `constructs` pn
+       constructs fn _ = error $ "Invalid arguments to construct" ++ show fn
+
diff --git a/src/Language/PureScript/TypeChecker/Kinds.hs b/src/Language/PureScript/TypeChecker/Kinds.hs
--- a/src/Language/PureScript/TypeChecker/Kinds.hs
+++ b/src/Language/PureScript/TypeChecker/Kinds.hs
@@ -15,8 +15,9 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 
 module Language.PureScript.TypeChecker.Kinds (
+    kindOf,
     kindsOf,
-    kindOf
+    kindsOfAll
 ) where
 
 import Language.PureScript.Types
@@ -28,6 +29,7 @@
 
 import Control.Monad.State
 import Control.Monad.Error
+import Control.Monad.Reader
 
 import Control.Applicative
 
@@ -53,14 +55,31 @@
   unknowns (FunKind k1 k2) = unknowns k1 ++ unknowns k2
   unknowns _ = []
 
-kindOf :: Type -> Check Kind
-kindOf ty = fmap (\(k, _, _) -> k) . runSubst $ starIfUnknown <$> infer Nothing M.empty ty
+kindOf :: ModuleName -> Type -> Check Kind
+kindOf moduleName ty = fmap (\(k, _, _) -> k) . runSubst (SubstContext moduleName) $ starIfUnknown <$> infer ty
 
-kindsOf :: Maybe ProperName -> [String] -> [Type] -> Check Kind
-kindsOf name args ts = fmap (starIfUnknown . (\(k, _, _) -> k)) . runSubst $ do
+kindsOf :: ModuleName -> ProperName -> [String] -> [PolyType] -> Check Kind
+kindsOf moduleName name args ts = fmap (starIfUnknown . (\(k, _, _) -> k)) . runSubst (SubstContext moduleName) $ do
   tyCon <- fresh
   kargs <- replicateM (length args) fresh
-  ks <- inferAll (fmap (\pn -> (pn, tyCon)) name) (M.fromList (zip args kargs)) ts
+  let dict = (name, tyCon) : zip (map ProperName args) kargs
+  bindLocalTypeVariables moduleName dict $
+    solveTypes ts kargs tyCon
+
+kindsOfAll :: ModuleName -> [(ProperName, [String], [PolyType])] -> Check [Kind]
+kindsOfAll moduleName tys = fmap (map starIfUnknown . (\(ks, _, _) -> ks)) . runSubst (SubstContext moduleName) $ do
+  tyCons <- replicateM (length tys) fresh
+  let dict = zipWith (\(name, _, _) tyCon -> (name, tyCon)) tys tyCons
+  bindLocalTypeVariables moduleName dict $
+    zipWithM (\tyCon (_, args, ts) -> do
+      kargs <- replicateM (length args) fresh
+      let argDict = zip (map ProperName args) kargs
+      bindLocalTypeVariables moduleName argDict $
+        solveTypes ts kargs tyCon) tyCons tys
+
+solveTypes :: [Type] -> [Kind] -> Kind -> Subst Kind
+solveTypes ts kargs tyCon = do
+  ks <- mapM infer ts
   tyCon ~~ foldr FunKind Star kargs
   forM_ ks $ \k -> k ~~ Star
   return tyCon
@@ -70,56 +89,51 @@
 starIfUnknown (FunKind k1 k2) = FunKind (starIfUnknown k1) (starIfUnknown k2)
 starIfUnknown k = k
 
-inferAll :: Maybe (ProperName, Kind) -> M.Map String Kind -> [Type] -> Subst [Kind]
-inferAll name m = mapM (infer name m)
-
-infer :: Maybe (ProperName, Kind) -> M.Map String Kind -> Type -> Subst Kind
-infer name m (Array t) = do
-  k <- infer name m t
+infer :: Type -> Subst Kind
+infer (Array t) = do
+  k <- infer t
   k ~~ Star
   return Star
-infer name m (Object row) = do
-  k <- inferRow name m row
+infer (Object row) = do
+  k <- inferRow row
   k ~~ Row
   return Star
-infer name m (Function args ret) = do
-  ks <- inferAll name m args
-  k <- infer name m ret
+infer (Function args ret) = do
+  ks <- mapM infer args
+  k <- infer ret
   k ~~ Star
   forM ks (~~ Star)
   return Star
-infer _ m (TypeVar v) =
-  case M.lookup v m of
-    Just k -> return k
-    Nothing -> throwError $ "Unbound type variable " ++ v
-infer (Just (name, k)) _ (TypeConstructor (Qualified (ModulePath []) pn)) | name == pn = return k
-infer _ _ (TypeConstructor v) = do
+infer (TypeVar v) = do
+  moduleName <- substCurrentModule <$> ask
+  lookupTypeVariable moduleName (Qualified Nothing (ProperName v))
+infer (TypeConstructor v) = do
   env <- liftCheck getEnv
-  modulePath <- checkModulePath `fmap` get
-  case M.lookup (qualify modulePath v) (types env) of
+  moduleName <- substCurrentModule `fmap` ask
+  case M.lookup (qualify moduleName v) (types env) of
     Nothing -> throwError $ "Unknown type constructor '" ++ show v ++ "'"
     Just (kind, _) -> return kind
-infer name m (TypeApp t1 t2) = do
+infer (TypeApp t1 t2) = do
   k0 <- fresh
-  k1 <- infer name m t1
-  k2 <- infer name m t2
+  k1 <- infer t1
+  k2 <- infer t2
   k1 ~~ FunKind k2 k0
   return k0
-infer name m (ForAll ident ty) = do
+infer (ForAll ident ty) = do
   k <- fresh
-  infer name (M.insert ident k m) ty
-infer _ _ _ = return Star
+  moduleName <- substCurrentModule <$> ask
+  bindLocalTypeVariables moduleName [(ProperName ident, k)] $ infer ty
+infer _ = return Star
 
-inferRow :: Maybe (ProperName, Kind) -> M.Map String Kind -> Row -> Subst Kind
-inferRow _ m (RowVar v) = do
-  case M.lookup v m of
-    Just k -> return k
-    Nothing -> throwError $ "Unbound row variable " ++ v
-inferRow _ _ REmpty = return Row
-inferRow name m (RCons _ ty row) = do
-  k1 <- infer name m ty
-  k2 <- inferRow name m row
+inferRow :: Row -> Subst Kind
+inferRow (RowVar v) = do
+  moduleName <- substCurrentModule <$> ask
+  lookupTypeVariable moduleName (Qualified Nothing (ProperName v))
+inferRow REmpty = return Row
+inferRow (RCons _ ty row) = do
+  k1 <- infer ty
+  k2 <- inferRow row
   k1 ~~ Star
   k2 ~~ Row
   return Row
-inferRow _ _ _ = error "Invalid row in inferRow"
+inferRow _ = error "Invalid row in inferRow"
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
@@ -28,52 +28,77 @@
 import Control.Applicative
 import Control.Monad.State
 import Control.Monad.Error
+import Control.Monad.Reader
 
 import qualified Data.Map as M
 
-data NameKind = Value | Extern | Alias ModulePath Ident | LocalVariable deriving Show
+data NameKind
+  = Value
+  | Extern
+  | Alias ModuleName Ident
+  | LocalVariable deriving Show
 
-data TypeDeclarationKind = Data | ExternData | TypeSynonym deriving Show
+data TypeDeclarationKind
+  = Data
+  | ExternData
+  | TypeSynonym
+  | DataAlias ModuleName ProperName
+  | LocalTypeVariable deriving Show
 
 data Environment = Environment
-  { names :: M.Map (ModulePath, Ident) (Type, NameKind)
-  , types :: M.Map (ModulePath, ProperName) (Kind, TypeDeclarationKind)
-  , dataConstructors :: M.Map (ModulePath, ProperName) Type
-  , typeSynonyms :: M.Map (ModulePath, ProperName) ([String], Type)
-  , members :: M.Map (ModulePath, Ident) String
+  { names :: M.Map (ModuleName, Ident) (Type, NameKind)
+  , types :: M.Map (ModuleName, ProperName) (Kind, TypeDeclarationKind)
+  , dataConstructors :: M.Map (ModuleName, ProperName) Type
+  , typeSynonyms :: M.Map (ModuleName, ProperName) ([String], Type)
+  , members :: M.Map (ModuleName, Ident) String
   } deriving (Show)
 
 emptyEnvironment :: Environment
 emptyEnvironment = Environment M.empty M.empty M.empty M.empty M.empty
 
-bindNames :: (MonadState CheckState m) => M.Map (ModulePath, Ident) (Type, NameKind) -> m a -> m a
+bindNames :: (MonadState CheckState m) => M.Map (ModuleName, Ident) (Type, NameKind) -> m a -> m a
 bindNames newNames action = do
-  orig <-  get
+  orig <- get
   modify $ \st -> st { checkEnv = (checkEnv st) { names = newNames `M.union` (names . checkEnv $ st) } }
   a <- action
   modify $ \st -> st { checkEnv = (checkEnv st) { names = names . checkEnv $ orig } }
   return a
 
-bindLocalVariables :: (Functor m, MonadState CheckState m) => [(Ident, Type)] -> m a -> m a
-bindLocalVariables bindings action = do
-  modulePath <- checkModulePath `fmap` get
-  bindNames (M.fromList $ flip map bindings $ \(name, ty) -> ((modulePath, name), (ty, LocalVariable))) action
+bindTypes :: (MonadState CheckState m) => M.Map (ModuleName, ProperName) (Kind, TypeDeclarationKind) -> m a -> m a
+bindTypes newNames action = do
+  orig <- get
+  modify $ \st -> st { checkEnv = (checkEnv st) { types = newNames `M.union` (types . checkEnv $ st) } }
+  a <- action
+  modify $ \st -> st { checkEnv = (checkEnv st) { types = types . checkEnv $ orig } }
+  return a
 
-lookupVariable :: (Functor m, MonadState CheckState m, MonadError String m) => Qualified Ident -> m Type
-lookupVariable var = do
+bindLocalVariables :: (Functor m, MonadState CheckState m) => ModuleName -> [(Ident, Type)] -> m a -> m a
+bindLocalVariables moduleName bindings action =
+  bindNames (M.fromList $ flip map bindings $ \(name, ty) -> ((moduleName, name), (ty, LocalVariable))) action
+
+bindLocalTypeVariables :: (Functor m, MonadState CheckState m) => ModuleName -> [(ProperName, Kind)] -> m a -> m a
+bindLocalTypeVariables moduleName bindings action =
+  bindTypes (M.fromList $ flip map bindings $ \(name, k) -> ((moduleName, name), (k, LocalTypeVariable))) action
+
+lookupVariable :: (Functor m, MonadState CheckState m, MonadError String m) => ModuleName -> Qualified Ident -> m Type
+lookupVariable currentModule (Qualified moduleName var) = do
   env <- getEnv
-  modulePath <- checkModulePath <$> get
-  let tries = map (First . flip M.lookup (names env)) (nameResolution modulePath var)
-  case getFirst (mconcat tries) of
+  case M.lookup (fromMaybe currentModule moduleName, var) (names env) of
     Nothing -> throwError $ show var ++ " is undefined"
     Just (ty, _) -> return ty
 
+lookupTypeVariable :: (Functor m, MonadState CheckState m, MonadError String m) => ModuleName -> Qualified ProperName -> m Kind
+lookupTypeVariable currentModule (Qualified moduleName name) = do
+  env <- getEnv
+  case M.lookup (fromMaybe currentModule moduleName, name) (types env) of
+    Nothing -> throwError $ "Type variable " ++ show name ++ " is undefined"
+    Just (k, _) -> return k
+
 data AnyUnifiable where
   AnyUnifiable :: forall t. (Unifiable t) => t -> AnyUnifiable
 
 data CheckState = CheckState { checkEnv :: Environment
                              , checkNextVar :: Int
-                             , checkModulePath :: ModulePath
                              }
 
 newtype Check a = Check { unCheck :: StateT CheckState (Either String) a }
@@ -90,7 +115,7 @@
 
 runCheck :: Check a -> Either String (a, Environment)
 runCheck c = do
-  (a, s) <- flip runStateT (CheckState emptyEnvironment 0 global) $ unCheck c
+  (a, s) <- flip runStateT (CheckState emptyEnvironment 0) $ unCheck c
   return (a, checkEnv s)
 
 guardWith :: (MonadError e m) => e -> Bool -> m ()
@@ -100,14 +125,6 @@
 rethrow :: (MonadError e m) => (e -> e) -> m a -> m a
 rethrow f = flip catchError $ \e -> throwError (f e)
 
-withModule :: ProperName -> Check a -> Check a
-withModule name act = do
-  original <- checkModulePath `fmap` get
-  modify $ \s -> s { checkModulePath = subModule (checkModulePath s) name }
-  a <- act
-  modify $ \s -> s { checkModulePath = original }
-  return a
-
 newtype Substitution = Substitution { runSubstitution :: forall t. (Unifiable t) => Unknown t -> t }
 
 instance Monoid Substitution where
@@ -117,21 +134,23 @@
 data SubstState = SubstState { substSubst :: Substitution
                              , substFutureEscapeChecks :: [AnyUnifiable] }
 
-newtype Subst a = Subst { unSubst :: StateT SubstState Check a }
-  deriving (Functor, Monad, Applicative, MonadPlus)
+newtype SubstContext = SubstContext { substCurrentModule :: ModuleName } deriving (Show)
 
+newtype Subst a = Subst { unSubst :: ReaderT SubstContext (StateT SubstState Check) a }
+  deriving (Functor, Monad, Applicative, MonadPlus, MonadReader SubstContext)
+
 instance MonadState CheckState Subst where
-  get = Subst . lift $ get
-  put = Subst . lift . put
+  get = Subst . lift . lift $ get
+  put = Subst . lift . lift . put
 
 deriving instance MonadError String Subst
 
 liftCheck :: Check a -> Subst a
-liftCheck = Subst . lift
+liftCheck = Subst . lift . lift
 
-runSubst :: (Unifiable a) => Subst a -> Check (a, Substitution, [AnyUnifiable])
-runSubst subst = do
-  (a, s) <- flip runStateT (SubstState mempty []) . unSubst $ subst
+runSubst :: (Unifiable a) => SubstContext -> Subst a -> Check (a, Substitution, [AnyUnifiable])
+runSubst context subst = do
+  (a, s) <- flip runStateT (SubstState mempty []) . flip runReaderT context . unSubst $ subst
   return (apply (substSubst s) a, substSubst s, substFutureEscapeChecks s)
 
 substituteWith :: (Typeable t) => (Unknown t -> t) -> Substitution
@@ -162,6 +181,13 @@
   isUnknown :: t -> Maybe (Unknown t)
   apply :: Substitution -> t -> t
   unknowns :: t -> [Int]
+
+instance (Unifiable a) => Unifiable [a] where
+  unknown _ = error "not supported"
+  (~~) = zipWithM_ (~~)
+  isUnknown _ = error "not supported"
+  apply s = map (apply s)
+  unknowns = concatMap unknowns
 
 occursCheck :: (Unifiable t) => Unknown s -> t -> Subst ()
 occursCheck (Unknown u) t =
diff --git a/src/Language/PureScript/TypeChecker/Synonyms.hs b/src/Language/PureScript/TypeChecker/Synonyms.hs
--- a/src/Language/PureScript/TypeChecker/Synonyms.hs
+++ b/src/Language/PureScript/TypeChecker/Synonyms.hs
@@ -27,22 +27,22 @@
 import Control.Monad.Writer
 import Control.Monad.Error
 
-buildTypeSubstitution :: Qualified ProperName -> Int -> Type -> Either String (Maybe Type)
-buildTypeSubstitution name n = go n []
+buildTypeSubstitution :: ModuleName -> Qualified ProperName -> Int -> Type -> Either String (Maybe Type)
+buildTypeSubstitution moduleName name n = go n []
   where
   go :: Int -> [Type] -> Type -> Either String (Maybe Type)
-  go 0 args (TypeConstructor ctor) | name == ctor = return (Just $ SaturatedTypeSynonym ctor args)
+  go 0 args (TypeConstructor ctor) | qualify moduleName name == qualify moduleName ctor = return (Just $ SaturatedTypeSynonym ctor args)
   go m _ (TypeConstructor ctor) | m > 0 && name == ctor = throwError $ "Partially applied type synonym " ++ show name
   go m args (TypeApp f arg) = go (m - 1) (arg:args) f
   go _ _ _ = return Nothing
 
-saturateTypeSynonym :: (Data d) => Qualified ProperName -> Int -> d -> Either String d
-saturateTypeSynonym name n = everywhereM' (mkM replace)
+saturateTypeSynonym :: (Data d) => ModuleName -> Qualified ProperName -> Int -> d -> Either String d
+saturateTypeSynonym moduleName name n = everywhereM' (mkM replace)
   where
-  replace t = fmap (fromMaybe t) $ buildTypeSubstitution name n t
+  replace t = fmap (fromMaybe t) $ buildTypeSubstitution moduleName name n t
 
-saturateAllTypeSynonyms :: (Data d) => [(Qualified ProperName, Int)] -> d -> Either String d
-saturateAllTypeSynonyms syns d = foldM (\result (name, n) -> saturateTypeSynonym name n result) d syns
+saturateAllTypeSynonyms :: (Data d) => ModuleName -> [(Qualified ProperName, Int)] -> d -> Either String d
+saturateAllTypeSynonyms moduleName syns d = foldM (\result (name, n) -> saturateTypeSynonym moduleName name n result) d syns
 
 
 
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
@@ -15,11 +15,12 @@
 {-# LANGUAGE DeriveDataTypeable, FlexibleContexts #-}
 
 module Language.PureScript.TypeChecker.Types (
-    typeOf
+    typesOf
 ) where
 
 import Data.List
 import Data.Maybe (fromMaybe)
+import Data.Either (lefts, rights)
 import qualified Data.Data as D
 import Data.Generics
        (mkT, something, everywhere, everywhereBut, mkQ, extQ)
@@ -36,6 +37,7 @@
 
 import Control.Monad.State
 import Control.Monad.Error
+import Control.Monad.Reader
 
 import Control.Applicative
 import Control.Arrow (Arrow(..))
@@ -131,42 +133,58 @@
     ret1 `unifyTypes` ret2
   unifyTypes' (TypeVar v1) (TypeVar v2) | v1 == v2 = return ()
   unifyTypes' (TypeConstructor c1) (TypeConstructor c2) = do
-    modulePath <- checkModulePath `fmap` get
-    guardWith ("Cannot unify " ++ show c1 ++ " with " ++ show c2 ++ ".") (qualify modulePath c1 == qualify modulePath c2)
+    env <- getEnv
+    moduleName <- substCurrentModule `fmap` ask
+    guardWith ("Cannot unify " ++ show c1 ++ " with " ++ show c2 ++ ".") (typeConstructorsAreEqual env moduleName c1 c2)
   unifyTypes' (TypeApp t3 t4) (TypeApp t5 t6) = do
     t3 `unifyTypes` t5
     t4 `unifyTypes` t6
   unifyTypes' (Skolem s1) (Skolem s2) | s1 == s2 = return ()
   unifyTypes' t3 t4 = throwError $ "Cannot unify " ++ prettyPrintType t3 ++ " with " ++ prettyPrintType t4 ++ "."
 
-isFunction :: Value -> Bool
-isFunction (Abs _ _) = True
-isFunction (TypedValue untyped _) = isFunction untyped
-isFunction _ = False
+typeConstructorsAreEqual :: Environment -> ModuleName -> Qualified ProperName -> Qualified ProperName -> Bool
+typeConstructorsAreEqual env moduleName c1 c2 =
+  let
+    c1' = qualify moduleName c1
+    c2' = qualify moduleName c2
+  in
+    canonicalize env c1' == canonicalize env c2'
+  where
+  canonicalize :: Environment -> (ModuleName, ProperName) -> (ModuleName, ProperName)
+  canonicalize _ key = case key `M.lookup` types env of
+    Just (_, DataAlias mn' pn') -> (mn', pn')
+    _ -> key
 
-typeOf :: Maybe Ident -> Value -> Check Type
-typeOf name val = do
-  (ty, sub, checks) <- runSubst $ case name of
-        Just ident | isFunction val ->
-          case val of
-            TypedValue value ty -> do
-              kind <- liftCheck $ kindOf ty
-              guardWith ("Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
-              ty' <- replaceAllTypeSynonyms ty
-              modulePath <- checkModulePath <$> get
-              bindNames (M.singleton (modulePath, ident) (ty, LocalVariable)) $ check value ty'
-              return ty'
-            _ -> do
-              me <- fresh
-              modulePath <- checkModulePath <$> get
-              ty <- bindNames (M.singleton (modulePath, ident) (me, LocalVariable)) $ infer val
-              ty ~~ me
-              return ty
-        _ -> infer val
-  escapeCheck checks ty sub
-  skolemEscapeCheck ty
-  return $ varIfUnknown $ desaturateAllTypeSynonyms $ setifyAll ty
+typesOf :: ModuleName -> [(Ident, Value)] -> Check [Type]
+typesOf moduleName vals = do
+  (tys, sub, checks) <- runSubst (SubstContext moduleName) $ do
+    let es = map isTyped vals
+        typed = lefts es
+        untyped = rights es
+        typedDict = map (\(ident, ty, _) -> (ident, ty)) typed
+    untypedNames <- replicateM (length untyped) fresh
+    let untypedDict = zip (map fst untyped) untypedNames
+        dict = M.fromList (map (\(ident, ty) -> ((moduleName, ident), (ty, LocalVariable))) $ typedDict ++ untypedDict)
+    tys <- forM es $ \e -> case e of
+      Left (_, ty, val) -> do
+        kind <- liftCheck $ kindOf moduleName ty
+        guardWith ("Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
+        ty' <- replaceAllTypeSynonyms ty
+        bindNames dict $ check val ty'
+        return ty'
+      Right (ident, val) -> do
+        ty <- bindNames dict $ infer val
+        ty ~~ fromMaybe (error "name not found in dictionary") (lookup ident untypedDict)
+        return ty
+    return tys
+  forM tys $ flip (escapeCheck checks) sub
+  forM tys $ skolemEscapeCheck
+  return $ map (varIfUnknown . desaturateAllTypeSynonyms . setifyAll) tys
 
+isTyped :: (Ident, Value) -> Either (Ident, Type, Value) (Ident, Value)
+isTyped (name, TypedValue value ty) = Left (name, ty, value)
+isTyped (name, value) = Right (name, value)
+
 escapeCheck :: [AnyUnifiable] -> Type -> Substitution -> Check ()
 escapeCheck checks ty sub =
   let
@@ -234,11 +252,12 @@
   ru <- fresh
   return $ replaceRowVars ident ru . replaceTypeVars ident tu $ ty
 
-replaceAllTypeSynonyms :: (Functor m, MonadState CheckState m, MonadError String m) => (D.Data d) => d -> m d
+replaceAllTypeSynonyms :: (Functor m, MonadState CheckState m, MonadReader SubstContext m, MonadError String m) => (D.Data d) => d -> m d
 replaceAllTypeSynonyms d = do
   env <- getEnv
-  let syns = map (\((path, name), (args, _)) -> (Qualified path name, length args)) . M.toList $ typeSynonyms env
-  either throwError return $ saturateAllTypeSynonyms syns d
+  moduleName <- substCurrentModule <$> ask
+  let syns = map (\((path, name), (args, _)) -> (Qualified (Just path) name, length args)) . M.toList $ typeSynonyms env
+  either throwError return $ saturateAllTypeSynonyms moduleName syns d
 
 desaturateAllTypeSynonyms :: (D.Data d) => d -> d
 desaturateAllTypeSynonyms = everywhere (mkT replaceSaturatedTypeSynonym)
@@ -249,8 +268,8 @@
 expandTypeSynonym :: Qualified ProperName -> [Type] -> Subst Type
 expandTypeSynonym name args = do
   env <- getEnv
-  modulePath <- checkModulePath `fmap` get
-  case M.lookup (qualify modulePath name) (typeSynonyms env) of
+  moduleName <- substCurrentModule `fmap` ask
+  case M.lookup (qualify moduleName name) (typeSynonyms env) of
     Just (synArgs, body) -> return $ replaceAllTypeVars (zip synArgs args) body
     Nothing -> error "Type synonym was not defined"
 
@@ -308,7 +327,8 @@
     Just ty -> return ty
 infer' (Abs args ret) = do
   ts <- replicateM (length args) fresh
-  bindLocalVariables (zip args ts) $ do
+  moduleName <- substCurrentModule <$> ask
+  bindLocalVariables moduleName (zip args ts) $ do
     body <- infer' ret
     return $ Function ts body
 infer' app@(App _ _) = do
@@ -318,7 +338,8 @@
   checkFunctionApplications ft argss ret
   return ret
 infer' (Var var) = do
-  ty <- lookupVariable var
+  moduleName <- substCurrentModule <$> ask
+  ty <- lookupVariable moduleName var
   replaceAllTypeSynonyms ty
 infer' (Block ss) = do
   ret <- fresh
@@ -327,8 +348,8 @@
   return ret
 infer' (Constructor c) = do
   env <- getEnv
-  modulePath <- checkModulePath `fmap` get
-  case M.lookup (qualify modulePath c) (dataConstructors env) of
+  moduleName <- substCurrentModule `fmap` ask
+  case M.lookup (qualify moduleName c) (dataConstructors env) of
     Nothing -> throwError $ "Constructor " ++ show c ++ " is undefined"
     Just ty -> replaceAllTypeSynonyms ty
 infer' (Case vals binders) = do
@@ -343,7 +364,8 @@
   t2 ~~ t3
   return t2
 infer' (TypedValue val ty) = do
-  kind <- liftCheck $ kindOf ty
+  moduleName <- substCurrentModule <$> ask
+  kind <- liftCheck $ kindOf moduleName ty
   guardWith ("Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
   ty' <- replaceAllTypeSynonyms ty
   check val ty'
@@ -440,16 +462,16 @@
 inferBinder val (VarBinder name) = return $ M.singleton name val
 inferBinder val (NullaryBinder ctor) = do
   env <- getEnv
-  modulePath <- checkModulePath <$> get
-  case M.lookup (qualify modulePath ctor) (dataConstructors env) of
+  moduleName <- substCurrentModule <$> ask
+  case M.lookup (qualify moduleName ctor) (dataConstructors env) of
     Just ty -> do
       ty `subsumes` val
       return M.empty
     _ -> throwError $ "Constructor " ++ show ctor ++ " is not defined"
 inferBinder val (UnaryBinder ctor binder) = do
   env <- getEnv
-  modulePath <- checkModulePath <$> get
-  case M.lookup (qualify modulePath ctor) (dataConstructors env) of
+  moduleName <- substCurrentModule <$> ask
+  case M.lookup (qualify moduleName ctor) (dataConstructors env) of
     Just ty -> do
       fn <- replaceAllVarsWithUnknowns ty
       case fn of
@@ -490,8 +512,9 @@
 checkBinders :: [Type] -> Type -> [([Binder], Maybe Guard, Value)] -> Subst ()
 checkBinders _ _ [] = return ()
 checkBinders nvals ret ((binders, grd, val):bs) = do
+  moduleName <- substCurrentModule <$> ask
   m1 <- M.unions <$> zipWithM inferBinder nvals binders
-  bindLocalVariables (M.toList m1) $ do
+  bindLocalVariables moduleName (M.toList m1) $ do
     check val ret
     case grd of
       Nothing -> return ()
@@ -501,8 +524,8 @@
 assignVariable :: Ident -> Subst ()
 assignVariable name = do
   env <- checkEnv <$> get
-  modulePath <- checkModulePath <$> get
-  case M.lookup (modulePath, name) (names env) of
+  moduleName <- substCurrentModule <$> ask
+  case M.lookup (moduleName, name) (names env) of
     Just (_, LocalVariable) -> throwError $ "Variable with name " ++ show name ++ " already exists."
     _ -> return ()
 
@@ -525,16 +548,18 @@
   allCodePathsReturn <- checkIfStatement mass ret ifst
   return (allCodePathsReturn, mass)
 checkStatement mass ret (For ident start end inner) = do
+  moduleName <- substCurrentModule <$> ask
   assignVariable ident
   check start Number
   check end Number
-  (allCodePathsReturn, _) <- bindLocalVariables [(ident, Number)] $ checkBlock mass ret inner
+  (allCodePathsReturn, _) <- bindLocalVariables moduleName [(ident, Number)] $ checkBlock mass ret inner
   return (allCodePathsReturn, mass)
 checkStatement mass ret (ForEach ident vals inner) = do
+  moduleName <- substCurrentModule <$> ask
   assignVariable ident
   val <- fresh
   check vals (Array val)
-  (allCodePathsReturn, _) <- bindLocalVariables [(ident, val)] $ checkBlock mass ret inner
+  (allCodePathsReturn, _) <- bindLocalVariables moduleName [(ident, val)] $ checkBlock mass ret inner
   guardWith "Cannot return from within a foreach block" $ not allCodePathsReturn
   return (False, mass)
 checkStatement mass _ (ValueStatement val) = do
@@ -562,8 +587,9 @@
 checkBlock :: M.Map Ident Type -> Type -> [Statement] -> Subst (Bool, M.Map Ident Type)
 checkBlock mass _ [] = return (False, mass)
 checkBlock mass ret (s:ss) = do
+  moduleName <- substCurrentModule <$> ask
   (b1, mass1) <- checkStatement mass ret s
-  bindLocalVariables (M.toList mass1) $ case (b1, ss) of
+  bindLocalVariables moduleName (M.toList mass1) $ case (b1, ss) of
     (True, []) -> return (True, mass1)
     (True, _) -> throwError "Unreachable code"
     (False, ss') -> checkBlock mass1 ret ss'
@@ -602,18 +628,21 @@
 check' (ArrayLiteral vals) (Array ty) = forM_ vals (\val -> check val ty)
 check' (Indexer index vals) ty = check index Number >> check vals (Array ty)
 check' (Abs args ret) (Function argTys retTy) = do
+  moduleName <- substCurrentModule <$> ask
   guardWith "Incorrect number of function arguments" (length args == length argTys)
-  bindLocalVariables (zip args argTys) $ check ret retTy
+  bindLocalVariables moduleName (zip args argTys) $ check ret retTy
 check' app@(App _ _) ret = do
   let (f, argss) = unfoldApplication app
   ft <- infer f
   checkFunctionApplications ft argss ret
 check' (Var var) ty = do
-  ty1 <- lookupVariable var
+  moduleName <- substCurrentModule <$> ask
+  ty1 <- lookupVariable moduleName var
   repl <- replaceAllTypeSynonyms ty1
   repl `subsumes` ty
 check' (TypedValue val ty1) ty2 = do
-  kind <- liftCheck $ kindOf ty1
+  moduleName <- substCurrentModule <$> ask
+  kind <- liftCheck $ kindOf moduleName ty1
   guardWith ("Expected type of kind *, was " ++ prettyPrintKind kind) $ kind == Star
   ty1 `subsumes` ty2
   check val ty1
@@ -643,8 +672,8 @@
   guardWith "Block is missing a return statement" allCodePathsReturn
 check' (Constructor c) ty = do
   env <- getEnv
-  modulePath <- checkModulePath <$> get
-  case M.lookup (qualify modulePath c) (dataConstructors env) of
+  moduleName <- substCurrentModule <$> ask
+  case M.lookup (qualify moduleName c) (dataConstructors env) of
     Nothing -> throwError $ "Constructor " ++ show c ++ " is undefined"
     Just ty1 -> do
       repl <- replaceAllTypeSynonyms ty1
diff --git a/src/Language/PureScript/TypeDeclarations.hs b/src/Language/PureScript/TypeDeclarations.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/PureScript/TypeDeclarations.hs
@@ -0,0 +1,35 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  Language.PureScript.TypeDeclarations
+-- Copyright   :  (c) Phil Freeman 2013
+-- License     :  MIT
+--
+-- Maintainer  :  Phil Freeman <paf31@cantab.net>
+-- Stability   :  experimental
+-- Portability :
+--
+-- |
+--
+-----------------------------------------------------------------------------
+
+module Language.PureScript.TypeDeclarations (
+    desugarTypeDeclarations,
+    desugarTypeDeclarationsModule
+) where
+
+import Control.Applicative
+import Control.Monad.Error.Class
+import Control.Monad (forM)
+
+import Language.PureScript.Declarations
+import Language.PureScript.Values
+
+desugarTypeDeclarationsModule :: [Module] -> Either String [Module]
+desugarTypeDeclarationsModule ms = forM ms $ \(Module name ds) -> Module name <$> desugarTypeDeclarations ds
+
+desugarTypeDeclarations :: [Declaration] -> Either String [Declaration]
+desugarTypeDeclarations (TypeDeclaration name ty : ValueDeclaration name' [] Nothing val : rest) | name == name' =
+  desugarTypeDeclarations (ValueDeclaration name [] Nothing (TypedValue val ty) : rest)
+desugarTypeDeclarations (TypeDeclaration name _ : _) = throwError $ "Orphan type declaration for " ++ show name
+desugarTypeDeclarations (d:ds) = (:) d <$> desugarTypeDeclarations ds
+desugarTypeDeclarations [] = return []
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -22,21 +22,21 @@
 import qualified System.IO.UTF8 as U
 import Text.Parsec (ParseError)
 
-readInput :: Maybe [FilePath] -> IO (Either ParseError [P.Declaration])
-readInput Nothing = getContents >>= return . P.runIndentParser P.parseDeclarations
+readInput :: Maybe [FilePath] -> IO (Either ParseError [P.Module])
+readInput Nothing = getContents >>= return . P.runIndentParser P.parseModules
 readInput (Just input) = fmap (fmap concat . sequence) $ forM input $ \inputFile -> do
   text <- U.readFile inputFile
-  return $ P.runIndentParser P.parseDeclarations text
+  return $ P.runIndentParser P.parseModules text
 
 compile :: Maybe [FilePath] -> Maybe FilePath -> Maybe FilePath -> IO ()
 compile input output externs = do
-  asts <- readInput input
-  case asts of
+  modules <- readInput input
+  case modules of
     Left err -> do
       U.print err
       exitFailure
-    Right decls ->
-      case P.compile decls of
+    Right ms ->
+      case P.compile ms of
         Left err -> do
           U.putStrLn err
           exitFailure
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -27,12 +27,12 @@
 
 compile :: FilePath -> IO (Either String P.Environment)
 compile inputFile = do
-  ast <- P.runIndentParser P.parseDeclarations <$> U.readFile inputFile
-  case ast of
+  modules <- P.runIndentParser P.parseModules <$> U.readFile inputFile
+  case modules of
     Left parseError -> do
       return (Left $ show parseError)
-    Right decls -> do
-      case P.compile decls of
+    Right ms -> do
+      case P.compile ms of
         Left typeError -> do
           return (Left typeError)
         Right (_, _, env) -> do
