diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -50,7 +50,7 @@
 	$(RM) html
 
 
-VERSION = 0.4.12
+VERSION = 0.5.0
 DISTDIR = alms-$(VERSION)
 TARBALL = $(DISTDIR).tar.gz
 
diff --git a/alms.cabal b/alms.cabal
--- a/alms.cabal
+++ b/alms.cabal
@@ -1,5 +1,5 @@
 Name:           alms
-Version:        0.4.12
+Version:        0.5.0
 Copyright:      2010, Jesse A. Tov
 Cabal-Version:  >= 1.8
 License:        BSD3
@@ -31,8 +31,10 @@
 
 Flag editline
   Description: Enable line editing using the editline package
-  Default:     True
 
+Flag parsec3
+  Description: Use version 3 of the parsec package
+
 Flag readline
   Description: Enable line editing using the readline package
   Default:     False
@@ -47,7 +49,6 @@
                         syb >= 0.1,
                         pretty >= 1,
                         containers >= 0.1,
-                        parsec == 2.*,
                         mtl >= 1.1,
                         filepath >= 1.1,
                         network >= 2.2,
@@ -69,6 +70,7 @@
                         Basis.Thread,
                         BasisUtils,
                         Coercion,
+                        Compat,
                         Dynamics,
                         Env,
                         ErrorMessage,
@@ -121,4 +123,11 @@
     if flag(editline)
       Build-Depends:  editline >= 0.2.1
       CPP-Options:    -DUSE_READLINE=System.Console.Editline.Readline
+
+  if flag(parsec3)
+    Build-Depends:  parsec == 3.*
+    CPP-Options:    -DPARSEC_VERSION=3
+  else
+    Build-Depends:  parsec == 2.*
+    CPP-Options:    -DPARSEC_VERSION=2
 
diff --git a/examples/echoServer.alms b/examples/echoServer.alms
--- a/examples/echoServer.alms
+++ b/examples/echoServer.alms
@@ -19,7 +19,7 @@
 
   let rec acceptLoop['t] (sock: 't socket) (f: string -> string)
                          (cap: 't listening) : unit =
-    let (Pack('s, clientsock, clientcap), cap) = accept sock cap in
+    let (('s, clientsock, clientcap), cap) = accept sock cap in
       putStrLn "Opened connection";
       (Thread.fork :> (unit -o unit) -> Thread.thread)
         (fun () -> handleClient clientsock f clientcap;
@@ -27,7 +27,7 @@
       acceptLoop sock f cap
 
   let serve (port: int) (f: string -> string) =
-    let Pack('t, sock, cap) = socket () in
+    let ('t, sock, cap) = socket () in
     let cap = bind sock port cap in
     let cap = listen sock cap in
       acceptLoop sock f cap
diff --git a/examples/ex03-poly-blame-error.alms b/examples/ex03-poly-blame-error.alms
--- a/examples/ex03-poly-blame-error.alms
+++ b/examples/ex03-poly-blame-error.alms
@@ -5,7 +5,7 @@
 
 let inc2 =
   fun y: int ->
-    let g = (ap :> all 'a 'b. ('a -o 'b) -> 'a -> 'b)
+    let g = (ap :> all 'a 'b. ('a -A> 'b) -> 'a -U> 'b)
             (fun z: int -> z + 1) in
       g (g y)   (* g is used twice here *)
 
diff --git a/examples/ex27-focusing-and-adoption.alms b/examples/ex27-focusing-and-adoption.alms
--- a/examples/ex27-focusing-and-adoption.alms
+++ b/examples/ex27-focusing-and-adoption.alms
@@ -23,12 +23,12 @@
            (n + 1, Cons (x, xs)))
         (0, Nil[`a]) xs
 
-let snoc[`a] (x: `a) | (xs: `a list) : `a list =
+let snoc[`a] (x: `a) |[a] (xs: `a list) : `a list =
   foldr (fun (x: `a) (xs: `a list) -> Cons (x, xs))
         (Cons (x, Nil[`a])) xs
 
 let revAppN =
-  let rec loop[`a] (n: int) (xs: `a list) | (acc: `a list)
+  let rec loop[`a] (n: int) (xs: `a list) |[a] (acc: `a list)
                     : `a list * `a list =
         match n with
         | 0 -> (acc, xs)
@@ -41,55 +41,54 @@
   let (_, acc) = revAppN (-1) xs Nil[`a] 
    in acc
 
-let swapN[`a] (ix: int) (y: `a) | (xs: `a list)
+let swapN[`a] (ix: int) (y: `a) |[a] (xs: `a list)
        : `a * `a list =
   let (Cons(x, xs), acc) = revAppN ix xs Nil[`a] in
   let (xs, _) = revAppN (-1) acc (Cons (y, xs)) in
     (x, xs)
 
-abstype ('t, `a) region qualifier A = Rgn of `a list
-    and ('t, `a) region1 qualifier A = Rgn1 of `a
-    and 't ptr qualifier U = Ptr of int
-with
-  let newRgn[`a] () =
-    Pack[ex 't. ('t, `a) region] (unit, Rgn[unit] (Nil[`a]))
-  let freeRgn[`a,'t] (_: ('t, `a) region) = ()
+module Region : sig
+  type ('t, `a) region : A
+  type ('t, `a) region1 : A
+  type 't ptr
 
-  let mallocIn[`a,'t] (Rgn xs: ('t, `a) region) | (a: `a)
-      : 't ptr * ('t, `a) region =
+  val newRgn    : unit -> ex 't. ('t,`a) region
+  val mallocIn  : ('t,`a) region -> `a -o 't ptr * ('t,`a) region
+  val swap      : ('t,`a) region -> 't ptr -o `a -o `a * ('t,`a) region
+  val malloc    : unit -> ex 't. ('t,unit) region1 * 't ptr
+  val free      : ('t,`a) region1 -> unit
+  val adopt     : ('t1,`a) region -> ('t2,`a) region1 -o 't2 ptr -o
+                  't1 ptr * ('t1,`a) region
+  val focus     : ('t,`a) region -> 't ptr -o
+                  ex 't1. ('t1,`a) region1 * 't1 ptr *
+                          (('t1,`a) region1 -o ('t,`a) region)
+end = struct
+  type ('t, `a) region = `a list
+  type ('t, `a) region1 = `a
+  type 't ptr = int
+
+  let newRgn () = Nil
+
+  let freeRgn _ = ()
+
+  let mallocIn (xs: `a list) (a: `a) =
     let (ix, xs) = length xs in
-      (Ptr['t] ix, Rgn['t] (snoc a xs))
-  let swap[`a,'t] (Rgn xs: ('t, `a) region) |
-                   (Ptr ix: 't ptr) (x: `a)
-                   : `a * ('t, `a) region =
+      (ix, snoc a xs)
+
+  let swap (xs: `a list) (ix: 't ptr) (x: `a) =
     let (y, xs) = swapN ix x xs in
-      (y, Rgn['t] xs)
+      (y, xs)
 
-  let malloc () =
-    Pack[ex 't. ('t, unit) region1 * 't ptr]
-        (unit, Rgn1[unit] (), Ptr[unit] 0)
-  let swap1[`a,`b,'t] (Rgn1 x: ('t, `a) region1) |
-                        (_: 't ptr) (y: `b)
-                        : `a * ('t, `b) region1 =
-    (x, Rgn1['t] y)
-  let free[`a, 't] (_: ('t, `a) region1) = ()
+  let malloc () = ((), 0)
 
-  let adopt[`a,'t1,'t2] (rgn: ('t1, `a) region) |
-                         (Rgn1 x: ('t2, `a) region1)
-                         (_: 't2 ptr)
-                         : 't1 ptr * ('t1, `a) region =
+  let swap1 (x: `a) _ (y: `b) = (x, y)
+
+  let free _ = ()
+
+  let adopt (rgn: `a list) (x: `a) _ =
     mallocIn rgn x
 
-  let focus[`a,'t]
-        (Rgn xs: ('t, `a) region) |
-        (Ptr ix: 't ptr)
-        : ex 't1. ('t1, `a) region1 * 't1 ptr *
-                  (('t1, `a) region1 -o ('t, `a) region) =
-     let (Cons (x, xs), acc) = revAppN ix xs Nil[`a] in
-       Pack[ex 't1. ('t1, `a) region1 * 't1 ptr *
-                    (('t1, `a) region1 -o ('t, `a) region)]
-           (unit, Rgn1[unit] x, Ptr[unit] 0,
-            fun (Rgn1 y: (unit, `a) region1) ->
-              let (xs, _) = revAppN (-1) acc (Cons (y, xs)) in
-                Rgn['t] xs)
+  let focus (xs: `a list) (ix: 't ptr) =
+     let (Cons (x, xs), acc) = revAppN ix xs Nil in
+       (x, 0, fun (y: `a) -> fst (revAppN (-1) acc (Cons (y, xs))))
 end
diff --git a/examples/ex28-focusing-and-adoption.alms b/examples/ex28-focusing-and-adoption.alms
--- a/examples/ex28-focusing-and-adoption.alms
+++ b/examples/ex28-focusing-and-adoption.alms
@@ -11,7 +11,7 @@
            (n + 1, Cons (x, xs)))
         (0, Nil[`a]) xs
 
-let snoc[`a] (x: `a) | (xs: `a list) : `a list =
+let snoc[`a] (x: `a) |[a] (xs: `a list) : `a list =
   foldr (fun (x: `a) (xs: `a list) -> Cons (x, xs))
         (Cons (x, Nil[`a])) xs
 
@@ -25,7 +25,7 @@
                | xs          -> (acc, xs)
   in loop
 
-let swapN[`a] (ix: int) (y: `a) | (xs: `a list)
+let swapN[`a] (ix: int) (y: `a) |[a] (xs: `a list)
        : `a * `a list =
   let (Cons(x, xs), acc) = revAppN ix xs Nil[`a] in
   let (xs, _) = revAppN (-1) acc (Cons (y, xs)) in
diff --git a/examples/ex63-popl-CapArray.alms b/examples/ex63-popl-CapArray.alms
--- a/examples/ex63-popl-CapArray.alms
+++ b/examples/ex63-popl-CapArray.alms
@@ -4,9 +4,9 @@
   type ('a,'b) array
   type 'b cap : A
 
-  val new : int → 'a → ∃'b. ('a,'b) array * 'b cap
+  val new : int → 'a → ∃'b. ('a,'b) array × 'b cap
   val set : ('a,'b) array → int → 'a → 'b cap → 'b cap
-  val get : ('a,'b) array → int → 'b cap → 'a * 'b cap
+  val get : ('a,'b) array → int → 'b cap → 'a × 'b cap
 
   val dirtyGet : ('a,'b) array → int → 'a
   val size     : ('a,'b) array → int
diff --git a/lib/libbasis.alms b/lib/libbasis.alms
--- a/lib/libbasis.alms
+++ b/lib/libbasis.alms
@@ -142,6 +142,7 @@
                | Some y -> Cons(y, xs')
                | None   -> xs')
             Nil
+            xs
 let revApp[`c] (xs : `c list) (ys : `c list) =
   let cons (x : `c) (acc : `c list) = Cons (x, acc) in
     foldl cons ys xs
diff --git a/src/Basis.hs b/src/Basis.hs
--- a/src/Basis.hs
+++ b/src/Basis.hs
@@ -113,9 +113,9 @@
       -= (read :: String -> Double),
 
     -- Strings
-    fun "explode"  -: [$ty| string -> int list |]
+    fun "explode"  -: [$ty| string -> char list |]
       -= map char2integer,
-    fun "implode"  -: [$ty| int list -> string |]
+    fun "implode"  -: [$ty| char list -> string |]
       -= map integer2char,
     fun "^" -: [$ty| string -> string -> string |]
       -= ((++) :: String -> String -> String),
@@ -131,9 +131,9 @@
       -= (print :: Value -> IO ()),
 
     -- I/O
-    fun "putChar"  -: [$ty| int -> unit |]
+    fun "putChar"  -: [$ty| char -> unit |]
       -= putChar . integer2char,
-    fun "getChar"  -: [$ty| unit -> int |]
+    fun "getChar"  -: [$ty| unit -> char |]
       -= \() -> fmap char2integer getChar,
     fun "flush"    -: [$ty| unit -> unit |]
       -= \() -> IO.hFlush IO.stdout,
@@ -168,7 +168,7 @@
       -= (\r -> do
            v <- readIORef (unRef r)
            return (r, v)),
-    fun "<-" -: [$ty| all `a. `a ref -> `a -o `a |]
+    fun "<-" -: [$ty| all `a. `a ref -> `a -> `a |]
       -= (\r v -> do
            atomicModifyIORef (unRef r) (\v' -> (v, v'))),
     fun "<-!" -: [$ty| all `a `b. `a aref ->
diff --git a/src/Basis/Channel/Haskell.hs b/src/Basis/Channel/Haskell.hs
--- a/src/Basis/Channel/Haskell.hs
+++ b/src/Basis/Channel/Haskell.hs
@@ -6,7 +6,7 @@
 -- 
 -- Maintainer  :  tov@ccs.neu.edu
 -- Stability   :  experimental
--- Portability :  somewhat portable?
+-- Portability :  GHC 6-7
 --
 -- This module provides synchronous channels.  Unlike the channels in
 -- 'Control.Concurrent.Chan', which are unbounded queues on which
@@ -45,7 +45,7 @@
   --   with another.  In particular, we can write code like this:
   --
   --   @
-  --   'Control.Exception.block' $ do
+  --   'Control.Exception.mask_' $ do
   --     msg <- 'readChan' c
   --     'writeIORef' r msg
   --   @
@@ -113,11 +113,13 @@
 ) where
 
 import Control.Concurrent.MVar hiding ( modifyMVar )
-import Control.Exception
 import Control.Monad
 import Data.IORef
 import System.IO.Unsafe ( unsafeInterleaveIO )
 
+import Control.Exception ( finally, onException )
+import Compat ( mask )
+
 ---
 --- Amortized O(1) queues
 ---
@@ -196,7 +198,7 @@
 getWriters (WQ q) = dequeue empty q
 
 clear :: IO a -> IORef (Maybe b) -> IO a
-clear io r = block $ io `finally` writeIORef r Nothing
+clear io r = mask $ \_ -> io `finally` writeIORef r Nothing
 
 -- | Make a new channel.
 newChan :: IO (Chan a)
@@ -355,7 +357,7 @@
           Just a  -> do
             r'       <- newIORef (Just a')
             confirm' <- newEmptyMVar
-            _        <- block $ do
+            _        <- mask $ \_ -> do
               putMVar confirm ()
               commit (WQ ((r', confirm') <| writers))
             return $ do
@@ -585,7 +587,7 @@
     loop []     e = return (e, Success (Success (), []))
     loop (a:as) e = do
       case getReaders e of
-        r :< readers -> block $ do
+        r :< readers -> mask $ \_ -> do
           maybereader <- readIORef r
           case maybereader of
             Just reader -> do
@@ -609,64 +611,49 @@
 --- Helpful MVar stuff
 ---
 
-saveBlock :: IO (IO a -> IO a)
-saveBlock  = do
-  b <- blocked
-  case b of
-    True  -> return block
-    False -> return unblock
-
 modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b
-modifyMVar m io = do
-  restore <- saveBlock
-  block $ do
-    a <- takeMVar m
-    (a',b) <- restore (io a)
-      `onException` putMVar m a
-    putMVar m a'
-    return b
+modifyMVar m io = mask $ \restore -> do
+  a <- takeMVar m
+  (a',b) <- restore (io a)
+    `onException` putMVar m a
+  putMVar m a'
+  return b
 
 -- Control.Concurrent.MVar doesn't have this, but it's pretty useful
 -- for implementing non-blocking operations.
 tryModifyMVar :: MVar a -> (a -> IO (a, TryResult b)) -> IO (TryResult b)
-tryModifyMVar m io = do
-  restore <- saveBlock
-  block $ do
-    maybea <- tryTakeMVar m
-    case maybea of
-      Just a -> do
-        (a',b) <- restore (io a)
-          `onException` putMVar m a
-        putMVar m a'
-        return b
-      Nothing ->
-        return WouldBlock
+tryModifyMVar m io = mask $ \restore -> do
+  maybea <- tryTakeMVar m
+  case maybea of
+    Just a -> do
+      (a',b) <- restore (io a)
+        `onException` putMVar m a
+      putMVar m a'
+      return b
+    Nothing ->
+      return WouldBlock
 
 transactMVar :: MVar a ->
                 (a -> (a -> IO ()) -> IO b) ->
                 IO b
-transactMVar m io = do
-  restore <- saveBlock
-  block $ do
-    a <- takeMVar m
-    r <- newIORef a
-    restore (io a (writeIORef r))
-      `finally` (readIORef r >>= putMVar m)
+transactMVar m io = mask $ \restore -> do
+  a <- takeMVar m
+  r <- newIORef a
+  restore (io a (writeIORef r))
+    `finally` (readIORef r >>= putMVar m)
 
 {-
 tryTransactMVar :: MVar a ->
                    (a -> (a -> IO ()) -> IO (TryResult b)) ->
                    IO (TryResult b)
-tryTransactMVar m io = do
-  restore <- saveBlock
-  block $ do
-    maybea <- tryTakeMVar m
-    case maybea of
-      Just a -> do
-        r <- newIORef a
-        restore (io a (writeIORef r))
-          `finally` (readIORef r >>= putMVar m)
-      Nothing ->
-        return WouldBlock
+tryTransactMVar m io = mask $ \restore -> do
+  maybea <- tryTakeMVar m
+  case maybea of
+    Just a -> do
+      r <- newIORef a
+      restore (io a (writeIORef r))
+        `finally` (readIORef r >>= putMVar m)
+    Nothing ->
+      return WouldBlock
 -}
 
diff --git a/src/Coercion.hs b/src/Coercion.hs
--- a/src/Coercion.hs
+++ b/src/Coercion.hs
@@ -87,7 +87,7 @@
                exBVar (lid "x")
 build b recs (view -> TyQu Exists tv t) (view -> TyQu Exists tv' t') = do
   let recs' = M.insert (tv, tv') Nothing (shadow [tv] [tv'] recs)
-  body <- build b recs' t t' >>! instContract
+  body <- instContract `liftM` build b recs' t t'
   let tv''  = freshTyVar tv (ftv (tv, tv'))
       tstx  = typeToStx' t
       tstx' = typeToStx' t'
diff --git a/src/Compat.hs b/src/Compat.hs
new file mode 100644
--- /dev/null
+++ b/src/Compat.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE
+      TemplateHaskell #-}
+-- | Compatibility layer for different GHC and library versions
+module Compat (
+  mask, newQuasi,
+) where
+
+import Language.Haskell.TH
+
+import Language.Haskell.TH.Quote
+import qualified Control.Exception
+
+mask :: ((IO a -> IO a) -> IO b) -> IO b
+mask  = $(do
+  let name s = mkName ("Control.Exception." ++ s)
+      var    = varE . name
+  recover
+    [| \action -> do
+         b <- $(var "blocked")
+         let restore = if b then $(var "block")
+                            else $(var "unblock")
+         $(var "block") (action restore) |]
+    (do reify (name "mask")
+        [| \action -> $(var "mask") (\restore -> action restore) |]))
+
+newQuasi :: String -> QuasiQuoter
+newQuasi name = $(do
+  let quasiQuoter = conE 'QuasiQuoter
+      arity (AppT (AppT ArrowT _) t') = 1 + arity t'
+      arity _                         = 0 :: Int
+  DataConI _ ty _ _ <- reify 'QuasiQuoter
+  [| let qf cat _ = fail $ "Quasiquoter ‘" ++ name ++
+                           "’ does not support " ++ cat
+         q2       = $quasiQuoter (qf "expressions") (qf "patterns") in
+       $(if arity ty == 4
+           then [| q2 (qf "types") (qf "declarations") |]
+           else [| q2 |]) |])
+
+
diff --git a/src/ErrorST.hs b/src/ErrorST.hs
--- a/src/ErrorST.hs
+++ b/src/ErrorST.hs
@@ -51,9 +51,10 @@
         liftST_ newUndo
         unST (handler e)
 
-runST :: Error e => (forall s. ST s e a) -> Either e a
+runST :: (Error e, MonadError e m) => (forall s. ST s e a) -> m a
 runST block =
-  Super.runST (evalStateT (runErrorT (unST (transaction block))) (return ()))
+  either throwError return $
+    Super.runST (evalStateT (runErrorT (unST (transaction block))) (return ()))
 
 -- | Run something directly in the underlying ST monad
 liftST :: Error e => Super.ST s a -> ST s e a
diff --git a/src/Lexer.hs b/src/Lexer.hs
--- a/src/Lexer.hs
+++ b/src/Lexer.hs
@@ -11,7 +11,7 @@
   lolli, arrow, funbraces,
   lambda, forall, exists, mu,
   qualbox,
-  qualU, qualA,
+  qualU, qualA, qdisj, qconj,
   opP,
 
   -- * Token parsers from Parsec
@@ -23,6 +23,7 @@
 ) where
 
 import Prec
+import Util
 
 import Data.Char
 import Text.ParserCombinators.Parsec
@@ -182,16 +183,25 @@
 arrow           :: T.TokenEnd st => CharParser st ()
 arrow            = reservedOp "->" <|> reservedOp "→"
 
--- | The left part of the $-[_]>$ operator
+-- | The left part of the $-_>$ operator
 funbraceLeft    :: T.TokenEnd st => CharParser st ()
-funbraceLeft     = try (symbol "-[") >> return ()
+funbraceLeft     = try (symbol "-") >> return ()
 
--- | The right part of the $-[_]>$ operator
+-- | The right part of the $-_>$ operator
 funbraceRight   :: T.TokenEnd st => CharParser st ()
-funbraceRight    = try (symbol "]>") >> return ()
+funbraceRight    = try (symbol ">") >> return ()
 
+-- | The left part of the $-[_]>$ operator
+oldFunbraceLeft    :: T.TokenEnd st => CharParser st ()
+oldFunbraceLeft     = try (symbol "-[") >> return ()
+
+-- | The right part of the $-[_]>$ operator
+oldFunbraceRight   :: T.TokenEnd st => CharParser st ()
+oldFunbraceRight    = try (symbol "]>") >> return ()
+
 funbraces       :: T.TokenEnd st => CharParser st a -> CharParser st a
-funbraces        = between funbraceLeft funbraceRight
+funbraces        = liftM2 (<|>) (between oldFunbraceLeft oldFunbraceRight)
+                                (between funbraceLeft funbraceRight)
 
 -- | The left part of the $|[_]$ annotation
 qualboxLeft     :: T.TokenEnd st => CharParser st ()
@@ -242,6 +252,14 @@
 -- | Qualifier @A@ (not reserved)
 qualA    :: T.TokenEnd st => CharParser st ()
 qualA     = reserved "A"
+
+-- | Infix operator for qualifier disjunction
+qdisj           :: T.TokenEnd st => CharParser st ()
+qdisj            = reservedOp "," <|> reservedOp "\\/" <|> reservedOp "⋁"
+
+-- | Infix operator for qualifier conjunction
+qconj           :: T.TokenEnd st => CharParser st ()
+qconj            = reservedOp "/\\" <|> reservedOp "⋀"
 
 -- | Is the string an uppercase identifier?  (Special case: @true@ and
 --   @false@ are consider uppercase.)
diff --git a/src/Makefile b/src/Makefile
--- a/src/Makefile
+++ b/src/Makefile
@@ -9,8 +9,10 @@
               Message/*.hs Meta/*.hs
 HSBOOT_SRC  = Syntax/*.hs-boot
 
-HCOPTS  = -W -Wall -O0 $(EDITING) $(NOWARN)
+HCOPTS  = -W -Wall -O0 $(EDITING) $(PARSEC) $(NOWARN) $(IMPARR)
 EDITING = -DUSE_READLINE=System.Console.Editline.Readline
+PARSEC  = -DPARSEC_VERSION=3
+# IMPARR  = -DANNOTATION_PRINTING_RULE=Rule0
 NOWARN  = -fno-warn-unused-do-bind -fno-warn-orphans
 
 $(EXE) $(EXE)-%: $(SRC)
@@ -18,6 +20,7 @@
 
 $(EXE)-%:     GHC     = ghc-$*
 $(EXE)-6.8.%: EDITING = -DUSE_READLINE=System.Console.Readline
+$(EXE)-6.%:   PARSEC  = -DPARSEC_VERSION=2
 
 clean:
 	$(RM) $(HS_SRC:.hs=.hi) $(HS_SRC:.hs=.o)
diff --git a/src/Message/Quasi.hs b/src/Message/Quasi.hs
--- a/src/Message/Quasi.hs
+++ b/src/Message/Quasi.hs
@@ -12,6 +12,7 @@
 
 import Message.AST
 import Message.Parser
+import Meta.THHelpers
 import PprClass
 import Util
 
@@ -21,9 +22,7 @@
 import Language.Haskell.TH.Syntax (lift)
 
 msg :: QuasiQuoter
-msg  = QuasiQuoter qexp qpat where
-  qexp s = parseMessageQ s >>= msgAstToExpQ
-  qpat _ = fail "Quasiquoter ‘msg’ does not support patterns"
+msg  = (newQuasi "msg") { quoteExp = parseMessageQ >=> msgAstToExpQ }
 
 msgAstToExpQ :: Message d -> ExpQ
 msgAstToExpQ msg0 = do
diff --git a/src/Meta/Quasi.hs b/src/Meta/Quasi.hs
--- a/src/Meta/Quasi.hs
+++ b/src/Meta/Quasi.hs
@@ -73,29 +73,31 @@
 pa, ty, ex, dc, me, prQ, tdQ, atQ, caQ, bnQ, qeQ, tpQ, seQ, sgQ
   :: QuasiQuoter
 
-ex  = mkQuasi parseExpr
-dc  = mkQuasi parseDecl
-ty  = mkQuasi parseType
-me  = mkQuasi parseModExp
-pa  = mkQuasi parsePatt
-prQ = mkQuasi parseProg
-tdQ = mkQuasi parseTyDec
-atQ = mkQuasi parseAbsTy
-caQ = mkQuasi parseCaseAlt
-bnQ = mkQuasi parseBinding
-qeQ = mkQuasi parseQExp
-tpQ = mkQuasi parseTyPat
-seQ = mkQuasi parseSigExp
-sgQ = mkQuasi parseSigItem
+ex  = mkQuasi "ex" parseExpr
+dc  = mkQuasi "dc" parseDecl
+ty  = mkQuasi "ty" parseType
+me  = mkQuasi "me" parseModExp
+pa  = mkQuasi "pa" parsePatt
+prQ = mkQuasi "prQ" parseProg
+tdQ = mkQuasi "tdQ" parseTyDec
+atQ = mkQuasi "atQ" parseAbsTy
+caQ = mkQuasi "caQ" parseCaseAlt
+bnQ = mkQuasi "bnQ" parseBinding
+qeQ = mkQuasi "qeQ" parseQExp
+tpQ = mkQuasi "tpQ" parseTyPat
+seQ = mkQuasi "seQ" parseSigExp
+sgQ = mkQuasi "sgQ" parseSigItem
 
 mkQuasi :: forall stx note.
            (Data (note Raw), Data (stx Raw),
             LocAst (N (note Raw) (stx Raw)),
             Data (note Renamed), Data (stx Renamed),
             LocAst (N (note Renamed) (stx Renamed))) =>
+           String ->
            (forall i. Id i => P (N (note i) (stx i))) ->
            QuasiQuoter
-mkQuasi parser = QuasiQuoter qast qast where
+mkQuasi name parser = (newQuasi name) { quoteExp = qast, quotePat = qast }
+  where
   qast s =
     join $
       parseQuasi s $ \iflag lflag ->
diff --git a/src/Meta/THHelpers.hs b/src/Meta/THHelpers.hs
--- a/src/Meta/THHelpers.hs
+++ b/src/Meta/THHelpers.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE
+      CPP,
       DeriveDataTypeable,
       RankNTypes,
       TemplateHaskell,
@@ -6,6 +7,8 @@
 module Meta.THHelpers (
   -- * Simplified TH quasiquote
   th,
+  -- * For initializing quasiquoters
+  newQuasi,
   -- * Generic expression/pattern AST construction
   ToSyntax(..),
   -- * Miscellany
@@ -14,6 +17,7 @@
 
 import Lexer (lid, uid)
 import Util
+import Compat (newQuasi)
 
 import Data.Generics (Typeable, Data, everything, mkQ)
 import Language.Haskell.TH
@@ -36,9 +40,7 @@
 
 -- | The quasiquoter for building TH expressions
 th :: QuasiQuoter
-th = QuasiQuoter qexp qpat where
-  qexp s = parseHs s >>= hsToExpQ
-  qpat _ = fail "Quasiquoter `hs' does not support patterns"
+th  = (newQuasi "th") { quoteExp = parseHs >=> hsToExpQ }
 
 -- | Don't allow HsOr to the left of HsApp:
 hsApp :: HsAst -> HsAst -> HsAst
diff --git a/src/Parser.hs b/src/Parser.hs
--- a/src/Parser.hs
+++ b/src/Parser.hs
@@ -397,7 +397,7 @@
                (choice
                 [ tyArr <$ arrow,
                   tyLol <$ lolli,
-                  funbraces (tyFun <$> qExpp),
+                  funbraces (tyFun <$> (antiblep <|> Just <$> qExpp)),
                   tybinopp (Right precArr) ])
                (typepP precStart)
     | p == precSemi
@@ -801,8 +801,8 @@
 
 qExpp :: Id i => P (QExp i)
 qExpp  = "qualifier expression" @@ qexp where
-  qexp  = addLoc $ qeDisj <$> sepBy1 qterm (reservedOp "\\/")
-  qterm = addLoc $ qeConj <$> sepBy1 qfact (reservedOp "/\\")
+  qexp  = addLoc $ qeDisj <$> sepBy1 qterm qdisj
+  qterm = addLoc $ qeConj <$> sepBy1 qfact qconj
   qfact = addLoc $ parens qexp <|> qatom
   qatom = addLoc $
           qeLit Qu <$  qualU
@@ -1007,24 +1007,24 @@
 -- Zero or more of (pat:typ, ...), (), or tyvar, recognizing '|'
 -- to introduce affine arrows
 afargsp :: Id i => P (Bool, Type i -> Type i, Expr i -> Expr i)
-afargsp = loop tyArr where
-  loop arrcon0 = do
-    arrcon <- option arrcon0 $ choice
-      [ tyFun <$> qualbox qExpp,
+afargsp = choice
+  [ do (tvt, tve) <- tyargp
+       (b, ft, fe) <- afargsp
+       return (b, tvt . ft, tve . fe),
+    do arrcon <- arrconp
+       (b, ft, fe) <- vargp arrcon
+       if b
+          then return (b, ft, fe)
+          else do
+            (b', fts, fes) <- afargsp
+            return (b', ft . fts, fe . fes),
+    return (False, id, id) ]
+  where
+    arrconp = option tyArr $ choice
+      [ tyFun . Just <$> qualbox qExpp,
         do
           reservedOp "|"
           return tyLol ]
-    choice
-      [ do (tvt, tve) <- tyargp
-           (b, ft, fe) <- loop arrcon
-           return (b, tvt . ft, tve . fe),
-        do (b, ft, fe) <- vargp arrcon
-           if b
-              then return (b, ft, fe)
-              else do
-                (b', fts, fes) <- loop arrcon
-                return (b', ft . fts, fe . fes),
-        return (False, id, id) ]
 
 -- One or more of (pat:typ, ...), (), tyvar
 argsp1 :: Id i => P (Bool, Expr i -> Expr i)
diff --git a/src/Ppr.hs b/src/Ppr.hs
--- a/src/Ppr.hs
+++ b/src/Ppr.hs
@@ -75,13 +75,13 @@
 
 instance Ppr (Type i) where
   -- pprPrec p (TyFun q t1 t2)
+  ppr [$ty| $t1 -> $t2 |]
+            = prec precArr $
+              sep [ ppr1 t1, text "->" <+> pprRight t2 ]
   ppr [$ty| $t1 -[$q]> $t2 |]
             = prec precArr $
               sep [ ppr1 t1,
-                    pprArr (view q) <+> pprRight t2 ]
-    where pprArr (QeLit Qu) = text "->"
-          pprArr (QeLit Qa) = text "-o"
-          pprArr _          = text "-[" <> ppr0 q <> text "]>"
+                    text "-" <> ppr0 q <> text ">" <+> pprRight t2 ]
   ppr t@[$ty| ($list:ts) $qlid:n |]
     | Just doc <- pprInfix unfoldType t
                     = doc
@@ -131,8 +131,8 @@
     []    -> ppr Qu
     [qe]  -> ppr qe
     _     -> prec precPlus $
-               fsep $
-                 intersperse (text "\\/") $
+               hcat $
+                 intersperse (text ",") $
                    map ppr1 qes
   ppr [$qeQ| $qconj:qes |] = case qes of
     []    -> ppr Qa
diff --git a/src/Rename.hs b/src/Rename.hs
--- a/src/Rename.hs
+++ b/src/Rename.hs
@@ -80,6 +80,10 @@
   m >>= k = R (unR m >>= unR . k)
   fail    = renameError . [$msg| $words:1 |]
 
+instance Applicative Renaming where
+  pure  = return
+  (<*>) = ap
+
 instance MonadWriter Module Renaming where
   listen = R . listen . unR
   tell   = R . tell
@@ -755,11 +759,11 @@
   [$ty| '$tv |] -> do
     tv' <- getTyvar tv
     return [$ty|+ '$tv' |]
-  [$ty| $t1 -[$qe]> $t2 |] -> do
-    t1' <- renameType t1
-    qe' <- renameQExp qe
-    t2' <- renameType t2
-    return [$ty|+ $t1' -[$qe']> $t2' |]
+  [$ty| $t1 -[$opt:mqe]> $t2 |] -> do
+    t1'  <- renameType t1
+    mqe' <- gmapM renameQExp mqe
+    t2'  <- renameType t2
+    return [$ty|+ $t1' -[$opt:mqe']> $t2' |]
   [$ty| $quant:u '$tv. $t |] -> do
     (tv', md) <- steal $ bindTyvar tv
     t' <- inModule md $ renameType t
@@ -902,8 +906,8 @@
   ftvList t0 = case t0 of
     [$ty| ($list:ts) $qlid:_ |] -> ftvList ts
     [$ty| '$tv |]               -> [tv]
-    [$ty| $t1 -[$qe]> $t2 |]    ->
-      ftvList t1 `List.union` ftvList qe `List.union` ftvList t2
+    [$ty| $t1 -[$opt:mqe]> $t2 |]    ->
+      ftvList t1 `List.union` ftvList mqe `List.union` ftvList t2
     [$ty| $quant:_ '$tv. $t |]  -> List.delete tv (ftvList t)
     [$ty| mu '$tv. $t |]        -> List.delete tv (ftvList t)
     [$ty| $anti:a |] -> $antierror
diff --git a/src/Statics.hs b/src/Statics.hs
--- a/src/Statics.hs
+++ b/src/Statics.hs
@@ -3,7 +3,6 @@
       DeriveDataTypeable,
       FlexibleContexts,
       FlexibleInstances,
-      GeneralizedNewtypeDeriving,
       ImplicitParams,
       MultiParamTypeClasses,
       ParallelListComp,
@@ -51,6 +50,7 @@
 
 import Control.Monad.RWS    as RWS
 import Control.Monad.Error  as Error
+import System.IO (hPutStrLn, stderr)
 import Data.Data (Typeable, Data)
 import Data.Generics (everywhere, mkT)
 import Data.List (transpose, tails)
@@ -63,6 +63,8 @@
 pP a b = unsafePerformIO (print a) `seq` b
 pM :: (Show a, Monad m) => a -> m ()
 pM a = if pP a True then return () else fail "wibble"
+ioM :: Monad m => IO a -> m ()
+ioM a = if unsafePerformIO a `seq` True then return () else fail "wibble"
 
 -- The kind of names we're using.
 type R = Renamed
@@ -198,13 +200,16 @@
 --   and keeps track of a gensym counter (currently unused).
 newtype TC m a = TC {
   unTC :: RWST Context Module Int (ErrorT AlmsException m) a
-} deriving Functor
+}
 
 instance Monad m => Monad (TC m) where
   return  = TC . return
   m >>= k = TC (unTC m >>= unTC . k)
   fail    = let ?loc = bogus in typeError . [$msg| $words:1 |]
 
+instance Monad m => Functor (TC m) where
+  fmap = liftM
+
 instance Monad m => Applicative (TC m) where
   pure  = return
   (<*>) = ap
@@ -395,17 +400,21 @@
 -- | Check type for closed-ness and and defined-ness, and add info
 tcType :: (?loc :: Loc, Monad m) =>
           Syntax.Type R -> TC m Type
-tcType = tc where
-  tc :: Monad m => Syntax.Type R -> TC m Type
-  tc [$ty| '$tv |] = do
+tcType stxtype0 = do
+  t <- tc iaeInit stxtype0
+  return t
+  where
+  tc :: Monad m => CurrentImpArrRule -> Syntax.Type R -> TC m Type
+  tc iae [$ty| '$tv |] = do
     return (TyVar tv)
-  tc [$ty| $t1 -[$q]> $t2 |] = do
-    TyFun <$> qInterpretM q
-          <*> tcType t1
-          <*> tcType t2
-  tc [$ty| ($list:ts) $qlid:n |] = do
-    ts'  <- mapM tc ts
+  tc iae [$ty| $t1 -[$opt:mq]> $t2 |] = do
+    qd  <- iaeInterpret iae mq
+    t1' <- tc (iaeLeft iae) t1
+    t2' <- tc (iaeRight iae qd t1') t2
+    return (TyFun qd t1' t2')
+  tc iae [$ty| ($list:ts) $qlid:n |] = do
     tc'  <- find n
+    ts'  <- zipWithM (tc . iaeUnder iae) (tcArity tc') ts
     checkLength (length (tcArity tc'))
     checkBound (tcBounds tc') ts'
     return (tyApp tc' ts')
@@ -422,14 +431,14 @@
                  qualifiers than permitted: |]
           ([$msg| $1 |] (map (qRepresent . qualifier) ts'))
           [$msg| $quals (or less) |]
-  tc [$ty| $quant:u '$tv . $t |] =
-    TyQu u tv <$> tc t
-  tc t0@[$ty| mu '$tv . $t |] = do
+  tc iae [$ty| $quant:u '$tv . $t |] =
+    TyQu u tv <$> tc iae t
+  tc iae t0@[$ty| mu '$tv . $t |] = do
     case unfoldTyMu t of
       (_, N _ (Syntax.TyVar tv')) | tv == tv' ->
         typeError [$msg| Recursive type is not contractive: $t0 |]
       _ -> return ()
-    t' <- tc t
+    t' <- tc iae t
     let actqual = qualConst t'
         expqual = tvqual tv
     tassert (actqual == expqual)
@@ -441,7 +450,7 @@
                 <dt>in type:            <dd>$5:t0
               </dl> |]
     return (TyMu tv t')
-  tc [$ty| $anti:a |] = $antifail
+  tc _ [$ty| $anti:a |] = $antifail
 
 -- | Type check an A expression
 tcExpr :: Monad m => Expr R -> TC m (Type, Expr R)
@@ -465,8 +474,8 @@
         checkSharing "match or let" (caexpr ca) md
         return (ti, caClause xi' ei' <<@ note)
       tr <- foldM (\ti' ti -> case ti' \/? ti of
-        Just tr' -> return tr'
-        Nothing  -> typeError [$msg|
+        Right tr'          -> return tr'
+        Left (_ :: String) -> typeError [$msg|
           Mismatch in branches of match or let.  Cannot unify:
           <ul>
             <li>$ti
@@ -842,16 +851,15 @@
         if or changed
           then loop md'
           else return (tcs, md')
-   in do
-     (tcs, md') <- loop stub
-     forM_ tcs $ \tc -> do
-       case tcNext tc of
-         Nothing      -> return ()
-         Just clauses -> forM_ clauses $ \(tps, rhs) ->
-           tassert (rhs /= tyPatToType (TpApp tc {tcNext = Nothing} tps)) $
-             "Recursive type synonym is not contractive:" !:: tc
-     tell (replaceTyCons tcs md')
-     return tds0
+  (tcs, md') <- loop stub
+  forM_ tcs $ \tc -> do
+    case tcNext tc of
+      Nothing      -> return ()
+      Just clauses -> forM_ clauses $ \(tps, rhs) -> do
+        tassert (rhs /= tyPatToType (TpApp tc {tcNext = Nothing} tps)) $
+          "Recursive type synonym is not contractive:" !:: tc
+  tell (replaceTyCons tcs md')
+  return tds0
   where
     allocStub name params = do
       ix <- newIndex
@@ -1001,7 +1009,7 @@
     _      -> S.empty
   `S.union` S.unions (map tyConsOfType ts)
 tyConsOfType [$ty| '$_ |]              = S.empty
-tyConsOfType [$ty| $t1 -[$_]> $t2 |]   =
+tyConsOfType [$ty| $t1 -[$opt:_]> $t2 |]   =
   tyConsOfType t1 `S.union` tyConsOfType t2
 tyConsOfType [$ty| $quant:_ '$_. $t |] = tyConsOfType t
 tyConsOfType [$ty| mu '$_. $t |]       = tyConsOfType t
@@ -1138,6 +1146,7 @@
                </dl> |] (qRepresent (qualifier te))
       return te
   x' <- tcPatt t' x
+  -- ioM (hPutStrLn stderr (show te))
   return (x', Just (typeToStx' t'), e')
 
 -- | Run a computation in the context of a module open declaration
diff --git a/src/Syntax/Decl.hs-boot b/src/Syntax/Decl.hs-boot
--- a/src/Syntax/Decl.hs-boot
+++ b/src/Syntax/Decl.hs-boot
@@ -10,12 +10,14 @@
 import Syntax.Notable
 import Syntax.Ident (Id, Fv, Dv)
 
-import Data.Data (Data)
+import Data.Data (Data, Typeable1)
 
 data DeclNote i
 data Decl' i
 type Decl i = N (DeclNote i) (Decl' i)
 
+instance Typeable1 DeclNote
+instance Typeable1 Decl'
 instance Id i => Data (DeclNote i)
 instance Id i => Data (Decl' i)
 instance Locatable (DeclNote i)
diff --git a/src/Syntax/Ident.hs-boot b/src/Syntax/Ident.hs-boot
--- a/src/Syntax/Ident.hs-boot
+++ b/src/Syntax/Ident.hs-boot
@@ -1,10 +1,12 @@
 module Syntax.Ident where
 
-import Data.Data (Data)
+import Data.Data (Data, Typeable1)
 
 class Id i
 
 data TyVar i
 
+instance Typeable1 TyVar
 instance Data i => Data (TyVar i)
 instance Id i   => Ord (TyVar i)
+instance Id i   => Eq (TyVar i)
diff --git a/src/Syntax/POClass.hs b/src/Syntax/POClass.hs
--- a/src/Syntax/POClass.hs
+++ b/src/Syntax/POClass.hs
@@ -5,6 +5,7 @@
 
 import Util
 
+import Control.Monad.Error
 import qualified Data.Set as S
 
 -- | Partial orders.
@@ -19,29 +20,29 @@
   -- | Takes a boolean parameter, and does join if true
   --   and meet if false.  This sometimes allows us to exploit duality
   --   to define both at once.
-  ifMJ :: Monad m => Bool -> a -> a -> m a
+  ifMJ :: (Error e, MonadError e m) => Bool -> a -> a -> m a
   ifMJ True  x y = return (x \/ y)
   ifMJ False x y = return (x /\ y)
 
   -- | Partial join returns in a monad, in case join DNE
-  (\/?) :: Monad m => a -> a -> m a
+  (\/?) :: (Error e, MonadError e m) => a -> a -> m a
   (\/?)  = ifMJ True
 
   -- | Partial meet returns in a monad, in case meet DNE
-  (/\?) :: Monad m => a -> a -> m a
+  (/\?) :: (Error e, MonadError e m) => a -> a -> m a
   (/\?)  = ifMJ False
 
   -- | Total join
   (\/)  :: a -> a -> a
   -- | Total meet
   (/\)  :: a -> a -> a
-  x \/ y = fromJust (x \/? y)
-  x /\ y = fromJust (x /\? y)
+  x \/ y = either error id (x \/? y)
+  x /\ y = either error id (x /\? y)
 
   -- | The order relation (derived)
   (<:)  :: a -> a -> Bool
-  x <: y = Just x == (x /\? y)
-        || Just y == (x \/? y)
+  x <: y = either (const False :: String -> Bool) ((==) x) (x /\? y)
+        || either (const False :: String -> Bool) ((==) y) (x \/? y)
 
   -- | The complement of the order relation (derived)
   (/<:) :: a -> a -> Bool
@@ -54,13 +55,13 @@
 bigVee :: (Bounded a, PO a) => [a] -> a
 bigVee  = foldr (\/) minBound
 
-bigVeeM :: (Monad m, Bounded a, PO a) => [a] -> m a
+bigVeeM :: (Error e, MonadError e m, Bounded a, PO a) => [a] -> m a
 bigVeeM  = foldrM (\/?) minBound
 
 bigWedge :: (Bounded a, PO a) => [a] -> a
 bigWedge  = foldr (/\) maxBound
 
-bigWedgeM :: (Monad m, Bounded a, PO a) => [a] -> m a
+bigWedgeM :: (Error e, MonadError e m, Bounded a, PO a) => [a] -> m a
 bigWedgeM  = foldrM (/\?) maxBound
 
 instance Ord a => PO (S.Set a) where
@@ -72,7 +73,7 @@
   Nothing \/? b       = return b
   a       \/? Nothing = return a
 
-  Just a  /\? Just b  = return (a /\? b)
+  Just a  /\? Just b  = liftM Just (a /\? b)
   Nothing /\? _       = return Nothing
   _       /\? Nothing = return Nothing
 
diff --git a/src/Syntax/Type.hs b/src/Syntax/Type.hs
--- a/src/Syntax/Type.hs
+++ b/src/Syntax/Type.hs
@@ -42,7 +42,7 @@
 data Type' i
   = TyApp  (QLid i) [Type i]
   | TyVar  (TyVar i)
-  | TyFun  (QExp i) (Type i) (Type i)
+  | TyFun  (Maybe (QExp i)) (Type i) (Type i)
   | TyQu   Quant (TyVar i) (Type i)
   | TyMu   (TyVar i) (Type i)
   | TyAnti Anti
@@ -99,10 +99,10 @@
 tyAf           = tyNulOp "A"
 
 tyArr         :: Type i -> Type i -> Type i
-tyArr          = tyFun minBound
+tyArr          = tyFun Nothing
 
 tyLol         :: Type i -> Type i -> Type i
-tyLol          = tyFun maxBound
+tyLol          = tyFun (Just maxBound)
 
 infixr 8 `tyArr`, `tyLol`
 
@@ -115,8 +115,10 @@
       putStrLn $ show n ++ " {"
       mapM_ (dumpType (i + 2)) ps
       putStrLn (replicate i ' ' ++ "}")
-    TyFun q dom cod -> do
-      putStrLn $ "-[" ++ maybe "ANTI" show (qInterpretM q) ++ "]> {"
+    TyFun mq dom cod -> do
+      putStrLn $ case mq of
+        Just q  -> "-[" ++ maybe "ANTI" show (qInterpretM q) ++ "]> {"
+        Nothing -> "-> {"
       dumpType (i + 2) dom
       dumpType (i + 2) cod
       putStrLn (replicate i ' ' ++ "}")
diff --git a/src/Type.hs b/src/Type.hs
--- a/src/Type.hs
+++ b/src/Type.hs
@@ -1,6 +1,7 @@
 -- | The internal representation of types, created by the type checker
 --   from the syntactic types in 'Syntax.Type'.
 {-# LANGUAGE
+      CPP,
       DeriveDataTypeable,
       DeriveFunctor,
       ViewPatterns,
@@ -47,6 +48,8 @@
   vtAppTc, isBotType,
   -- ** Unfolds
   vtFuns, vtQus,
+  -- * Implicit arrow annotations
+  CurrentImpArrRule, ImpArrRule(..),
   -- * Re-exports
   module Syntax.Ident,
   module Syntax.Kind,
@@ -692,24 +695,58 @@
 typeToStx'  = typeToStx tyNames0
 
 -- | Represent a type value as a syntactic type, for printing; renames
---   so that scope is apparent, since internal renaming may result int
+--   so that scope is apparent, since internal renaming may result in
 --   different identifiers that print the same
 typeToStx :: TyNames -> Type -> Stx.Type Renamed
-typeToStx f = loop (S.empty, M.empty) where
-  loop ren t0 = case t0 of
+typeToStx tns = typeToStxRule tns (iaeInit :: CurrentImpArrPrintingRule)
+
+#ifdef ANNOTATION_RULE
+type CurrentImpArrRule = ANNOTATION_RULE
+#else
+type CurrentImpArrRule = Rule4
+#endif
+
+#ifdef ANNOTATION_PRINTING_RULE
+type CurrentImpArrPrintingRule = ANNOTATION_PRINTING_RULE
+#else
+type CurrentImpArrPrintingRule = CurrentImpArrRule
+#endif
+
+class ImpArrRule a where
+  iaeInit      :: a
+  iaeLeft      :: a -> a
+  iaeRight     :: a -> QDen (TyVar Renamed) -> Type -> a
+  iaeImplied   :: a -> QDen (TyVar Renamed)
+  iaeInterpret :: Monad m =>
+                  a -> Maybe (QExp Renamed) -> m (QDen (TyVar Renamed))
+  iaeRepresent :: a -> QDen (TyVar Renamed) -> Maybe (QExp Renamed)
+  iaeUnder     :: a -> Variance -> a
+  --
+  iaeLeft _           = iaeInit
+  iaeRight iae _ _    = iae
+  iaeImplied _        = minBound
+  iaeInterpret iae    = maybe (return (iaeImplied iae)) qInterpretM
+  iaeRepresent iae actual
+    | actual == iaeImplied iae = Nothing
+    | otherwise                = Just (qRepresent actual)
+  iaeUnder _ _        = iaeInit
+
+-- | Turns annotated arrows into implicit arrows where possible
+typeToStxRule :: ImpArrRule iae => TyNames -> iae -> Type -> Stx.Type Renamed
+typeToStxRule f iae0 = loop (S.empty, M.empty) iae0 where
+  loop ren iae t0 = case t0 of
     TyVar tv      -> Stx.tyVar (maybe tv id (M.lookup tv (snd ren)))
-    TyFun q t1 t2 -> Stx.tyFun (qRepresent q) (loop ren t1) (loop ren t2)
-    TyApp tc ts _ -> Stx.tyApp (bestName f tc) (map (loop ren) ts)
-    {-
-        (fmap (\ql -> lid ("[" ++ show (tcId tc) ++ "]" ++ unLid ql))
-              (tcName tc)) 
-        (map (loop ren) ts)
-    -}
-    TyQu qu tv t1 -> Stx.tyQu qu tv' (loop ren' t1)
+    TyFun q t1 t2 -> Stx.tyFun (iaeRepresent iae q)
+                               (loop ren (iaeLeft iae) t1)
+                               (loop ren (iaeRight iae q t1) t2)
+    TyApp tc ts _ -> Stx.tyApp
+                       (bestName f tc)
+                       (zipWith (loop ren . iaeUnder iae) (tcArity tc) ts)
+    TyQu qu tv t1 -> Stx.tyQu qu tv' (loop ren' iae t1)
       where (tv', ren') = fresh tv ren
-    TyMu tv t1    -> Stx.tyMu tv' (loop ren' t1)
+    TyMu tv t1    -> Stx.tyMu tv' (loop ren' iae t1)
       where (tv', ren') = fresh tv ren
-  fresh tv (seen, remap) = 
+  fresh tv (seen, remap) =
     let tv' = if S.member (unLid (tvname tv)) seen
                 then freshTyVar tv $
                        M.keysSet remap `S.union`
@@ -718,6 +755,72 @@
      in (tv', (S.insert (unLid (tvname tv')) seen,
                M.insert tv tv' remap))
 
+-- | Print all arrow annotations explicitly
+data Rule0 = Rule0
+
+instance ImpArrRule Rule0 where
+  iaeInit                = Rule0
+  iaeRepresent _ actual  = Just (qRepresent actual)
+
+-- | Annotation ‘U’ is implicit for unlabeled arrows.
+data Rule1 = Rule1
+
+instance ImpArrRule Rule1 where
+  iaeInit        = Rule1
+
+newtype Rule2 = Rule2 { unRule2 :: QDen (TyVar Renamed) }
+
+-- | Implicit annotation is lub of qualifiers of prior curried
+--   arguments.  Explicit annotations have no effect on subsequent
+--   arrows.
+instance ImpArrRule Rule2 where
+  iaeInit      = Rule2 minBound
+  iaeRight iae _ t = Rule2 (unRule2 iae \/ qualifier t)
+  iaeImplied   = unRule2
+
+-- | Like 'Rule2', but explicit annotations reset the qualifier to
+--   themselves for subsequent arrows.
+newtype Rule3 = Rule3 { unRule3 :: QDen (TyVar Renamed) }
+
+instance ImpArrRule Rule3 where
+  iaeInit      = Rule3 minBound
+  iaeRight iae actual t
+    | unRule3 iae == actual = Rule3 (unRule3 iae \/ qualifier t)
+    | otherwise             = Rule3 (actual \/ qualifier t)
+  iaeImplied   = unRule3
+
+-- | Like 'Rule3', but we arrow the implicit qualifer into covariant
+--   type constructors.
+newtype Rule4 = Rule4 { unRule4 :: QDen (TyVar Renamed) }
+
+instance ImpArrRule Rule4 where
+  iaeInit      = Rule4 minBound
+  iaeRight iae actual t
+    | unRule4 iae == actual = Rule4 (unRule4 iae \/ qualifier t)
+    | otherwise             = Rule4 (actual \/ qualifier t)
+  iaeImplied   = unRule4
+  iaeUnder iae Covariant    = iae
+  iaeUnder _   _            = iaeInit
+
+-- | Like 'Rule4', but we carry the implicit quantifier into ALL type
+--   constructors and only use it when we arrive at an arrow in a
+--   positive position wrt the surrounding arrow.
+data Rule5
+  = Rule5 {
+      unRule5 :: !(QDen (TyVar Renamed)),
+      r4Var   :: !Variance
+    }
+
+instance ImpArrRule Rule5 where
+  iaeInit      = Rule5 minBound 1
+  iaeRight iae actual t
+    | unRule5 iae == actual = Rule5 (unRule5 iae \/ qualifier t) 1
+    | otherwise             = Rule5 (actual \/ qualifier t) 1
+  iaeImplied iae
+    | r4Var iae == 1 = unRule5 iae
+    | otherwise      = minBound
+  iaeUnder iae var          = Rule5 (unRule5 iae) (var * r4Var iae)
+
 tyPatToStx' :: TyPat -> Stx.TyPat Renamed
 tyPatToStx'  = tyPatToStx tyNames0
 
@@ -957,6 +1060,7 @@
                             ppr (qRepresent
                                  (denumberQDen
                                   (map qDenOfTyVar tvs) (tcQual tc)))
+      ps :: Ppr a => [a] -> [Doc]
       ps tvs = [ ppr var <> pprPrec (precApp + 1) tv
                | tv <- tvs
                | var <- tcArity tc ]
diff --git a/src/TypeRel.hs b/src/TypeRel.hs
--- a/src/TypeRel.hs
+++ b/src/TypeRel.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE
+      FlexibleContexts,
       GeneralizedNewtypeDeriving,
       ParallelListComp,
       PatternGuards,
@@ -27,6 +28,7 @@
 import Viewable
 
 import qualified Control.Monad.Reader as CMR
+import Control.Monad.Error
 import Data.Generics (Data, everywhere, mkT, extT)
 import Data.Monoid
 import qualified Data.Map as M
@@ -171,25 +173,21 @@
 env1 = Field tcsEnv1 (\tcs e -> tcs { tcsEnv1 = e })
 env2 = Field tcsEnv2 (\tcs e -> tcs { tcsEnv2 = e })
 
-lift :: (CMR.MonadTrans t, Monad m) => m a -> t m a
-lift  = CMR.lift
-
-runUT  :: forall s a m. Monad m =>
+runUT  :: forall s a m. MonadError String m =>
           (forall t. UT s t a) -> S.Set TyVarR -> m a
 runUT m set =
-  either fail return $
-    runST $ do
-      seen   <- newTransSTRef M.empty
-      supply <- newSTRef [ f | f <- tvalphabet
-                         , f Qu `S.notMember` set
-                         , f Qa `S.notMember` set ]
-      CMR.runReaderT m TCS {
-        tcsSeen   = seen,
-        tcsSupply = supply,
-        tcsLevel  = 1,
-        tcsEnv1   = M.empty,
-        tcsEnv2   = M.empty
-      }
+  runST $ do
+    seen   <- newTransSTRef M.empty
+    supply <- newSTRef [ f | f <- tvalphabet
+                       , f Qu `S.notMember` set
+                       , f Qa `S.notMember` set ]
+    CMR.runReaderT m TCS {
+      tcsSeen   = seen,
+      tcsSupply = supply,
+      tcsLevel  = 1,
+      tcsEnv1   = M.empty,
+      tcsEnv2   = M.empty
+    }
 
 getVar :: TyVarR -> Field s t -> UT s t (Maybe (UVar t))
 getVar tv field = CMR.asks (M.lookup tv . get field)
@@ -221,7 +219,7 @@
             if isBotType lower
               then if upper == tyUn || upper == tyAf then lower else upper
               else lower
-          else fail $
+          else throwError $
             "Unification cannot solve:\n" ++
             show lower ++ " <: " ++ show upper
     | (_, (_, ref)) <- refs ]
@@ -292,7 +290,7 @@
 -- pM = lift . unsafeIOToST . print
 -- pM = const $ return ()
 
-subtype :: Monad m =>
+subtype :: MonadError String m =>
            Int -> [TyVarR] -> Type -> [TyVarR] -> Type ->
            m ([Type], [Type])
 subtype limit uvars1 t1i uvars2 t2i =
@@ -388,7 +386,7 @@
       _ -> giveUp t u
     --
     giveUp t u = 
-      fail $
+      throwError $
         "Got type `" ++ show t ++ "' where type `" ++
         show u ++ "' expected"
     --
@@ -406,6 +404,7 @@
         | tj       <- ts
         | uj       <- us ]
     --
+    -- XXX This is woefully insufficient
     subkind qd1 qd2 orElse =
       if qd1 <: qd2 then return () else do
         (m1, m2) <- getUVars
@@ -423,7 +422,7 @@
             -> lowerBoundUVar ref (tyTop qlit)
           _ -> orElse
 
-jointype :: Monad m => Int -> Bool -> Type -> Type -> m Type
+jointype :: MonadError String m => Int -> Bool -> Type -> Type -> m Type
 jointype limit b t1i t2i =
   liftM clean $ runUT (cmp (b, True) t1i t2i) (alltv (t1i, t2i))
   where
@@ -485,7 +484,7 @@
         cmp m t (tysubst tvu u u1)
       -- Failure
       _ ->
-        fail $
+        throwError $
           "Could not " ++ (if direction then "join" else "meet") ++
           " types `" ++ show t ++
           "' and `" ++ show u ++ "'"
@@ -499,7 +498,7 @@
           -1 -> revCmp m tj uj
           _  -> if tj == uj
                   then return tj
-                  else fail $
+                  else throwError $
                     "Could not unify types `" ++ show tj ++
                     "' and `" ++ show uj ++ "'"
       | var      <- arity
@@ -546,7 +545,8 @@
 instance PO Type where
   t1 <: t2     = runEither (const False) (const True)
                            (subtype 100 [] t1 [] t2)
-  ifMJ b t1 t2 = jointype 100 b t1 t2
+  ifMJ b t1 t2 = runEither (throwError . strMsg) return
+                           (jointype 100 b t1 t2)
 
 subtypeTests, joinTests, uvarsTests :: T.Test
 
@@ -915,14 +915,14 @@
   t1 \/! t2 = Left (t1, t2)
   t1 /\! t2 = Right (t1, t2)
   Left  (t1, t2) ==! t =
-    T.assertEqual (show t1 ++ " \\/ " ++ show t2 ++ " = " ++ show t)
-                  (Just t) (t1 \/? t2)
+    tassertSuccess (show t1 ++ " \\/ " ++ show t2 ++ " = " ++ show t)
+                   t (t1 \/? t2)
   Right (t1, t2) ==! t =
-    T.assertEqual (show t1 ++ " /\\ " ++ show t2 ++ " = " ++ show t)
-                  (Just t) (t1 /\? t2)
+    tassertSuccess (show t1 ++ " /\\ " ++ show t2 ++ " = " ++ show t)
+                   t (t1 /\? t2)
   t1 !/\ t2 =
-    T.assertEqual (show t1 ++ " /\\ " ++ show t2 ++ " DNE")
-                  Nothing (t1 /\? t2)
+    tassertFailure (show t1 ++ " /\\ " ++ show t2 ++ " DNE")
+                   (t1 /\? t2)
   infix 2 ==!
   infix 4 \/!, /\!, !/\
   a = tvUn "a"; b = tvUn "b"; c = tvAf "c"; d = tvAf "d"
@@ -997,8 +997,8 @@
       ==! (noU, noU, TyVar e, noA)
   , tyRecv (TyVar c) .*. tyRecv (TyVar c) !>:
     tyRecv (TyVar e) .*. tyRecv (TyVar f)
-  , T.assertEqual "'<c `supertype` '<d = ERROR"
-      Nothing (subtype 100 [c] (TyVar c) [d] (TyVar d))
+  , tassertFailure "'<c `supertype` '<d = ERROR"
+                   (subtype 100 [c] (TyVar c) [d] (TyVar d))
   , tyFollow (TyVar a) (TyVar b) >:!
     tyFollow tyUnit (tyRecv tyInt .:.
                      TyMu e (tyFollow tyUnit (tyRecv tyInt .:.
@@ -1016,25 +1016,32 @@
   t1 <:! t2 = Left (t1, t2)
   t1 >:! t2 = Right (t1, t2)
   Left (t1, t2) ==! (ta, tb, tc, td) =
-    T.assertEqual (show t1 ++ " `subtype` " ++ show t2)
-      (Right ([ta, tb, tc, td], []))
-      (runEither Left Right $ subtype 100 set t1 [] t2)
+    tassertSuccess (show t1 ++ " `subtype` " ++ show t2)
+                   ([ta, tb, tc, td], [])
+                   (subtype 100 set t1 [] t2)
   Right (t1, t2) ==! (ta, tb, tc, td) =
-    T.assertEqual (show t1 ++ " `supertype` " ++ show t2)
-      (Right ([], [ta, tb, tc, td]))
-      (runEither Left Right $ subtype 100 [] t2 set t1)
+    tassertSuccess (show t1 ++ " `supertype` " ++ show t2)
+                   ([], [ta, tb, tc, td])
+                   (subtype 100 [] t2 set t1)
   t1 !<: t2 =
-    T.assertEqual (show t1 ++ " `subtype` " ++ show t2 ++ " = ERROR")
-                  Nothing (subtype 100 set t1 [] t2)
+    tassertFailure (show t1 ++ " `subtype` " ++ show t2 ++ " = ERROR")
+                   (subtype 100 set t1 [] t2)
   t1 !>: t2 =
-    T.assertEqual (show t1 ++ " `supertype` " ++ show t2 ++ " = ERROR")
-                  Nothing (subtype 100 [] t2 set t1)
+    tassertFailure (show t1 ++ " `supertype` " ++ show t2 ++ " = ERROR")
+                   (subtype 100 [] t2 set t1)
   infix 2 ==!
   infix 4 <:!, !<:, >:!, !>:
   noU = tyBot; noA = tyBot
   set = [a, b, c, d]
   a   = tvUn "a"; b = tvUn "b"; c = tvAf "c"; d = tvAf "d"
   e   = tvAf "e"; f = tvAf "f"
+
+tassertSuccess :: (Eq a, Show a) =>
+                  String -> a -> Either String a -> T.Assertion
+tassertSuccess msg = T.assertEqual msg . Right
+
+tassertFailure :: String -> Either String a -> T.Assertion
+tassertFailure msg = either (\_ -> return ()) (\_ -> T.assertFailure msg)
 
 tests :: IO ()
 tests = do
diff --git a/src/Util.hs b/src/Util.hs
--- a/src/Util.hs
+++ b/src/Util.hs
@@ -1,5 +1,8 @@
 -- | Utility functions
-{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE
+      CPP,
+      FlexibleContexts
+      #-}
 module Util (
   -- * List combinators
   -- ** Shallow mapping
@@ -45,7 +48,16 @@
 import Control.Arrow hiding (loop, (<+>))
 import Control.Monad
 import Control.Applicative (Applicative(..), (<$>), (<$), (<**>))
+
+#if PARSEC_VERSION == 2
+
 import Text.ParserCombinators.Parsec (GenParser)
+-- | Parsec parsers are Applicatives, which lets us write slightly
+--   more pleasant, non-monadic-looking parsers
+instance Applicative (GenParser a b) where
+  pure  = return
+  (<*>) = ap
+#endif
 
 -- | Right-associative monadic fold
 foldrM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
@@ -221,9 +233,3 @@
 instance GSequence Maybe where
   gsequence  = maybe (return Nothing) (liftM return)
   gsequence_ = maybe (return ()) (>> return ())
-
--- | Parsec parsers are Applicatives, which lets us write slightly
---   more pleasant, non-monadic-looking parsers
-instance Applicative (GenParser a b) where
-  pure  = return
-  (<*>) = ap
