diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+# 2.0.0.0
+
+  * Scrap `HasField` typeclass; add row types
+  * Expressions with multiple folds no longer blow up memory (🤞)
+  * Fix many bugs
+  * Unicode syntax works from command-line
+
 # 1.2.0.0
 
   * `~`, `!~` builtins require that the regex be the second argument.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -19,9 +19,9 @@
 cabal install jacinda
 ```
 
-## Vim Plugin
+## Editor Support
 
-There is a [vim plugin](https://github.com/vmchale/jacinda-vim).
+There is a [vim plugin](https://github.com/vmchale/jacinda-vim) and a [VSCode extension](https://marketplace.visualstudio.com/items?itemName=vmchale.jacinda).
 
 # SHOCK & AWE
 
@@ -59,23 +59,13 @@
 
 # Status
 
-The project is in beta, it doesn't necessarily work and there are many
-missing features, but the language will remain stable.
-
-It is worse than awk but it has its place and it avoids some of the painful
-imperative/scoping defects.
-
 ## Missing Features & Bugs
 
   * No nested dfns
-  * Obscure renamer edge cases during evaluation
-  * `printf` formatting for floats
   * No list literal syntax
-  * Typeclasses are not documented
   * Postfix `:f` and `:i` are handled poorly
   * Polymorphic functions can't be instantiated with separate types (global
     monomorphism restriction)
-  * Expressions with multiple folds blow up in memory sometimes
 
 Intentionally missing features:
 
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -1,18 +1,18 @@
 module Main (main) where
 
-import qualified Data.ByteString      as BS
-import qualified Data.ByteString.Lazy as BSL
-import           Data.Semigroup       ((<>))
-import qualified Data.Version         as V
-import           Jacinda.File
+import           Data.Semigroup      ((<>))
+import qualified Data.Text           as T
+import qualified Data.Text.IO        as TIO
+import qualified Data.Version        as V
+import           File
 import           Options.Applicative
-import qualified Paths_jacinda        as P
-import           System.IO            (stdin)
+import qualified Paths_jacinda       as P
+import           System.IO           (stdin)
 
 data Command = TypeCheck !FilePath ![FilePath]
              | Run !FilePath !(Maybe FilePath) ![FilePath]
-             | Expr !BSL.ByteString !(Maybe FilePath) !(Maybe BS.ByteString) ![FilePath]
-             | Eval !BSL.ByteString
+             | Expr !T.Text !(Maybe FilePath) !(Maybe T.Text) ![FilePath]
+             | Eval !T.Text
 
 jacFile :: Parser FilePath
 jacFile = argument str
@@ -20,14 +20,13 @@
     <> help "Source code"
     <> jacCompletions)
 
-jacFs :: Parser (Maybe BS.ByteString)
+jacFs :: Parser (Maybe T.Text)
 jacFs = optional $ option str
     (short 'F'
     <> metavar "REGEXP"
     <> help "Field separator")
 
--- FIXME: this seems to mishandle iota on the command-line..
-jacExpr :: Parser BSL.ByteString
+jacExpr :: Parser T.Text
 jacExpr = argument str
     (metavar "EXPR"
     <> help "Jacinda expression")
@@ -77,9 +76,9 @@
 main = run =<< execParser wrapper
 
 run :: Command -> IO ()
-run (TypeCheck fp is)         = tcIO is =<< BSL.readFile fp
-run (Run fp Nothing is)       = do { contents <- BSL.readFile fp ; runOnHandle is contents Nothing stdin }
-run (Run fp (Just dat) is)    = do { contents <- BSL.readFile fp ; runOnFile is contents Nothing dat }
+run (TypeCheck fp is)         = tcIO is =<< TIO.readFile fp
+run (Run fp Nothing is)       = do { contents <- TIO.readFile fp ; runOnHandle is contents Nothing stdin }
+run (Run fp (Just dat) is)    = do { contents <- TIO.readFile fp ; runOnFile is contents Nothing dat }
 run (Expr eb Nothing fs is)   = runOnHandle is eb fs stdin
 run (Expr eb (Just fp) fs is) = runOnFile is eb fs fp
 run (Eval e)                  = print (exprEval e)
diff --git a/bench/Bench.hs b/bench/Bench.hs
--- a/bench/Bench.hs
+++ b/bench/Bench.hs
@@ -2,28 +2,25 @@
 
 module Main (main) where
 
-import           Control.DeepSeq      (NFData (..))
+import           A
+import           Control.DeepSeq    (NFData (..))
 import           Criterion.Main
-import qualified Data.ByteString.Lazy as BSL
-import           Jacinda.AST
-import           Jacinda.File
-import           System.IO.Silently   (silence)
+import qualified Data.Text.IO       as TIO
+import           File
+import           System.IO.Silently (silence)
 
 main :: IO ()
 main =
     defaultMain [ bgroup "eval"
                       [ bench "exprEval" $ nf exprEval "[x+' '+y]|'' split '01-23-1987' /-/"
                       , bench "runOnFile" $ nfIO (silence $ runOnFile [] "(+)|0 {%/Bloom/}{1}" Nothing "bench/data/ulysses.txt")
-                      , bench "runOnFile" $ nfIO (silence $ do { contents <- BSL.readFile "examples/wc.jac" ; runOnFile [] contents Nothing "bench/data/ulysses.txt" })
-                      , bench "runOnFile" $ nfIO (silence $ do { contents <- BSL.readFile "examples/span2.jac" ; runOnFile [] contents Nothing "bench/data/span.txt" })
+                      , bench "runOnFile" $ nfIO (silence $ do { contents <- TIO.readFile "examples/wc.jac" ; runOnFile [] contents Nothing "bench/data/ulysses.txt" })
+                      , bench "runOnFile" $ nfIO (silence $ do { contents <- TIO.readFile "examples/span2.jac" ; runOnFile [] contents Nothing "bench/data/span.txt" })
                       ]
                 ]
 
 instance NFData (E a) where
-    rnf (StrLit _ str)  = rnf str
-    rnf (IntLit _ i)    = rnf i
-    rnf (BoolLit _ b)   = rnf b
-    rnf (FloatLit _ f)  = rnf f
-    rnf (Arr _ es)      = rnf es
-    rnf (Tup _ es)      = rnf es
+    rnf (StrLit _ str)  = rnf str; rnf (ILit _ i) = rnf i;
+    rnf (BLit _ b) = rnf b; rnf (FLit _ f) = rnf f
+    rnf (Arr _ es) = rnf es; rnf (Tup _ es) = rnf es
     rnf (OptionVal _ e) = rnf e
diff --git a/doc/guide.pdf b/doc/guide.pdf
Binary files a/doc/guide.pdf and b/doc/guide.pdf differ
diff --git a/examples/chess.jac b/examples/chess.jac
--- a/examples/chess.jac
+++ b/examples/chess.jac
@@ -1,9 +1,3 @@
-{. fn sum(x) :=
-  {. (+)|0 x;
-
-{. fn count(x) :=
-  {. sum [:1"x;
-
 fn count(x) ≔
   (+)|0 [:1"x;
 
diff --git a/examples/diffctx.jac b/examples/diffctx.jac
--- a/examples/diffctx.jac
+++ b/examples/diffctx.jac
@@ -17,7 +17,7 @@
 fn process(x) :=
   let
     val fpCtx ≔ fromMaybe 'WARN' (x->1)
-    val line ≔ (λl. sprintf'%s: %s' (fpCtx.l))"(x->2)
+    val line ≔ (λl. sprintf'%s: %s' (fpCtx.l))¨(x->2)
   in line end;
 
 process:?(step/(\+|-).*TODO/)^(None.None)$0
diff --git a/examples/extensions.jac b/examples/extensions.jac
new file mode 100644
--- /dev/null
+++ b/examples/extensions.jac
@@ -0,0 +1,10 @@
+{. shows extensions used + total line count
+{. invoke like so:
+{. fd '\.hs$' -x ja run extensions.jac -i | sort -k2 -n
+fn count(x):=
+  (+)|0 [:1"x;
+
+let
+  val ext := count {%/^\s*\{-#\s*LANGUAGE\s*(.*)#-\}/}{`0}
+  val tot := count $0
+in sprintf '%s\t%i\t%i' (fp.ext.tot) end
diff --git a/examples/fdupes.jac b/examples/fdupes.jac
new file mode 100644
--- /dev/null
+++ b/examples/fdupes.jac
@@ -0,0 +1,10 @@
+{. invoke like so:
+{. fd . -t f -x md5sum | sort | ja run fdupes.jac
+{. inspired by Yann Le Du: https://twitter.com/Yann_Le_Du/status/1610299070819729410
+
+fn step(acc, this) :=
+  if (substr this 0 32) = (substr (acc->1) 0 32)
+    then (this . Some (this + '\n' + acc->1))
+    else (this . None);
+
+(->2):?step^(''.None) $0
diff --git a/examples/hsExtensions.jac b/examples/hsExtensions.jac
--- a/examples/hsExtensions.jac
+++ b/examples/hsExtensions.jac
@@ -6,4 +6,4 @@
     val extList ≔ (\s.split s /,\s*/)"extStr
   in extList end;
 
-~.(λx.(intercalate'\n')"(findExtensions x)):?$0
+~.(λx.(intercalate'\n')¨(findExtensions x)):?$0
diff --git a/examples/laconicPragmas.jac b/examples/laconicPragmas.jac
new file mode 100644
--- /dev/null
+++ b/examples/laconicPragmas.jac
@@ -0,0 +1,3 @@
+let
+  val list := [x+', '+y]|>[x ~* 1 /\{-#\s*LANGUAGE\s*([^\s]*)\s*#-\}/]:?$0
+in sprintf '{-# LANGUAGE %s -#}' list end
diff --git a/examples/liblibversion.jac b/examples/liblibversion.jac
new file mode 100644
--- /dev/null
+++ b/examples/liblibversion.jac
@@ -0,0 +1,3 @@
+@include'lib/string.jac'
+
+(+)|'' (intercalate '\n')¨{% /-lHS/}{captures `0 1 /-lHS([A-Aa-z][A-Za-z0-9\-]*\d+(\.\d+)*)/}
diff --git a/examples/libversion.jac b/examples/libversion.jac
--- a/examples/libversion.jac
+++ b/examples/libversion.jac
@@ -1,3 +1,1 @@
-@include'lib/string.jac'
-
-(+)|'' (intercalate '\n')"{% /-lHS/}{captures `0 1 /-lHS([A-Aa-z][A-Za-z0-9\-]*\d+(\.\d+)*)/}
+(+)|> ([x+'\n'+y]|>)¨{%/-lHS/}{captures `0 1 /-lHS([A-Aa-z][A-Za-z0-9\-]*\d+(\.\d+)*)/}
diff --git a/examples/nmCtx.jac b/examples/nmCtx.jac
--- a/examples/nmCtx.jac
+++ b/examples/nmCtx.jac
@@ -17,7 +17,7 @@
 fn process(x) :=
   let
     val fpCtx := fromMaybe 'WARN' (x->1)
-    val line := (λl. sprintf'%s: %s' (fpCtx.l))"(x->2)
+    val line := (λl. sprintf'%s: %s' (fpCtx.l))¨(x->2)
   in line end;
 
 process:?step^(None.None)$0
diff --git a/examples/path.jac b/examples/path.jac
--- a/examples/path.jac
+++ b/examples/path.jac
@@ -2,4 +2,4 @@
 fn path(x) :=
   ([x+'\n'+y]) |> (splitc x ':');
 
-path"$0
+path¨$0
diff --git a/examples/path2.jac b/examples/path2.jac
--- a/examples/path2.jac
+++ b/examples/path2.jac
@@ -5,4 +5,4 @@
 fn path(x) :=
   intercalate '\n' (splitc x ':');
 
-path"$0
+path¨$0
diff --git a/examples/pathx.jac b/examples/pathx.jac
--- a/examples/pathx.jac
+++ b/examples/pathx.jac
@@ -2,4 +2,4 @@
 fn path(x) :=
   ([x+'\n'+y]) |> (splitc x ':');
 
-path"$0
+path¨$0
diff --git a/examples/silly.jac b/examples/silly.jac
deleted file mode 100644
--- a/examples/silly.jac
+++ /dev/null
@@ -1,7 +0,0 @@
-fn count(x):=
-  (+)|0 [:1"x;
-
-let
-  val ext := count {%/\{-#\s*LANGUAGE\s*(.*)#-\}/}{`0}
-  val tot := count $0
-in (ext.tot) end
diff --git a/examples/tags.jac b/examples/tags.jac
--- a/examples/tags.jac
+++ b/examples/tags.jac
@@ -7,4 +7,4 @@
     val outLine := sprintf '%s\t%s\t%s' (line.2 . fp . mkEx s)
   in outLine end;
 
-processStr"{%/fn +[[:lower:]][[:latin:]]*.*:=/}{`0}
+processStr¨{%/fn +[[:lower:]][[:latin:]]*.*:=/}{`0}
diff --git a/jacinda.cabal b/jacinda.cabal
--- a/jacinda.cabal
+++ b/jacinda.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.0
 name:               jacinda
-version:            1.2.0.0
+version:            2.0.0.0
 license:            AGPL-3
 license-file:       COPYING
 maintainer:         vamchale@gmail.com
@@ -35,31 +35,34 @@
 
 library jacinda-lib
     exposed-modules:
-        Jacinda.Parser
-        Jacinda.Parser.Rewrite
-        Jacinda.AST
-        Jacinda.Ty
-        Jacinda.Ty.Const
+        Parser
+        Parser.Rw
+        A
+        Ty
+        Ty.Const
         Jacinda.Regex
-        Jacinda.File
+        File
 
     hs-source-dirs:   src
     other-modules:
-        Jacinda.Lexer
-        Intern.Name
-        Intern.Unique
-        Jacinda.Rename
-        Jacinda.Backend.Normalize
-        Jacinda.Backend.TreeWalk
+        A.I
+        A.E
+        L
+        Jacinda.Fuse
+        Nm
+        U
+        R
+        Jacinda.Check.Field
+        Jacinda.Backend.Parse
+        Jacinda.Backend.Const
+        Jacinda.Backend.P
         Jacinda.Backend.Printf
-        Jacinda.Include
-        Data.List.Ext
-        Data.Vector.Ext
+        Include
         Paths_jacinda
 
     autogen-modules:  Paths_jacinda
     default-language: Haskell2010
-    ghc-options:      -Wall -O2
+    ghc-options:      -Wall -O2 -Wno-missing-signatures
     build-depends:
         base >=4.10.0.0 && <5,
         bytestring >=0.11.0.0,
@@ -79,7 +82,7 @@
         split
 
     if !flag(cross)
-        build-tool-depends: alex:alex, happy:happy
+        build-tool-depends: alex:alex >=3.4.0.0, happy:happy
 
     if impl(ghc >=8.0)
         ghc-options:
@@ -106,7 +109,7 @@
         base,
         jacinda-lib,
         optparse-applicative >=0.13.0.0,
-        bytestring
+        text
 
     if impl(ghc >=8.0)
         ghc-options:
@@ -132,8 +135,9 @@
         base,
         jacinda-lib,
         tasty,
-        tasty-hunit,
-        bytestring
+        bytestring,
+        text,
+        tasty-hunit
 
     if impl(ghc >=8.0)
         ghc-options:
@@ -160,7 +164,7 @@
         criterion,
         jacinda-lib,
         deepseq,
-        bytestring,
+        text,
         silently
 
     if impl(ghc >=8.0)
diff --git a/lib/gitCtx.jac b/lib/gitCtx.jac
new file mode 100644
--- /dev/null
+++ b/lib/gitCtx.jac
@@ -0,0 +1,22 @@
+@include'prelude/fn.jac'
+@include'lib/maybe.jac'
+
+fn mMatch(p, str) :=
+  if p str
+    then Some str
+    else None;
+
+fn step(p, ctx, line) :=
+  let
+    val fpCtx ≔ line ~* 1 /diff --git\s+([^\s]+)/
+    val mLine ≔ mMatch p line
+  in (alternative (ctx->1) fpCtx.mLine) end;
+
+fn process(x) :=
+  let
+    val fpCtx ≔ fromMaybe 'WARN' (x->1)
+    val line ≔ (λl. sprintf'%s: %s' (fpCtx.l))"(x->2)
+  in line end;
+
+fn main(p) :=
+  process:?(step p)^(None.None)$0;
diff --git a/man/ja.1 b/man/ja.1
--- a/man/ja.1
+++ b/man/ja.1
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pandoc 2.19.2
+.\" Automatically generated by Pandoc 3.1.6.1
 .\"
 .\" Define V font for inline verbatim, using C font in formats
 .\" that render this, and otherwise B font.
@@ -30,7 +30,7 @@
 ja e \[aq]11.67*1.2\[cq]
 .SH DESCRIPTION
 .PP
-\f[B]Jacinda\f[R] is a data stream processing language \[`a] la AWK.
+\f[B]Jacinda\f[R] is a data stream processing language à la AWK.
 .SH SUBCOMMANDS
 .PP
 \f[B]run\f[R] - Run a program from file
@@ -78,7 +78,7 @@
 \f[B]\[ha]\f[R] Ternary operator: scan
 (b -> a -> b) -> b -> Stream a -> Stream b
 .TP
-\f[B]\[dq]\f[R] Binary operator: map
+\f[B]\[dq]\f[R], \f[B]¨\f[R] Binary operator: map
 Functor f :=> a -> b -> f a -> f b
 .TP
 \f[B][:\f[R] Unary operator: const
@@ -91,11 +91,16 @@
 (a -> a -> b) -> Stream a -> Stream b
 .TP
 \f[B]\[ti].\f[R] Unary deduplication (stream)
-Eq a :=> Stream a -> Stream a
-.PP
+Ord a :=> Stream a -> Stream a
+.TP
+\f[B]\[ti].*\f[R] Deduplicate on (stream)
+Ord b :=> (a -> b) -> Stream a -> Stream a
+.TP
 \f[B]max\f[R] Maximum of two values
-.PP
+Ord a :=> a -> a -> a
+.TP
 \f[B]min\f[R] Minimum of two values
+Ord a :=> a -> a -> a
 .PP
 \f[B]&\f[R] Boolean and
 .PP
@@ -109,7 +114,7 @@
 \f[B]!\[ti]\f[R] Does not match
 Str -> Regex -> Bool
 .PP
-\f[B]ix\f[R] Line number
+\f[B]ix\f[R], \f[B]⍳\f[R] Line number
 .TP
 \f[B]substr\f[R] Extract substring
 Str -> Int -> Int -> Str
@@ -120,17 +125,17 @@
 \f[B]splitc\f[R] Split a string on a single character
 Str -> Str -> List Str
 .TP
-\f[B]|.\f[R] Floor function
+\f[B]⌊\f[R], \f[B]|.\f[R] Floor function
 Float -> Int
 .TP
-\f[B]|\[ga]\f[R] Ceiling function
+\f[B]⌈\f[R], \f[B]|\[ga]\f[R] Ceiling function
 Float -> Int
 .PP
 \f[B]-.\f[R] Unary negate
-.PP
-\f[B]sprintf\f[R] Convert an expression to a string using the format
-string
 .TP
+\f[B]sprintf\f[R] Convert an expression to a string using the format string
+\f[B]%f\f[R] float \f[B]%i\f[R] integer \f[B]%s\f[R] string
+.TP
 \f[B]option\f[R] Option eliminator
 b -> (a -> b) -> Option a -> b
 .TP
@@ -141,7 +146,7 @@
 \f[B]\[ti]*\f[R] Match, returning nth capture group
 Str -> Int -> Regex -> Option Str
 .TP
-\f[B]captures\f[R] Return all captures
+\f[B]captures\f[R] Return all captures (nth capture group)
 Str -> Int -> Regex -> List Str
 .TP
 \f[B]:?\f[R] mapMaybe
@@ -181,6 +186,11 @@
 \f[B]{.\f[R] Line comment
 .PP
 \f[B]\[at]include\[aq]/path/file.jac\[cq]\f[R] File include
+.SS DECLARATIONS
+.PP
+\f[B]:set fs=/REGEX/;\f[R] Set field separator
+.PP
+\f[B]:flush;\f[R] Flush stdout for every line
 .SH INFLUENTIAL ENVIRONMENT VARIABLES
 .PP
 \f[V]JAC_PATH\f[R] - colon-separated list of directories to search
@@ -192,13 +202,13 @@
 {#\[ga]0>72}{\[ga]0}
 Print lines longer than 72 bytes
 .TP
-{| sprintf \[aq]%i %i\[aq] (\[ga]2 . \[ga]1)}
-Print the first two fields in opposite order
-.TP
 {ix=3}{\[ga]0}
 Select only the third line
 .TP
-:set fs := /,[ \[rs]t]*|[ \[rs]t]+/; {| sprintf \[aq]%i %i\[aq] (\[ga]2 . \[ga]1)}
+{|sprintf \[aq]%i %i\[aq] (\[ga]2 . \[ga]1)}
+Print the first two fields in opposite order
+.TP
+:set fs := /,[ \[rs]t]*|[ \[rs]t]+/; {|sprintf \[aq]%i %i\[aq] (\[ga]2 . \[ga]1)}
 Same, with input fields separated by comma and/or blanks and tabs.
 .TP
 (+)|0 $1:i
@@ -224,6 +234,9 @@
 .TP
 [y]|> {|\[ga]0\[ti]/\[ha]$/}
 Is the last line blank?
+.TP
+\&.?{|\[ga]1 \[ti]* 1 /([\[ha]\[rs]?]*)/}
+Trim URL
 .SH BUGS
 .PP
 Please report any bugs you may come across to
diff --git a/prelude/fn.jac b/prelude/fn.jac
--- a/prelude/fn.jac
+++ b/prelude/fn.jac
@@ -30,12 +30,9 @@
 
 fn head :=
   ([[:x]|>);
-<<<<<<< Updated upstream
-=======
 
 {. fold two on the same stream
 fn foldTwo(op0, op1, seed0, seed1, stream) :=
   let
     val go := \acc. \line. (op0 (acc->1) line . op1 (acc->2) line)
   in go|(seed0.seed1) stream end;
->>>>>>> Stashed changes
diff --git a/src/A.hs b/src/A.hs
new file mode 100644
--- /dev/null
+++ b/src/A.hs
@@ -0,0 +1,424 @@
+{-# LANGUAGE DeriveFoldable    #-}
+{-# LANGUAGE DeriveFunctor     #-}
+{-# LANGUAGE DeriveGeneric     #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeFamilies      #-}
+
+module A ( E (..)
+         , T (..)
+         , TB (..)
+         , BBin (..)
+         , BTer (..)
+         , BUn (..)
+         , DfnVar (..)
+         , D (..)
+         , Program (..)
+         , C (..)
+         , N (..)
+         , mapExpr
+         , getFS, flushD
+         -- * Base functors
+         , EF (..)
+         ) where
+
+import           Control.Recursion  (Base, Corecursive, Recursive)
+import qualified Data.ByteString    as BS
+import qualified Data.IntMap        as IM
+import           Data.Maybe         (listToMaybe)
+import           Data.Semigroup     ((<>))
+import qualified Data.Text          as T
+import           Data.Text.Encoding (decodeUtf8)
+import qualified Data.Vector        as V
+import           GHC.Generics       (Generic)
+import           Nm
+import           Prettyprinter      (Doc, Pretty (..), braces, brackets, concatWith, encloseSep, flatAlt, group, hardline, indent, parens, pipe, punctuate, tupled, (<+>))
+import           Regex.Rure         (RurePtr)
+
+infixr 6 <#>
+infixr 6 <##>
+
+(<#>) :: Doc a -> Doc a -> Doc a
+(<#>) x y = x <> hardline <> y
+
+(<##>) :: Doc a -> Doc a -> Doc a
+(<##>) x y = x <> hardline <> hardline <> y
+
+data TB = TyInteger
+        | TyFloat
+        | TyStr | TyR
+        | TyStream
+        | TyVec
+        | TyBool
+        | TyOption
+        | TyUnit
+        deriving (Eq, Ord)
+
+tupledByFunky :: Doc ann -> [Doc ann] -> Doc ann
+tupledByFunky sep = group . encloseSep (flatAlt "⟨ " "⟨") (flatAlt " ⟩" "⟩") sep
+
+tupledBy :: Doc ann -> [Doc ann] -> Doc ann
+tupledBy sep = group . encloseSep (flatAlt "( " "(") (flatAlt " )" ")") sep
+
+jacTup :: Pretty a => [a] -> Doc ann
+jacTup = tupledBy " . " . fmap pretty
+
+data T = TyB { tyBuiltin :: TB }
+       | TyApp { tyApp0 :: T, tyApp1 :: T }
+       | TyArr { tyArr0 :: T, tyArr1 :: T }
+       | TyVar { tyVar :: Nm () }
+       | TyTup { tyTups :: [T] }
+       | Rho { tyRho :: Nm (), tyArms :: IM.IntMap T }
+       deriving (Eq, Ord)
+
+instance Pretty TB where
+    pretty TyInteger = "Integer"
+    pretty TyStream  = "Stream"
+    pretty TyBool    = "Bool"
+    pretty TyStr     = "Str"
+    pretty TyFloat   = "Float"
+    pretty TyVec     = "List"
+    pretty TyOption  = "Optional"
+    pretty TyUnit    = "𝟙"
+    pretty TyR       = "Regex"
+
+instance Show TB where show=show.pretty
+
+instance Pretty T where
+    pretty (TyB b)        = pretty b
+    pretty (TyApp ty ty') = pretty ty <+> pretty ty'
+    pretty (TyVar n)      = pretty n
+    pretty (TyArr ty ty') = pretty ty <+> "⟶" <+> pretty ty'
+    pretty (TyTup tys)    = jacTup tys
+    pretty (Rho n fs)     = braces (pretty n <+> pipe <+> prettyFields (IM.toList fs))
+
+prettyFields :: [(Int, T)] -> Doc ann
+prettyFields = mconcat . punctuate "," . fmap g where g (i, t) = pretty i <> ":" <+> pretty t
+
+instance Show T where show=show.pretty
+
+data BUn = Tally -- length of string field
+         | Const
+         | Not -- ^ Boolean
+         | At Int
+         | Select Int
+         | IParse
+         | FParse
+         | Parse
+         | Floor
+         | Ceiling
+         | Some
+         | Dedup
+         | CatMaybes
+         | Negate
+         | TallyList -- length of vector
+         deriving (Eq)
+
+instance Pretty BUn where
+    pretty Tally      = "#"
+    pretty Const      = "[:"
+    pretty Not        = "!"
+    pretty (At i)     = "." <> pretty i
+    pretty (Select i) = "->" <> pretty i
+    pretty IParse     = ":i"
+    pretty FParse     = ":f"
+    pretty Floor      = "floor"
+    pretty Ceiling    = "ceil"
+    pretty Parse      = ":"
+    pretty Some       = "Some"
+    pretty Dedup      = "~."
+    pretty CatMaybes  = ".?"
+    pretty Negate     = "-."
+    pretty TallyList  = "#*"
+
+data BTer = ZipW
+          | Fold | Scan
+          | Substr
+          | Option
+          | Captures | AllCaptures
+          deriving (Eq)
+
+instance Pretty BTer where
+    pretty ZipW        = ","
+    pretty Fold        = "|"
+    pretty Scan        = "^"
+    pretty Substr      = "substr"
+    pretty Option      = "option"
+    pretty Captures    = "~*"
+    pretty AllCaptures = "captures"
+
+-- builtin
+data BBin = Plus | Times | Div
+          | Minus | Exp
+          | Eq | Neq | Geq | Gt | Lt | Leq
+          | Map
+          | Matches -- ^ @'string' ~ /pat/@
+          | NotMatches
+          | And | Or
+          | Min | Max
+          | Split | Splitc
+          | Prior
+          | Filter
+          | Sprintf
+          | Match
+          | MapMaybe
+          | Fold1
+          | DedupOn
+          deriving (Eq)
+
+instance Pretty BBin where
+    pretty Plus       = "+"
+    pretty Times      = "*"
+    pretty Div        = "%"
+    pretty Minus      = "-"
+    pretty Eq         = "="
+    pretty Gt         = ">"
+    pretty Lt         = "<"
+    pretty Geq        = ">="
+    pretty Leq        = "<="
+    pretty Neq        = "!="
+    pretty Map        = "¨"
+    pretty Matches    = "~"
+    pretty NotMatches = "!~"
+    pretty And        = "&"
+    pretty Or         = "||"
+    pretty Max        = "max"
+    pretty Min        = "min"
+    pretty Prior      = "\\."
+    pretty Filter     = "#."
+    pretty Split      = "split"
+    pretty Splitc     = "splitc"
+    pretty Sprintf    = "sprintf"
+    pretty Match      = "match"
+    pretty MapMaybe   = ":?"
+    pretty Fold1      = "|>"
+    pretty Exp        = "**"
+    pretty DedupOn    = "~.*"
+
+data DfnVar = X | Y deriving (Eq)
+
+instance Pretty DfnVar where pretty X = "x"; pretty Y = "y"
+
+-- 0-ary
+data N = Ix | Nf | None | Fp deriving (Eq)
+
+-- expression
+data E a = Column { eLoc :: a, col :: Int }
+         | IParseCol { eLoc :: a, col :: Int } -- always a column
+         | FParseCol { eLoc :: a, col :: Int }
+         | ParseCol { eLoc :: a, col :: Int }
+         | Field { eLoc :: a, eField :: Int }
+         | LastField { eLoc :: a }
+         | AllField { eLoc :: a } -- ^ Think @$0@ in awk.
+         | AllColumn { eLoc :: a } -- ^ Think @$0@ in awk.
+         | EApp { eLoc :: a, eApp0 :: E a, eApp1 :: E a }
+         | Guarded { eLoc :: a, eP :: E a, eGuarded :: E a }
+         | Implicit { eLoc :: a, eImplicit :: E a }
+         | Let { eLoc :: a, eBind :: (Nm a, E a), eE :: E a }
+         -- TODO: literals type (make pattern matching easier down the road)
+         | Var { eLoc :: a, eVar :: !(Nm a) }
+         | ILit { eLoc :: a, eInt :: !Integer }
+         | BLit { eLoc :: a, eBool :: !Bool }
+         | StrLit { eLoc :: a, eStr :: BS.ByteString }
+         | RegexLit { eLoc :: a, eRr :: BS.ByteString }
+         | FLit { eLoc :: a, eFloat :: !Double }
+         | Lam { eLoc :: a, eBound :: Nm a, lamE :: E a }
+         | Dfn { eLoc :: a, eDfn :: E a }
+         | BB { eLoc :: a, eBin :: BBin }
+         | TB { eLoc :: a, eTer :: BTer }
+         | UB { eLoc :: a, eUn :: BUn }
+         | NB { eLoc :: a, eNil :: N }
+         | Tup { eLoc :: a, esTup :: [E a] }
+         | ResVar { eLoc :: a, dfnVar :: DfnVar }
+         | RC RurePtr -- compiled regex after normalization
+         | Arr { eLoc :: a, elems :: V.Vector (E a) }
+         | Anchor { eLoc :: a, eAnchored :: [E a] }
+         | Paren { eLoc :: a, eExpr :: E a }
+         | OptionVal { eLoc :: a, eMaybe :: Maybe (E a) }
+         | Cond { eLoc :: a, eIf :: E a, eThen :: E a, eElse :: E a }
+         | In { oop :: E a, ip :: Maybe (E a), mm :: Maybe (E a), istream :: E a }
+         deriving (Functor, Generic)
+
+instance Recursive (E a) where
+
+instance Corecursive (E a) where
+
+data EF a x = ColumnF a Int
+            | IParseColF a Int
+            | FParseColF a Int
+            | ParseColF a Int
+            | FieldF a Int
+            | LastFieldF a
+            | AllFieldF a
+            | AllColumnF a
+            | EAppF a x x
+            | GuardedF a x x
+            | ImplicitF a x
+            | LetF a (Nm a, x) x
+            | VarF a (Nm a)
+            | ILitF a Integer
+            | BLitF a Bool
+            | StrLitF a BS.ByteString
+            | RegexLitF a BS.ByteString
+            | FLitF a Double
+            | LamF a (Nm a) x
+            | DfnF a x
+            | BBF a BBin
+            | TBF a BTer
+            | UBF a BUn
+            | NBF a N
+            | TupF a [x]
+            | ResVarF a DfnVar
+            | RCF RurePtr
+            | ArrF a (V.Vector x)
+            | AnchorF a [x]
+            | ParenF a x
+            | OptionValF a (Maybe x)
+            | CondF a x x x
+            | InF x (Maybe x) (Maybe x) x
+            deriving (Generic, Functor, Foldable, Traversable)
+
+type instance Base (E a) = (EF a)
+
+instance Pretty N where
+    pretty Ix = "⍳"; pretty Nf = "nf"; pretty None = "None"; pretty Fp = "fp"
+
+instance Pretty (E a) where
+    pretty (Column _ i)                                           = "$" <> pretty i
+    pretty AllColumn{}                                            = "$0"
+    pretty (IParseCol _ i)                                        = "$" <> pretty i <> ":i"
+    pretty (FParseCol _ i)                                        = "$" <> pretty i <> ":f"
+    pretty (ParseCol _ i)                                         = "$" <> pretty i <> ":"
+    pretty AllField{}                                             = "`0"
+    pretty (Field _ i)                                            = "`" <> pretty i
+    pretty LastField{}                                            = "`*"
+    pretty (EApp _ (EApp _ (BB _ Prior) e) e')                    = pretty e <> "\\." <+> pretty e'
+    pretty (EApp _ (EApp _ (BB _ Max) e) e')                      = "max" <+> pretty e <+> pretty e'
+    pretty (EApp _ (EApp _ (BB _ Min) e) e')                      = "min" <+> pretty e <+> pretty e'
+    pretty (EApp _ (EApp _ (BB _ Split) e) e')                    = "split" <+> pretty e <+> pretty e'
+    pretty (EApp _ (EApp _ (BB _ Splitc) e) e')                   = "splitc" <+> pretty e <+> pretty e'
+    pretty (EApp _ (EApp _ (BB _ Match) e) e')                    = "match" <+> pretty e <+> pretty e'
+    pretty (EApp _ (EApp _ (BB _ Sprintf) e) e')                  = "sprintf" <+> pretty e <+> pretty e'
+    pretty (EApp _ (EApp _ (BB _ Map) e) e')                      = pretty e <> "¨" <> pretty e'
+    pretty (EApp _ (EApp _ (BB _ b) e) e')                        = pretty e <+> pretty b <+> pretty e'
+    pretty (EApp _ (BB _ b) e)                                    = parens (pretty e <> pretty b)
+    pretty (EApp _ (EApp _ (EApp _ (TB _ Fold) e) e') e'')        = pretty e <> "|" <> pretty e' <+> pretty e''
+    pretty (EApp _ (EApp _ (EApp _ (TB _ Scan) e) e') e'')        = pretty e <> "^" <> pretty e' <+> pretty e''
+    pretty (EApp _ (EApp _ (EApp _ (TB _ ZipW) op) e') e'')       = "," <> pretty op <+> pretty e' <+> pretty e''
+    pretty (EApp _ (EApp _ (EApp _ (TB _ Substr) e) e') e'')      = "substr" <+> pretty e <+> pretty e' <+> pretty e''
+    pretty (EApp _ (EApp _ (EApp _ (TB _ Option) e) e') e'')      = "option" <+> pretty e <+> pretty e' <+> pretty e''
+    pretty (EApp _ (EApp _ (EApp _ (TB _ AllCaptures) e) e') e'') = "captures" <+> pretty e <+> pretty e' <+> pretty e''
+    pretty (EApp _ (EApp _ (EApp _ (TB _ Captures) e) e') e'')    = pretty e <+> "~*" <+> pretty e' <+> pretty e''
+    pretty (EApp _ (UB _ (At i)) e)                               = pretty e <> "." <> pretty i
+    pretty (EApp _ (UB _ (Select i)) e)                           = pretty e <> "->" <> pretty i
+    pretty (EApp _ (UB _ IParse) e')                              = pretty e' <> ":i"
+    pretty (EApp _ (UB _ FParse) e')                              = pretty e' <> ":f"
+    pretty (EApp _ (UB _ Parse) e')                               = pretty e' <> ":"
+    pretty (EApp _ e@UB{} e')                                     = pretty e <> pretty e'
+    pretty (EApp _ e e')                                          = pretty e <+> pretty e'
+    pretty (Var _ n)                                              = pretty n
+    pretty (ILit _ i)                                             = pretty i
+    pretty (RegexLit _ rr)                                        = "/" <> pretty (decodeUtf8 rr) <> "/"
+    pretty (FLit _ f)                                             = pretty f
+    pretty (BLit _ True)                                          = "#t"
+    pretty (BLit _ False)                                         = "#f"
+    pretty (BB _ b)                                               = parens (pretty b)
+    pretty (UB _ u)                                               = pretty u
+    pretty (StrLit _ str)                                         = pretty (decodeUtf8 str)
+    pretty (ResVar _ x)                                           = pretty x
+    pretty (Tup _ es)                                             = jacTup es
+    pretty (Lam _ n e)                                            = parens ("λ" <> pretty n <> "." <+> pretty e)
+    pretty (Dfn _ e)                                              = brackets (pretty e)
+    pretty (Guarded _ p e)                                        = braces (pretty p) <> braces (pretty e)
+    pretty (Implicit _ e)                                         = braces ("|" <+> pretty e)
+    pretty (NB _ n)                                               = pretty n
+    pretty RC{}                                                   = "(compiled regex)"
+    pretty (Let _ (n, b) e)                                       = "let" <+> "val" <+> pretty n <+> ":=" <+> pretty b <+> "in" <+> pretty e <+> "end"
+    pretty (Paren _ e)                                            = parens (pretty e)
+    pretty (Arr _ es)                                             = tupledByFunky "," (V.toList $ pretty <$> es)
+    pretty (Anchor _ es)                                          = "&" <> tupledBy "." (pretty <$> es)
+    pretty (OptionVal _ (Just e))                                 = "Some" <+> pretty e
+    pretty (OptionVal _ Nothing)                                  = "None"
+    pretty (Cond _ e0 e1 e2)                                      = "if" <+> pretty e0 <+> "then" <+> pretty e1 <+> "else" <+> pretty e2
+
+instance Show (E a) where show=show.pretty
+
+-- for tests
+instance Eq (E a) where
+    (==) (Column _ i) (Column _ j)              = i == j
+    (==) (IParseCol _ i) (IParseCol _ j)        = i == j
+    (==) (FParseCol _ i) (FParseCol _ j)        = i == j
+    (==) (Field _ i) (Field _ j)                = i == j
+    (==) LastField{} LastField{}                = True
+    (==) AllColumn{} AllColumn{}                = True
+    (==) AllField{} AllField{}                  = True
+    (==) (EApp _ e0 e1) (EApp _ e0' e1')        = e0 == e0' && e1 == e1'
+    (==) (Guarded _ p e) (Guarded _ p' e')      = p == p' && e == e'
+    (==) (Implicit _ e) (Implicit _ e')         = e == e'
+    (==) (Let _ (n, eϵ) e) (Let _ (n', eϵ') e') = eqName n n' && e == e' && eϵ == eϵ'
+    (==) (Var _ n) (Var _ n')                   = eqName n n'
+    (==) (Lam _ n e) (Lam _ n' e')              = eqName n n' && e == e'
+    (==) (ILit _ i) (ILit _ j)                  = i == j
+    (==) (FLit _ u) (FLit _ v)                  = u == v
+    (==) (StrLit _ str) (StrLit _ str')         = str == str'
+    (==) (RegexLit _ rr) (RegexLit _ rr')       = rr == rr'
+    (==) (BLit _ b) (BLit _ b')                 = b == b'
+    (==) (BB _ b) (BB _ b')                     = b == b'
+    (==) (TB _ b) (TB _ b')                     = b == b'
+    (==) (UB _ unOp) (UB _ unOp')               = unOp == unOp'
+    (==) (NB _ x) (NB _ y)                      = x == y
+    (==) (Tup _ es) (Tup _ es')                 = es == es'
+    (==) (ResVar _ x) (ResVar _ y)              = x == y
+    (==) (Dfn _ f) (Dfn _ g)                    = f == g -- we're testing for lexical equivalence
+    (==) RC{} _                                 = error "Cannot compare compiled regex!"
+    (==) _ RC{}                                 = error "Cannot compare compiled regex!"
+    (==) (Paren _ e) e'                         = e == e'
+    (==) e (Paren _ e')                         = e == e'
+    (==) _ _                                    = False
+
+data C = IsNum | IsEq | IsOrd
+       | IsParse | IsPrintf
+       | IsSemigroup
+       | Functor -- ^ For map (@"@)
+       | Foldable | Witherable
+       deriving (Eq, Ord)
+
+instance Pretty C where
+    pretty IsNum       = "Num"
+    pretty IsEq        = "Eq"
+    pretty IsOrd       = "Ord"
+    pretty IsParse     = "Parseable"
+    pretty IsSemigroup = "Semigroup"
+    pretty Functor     = "Functor"
+    pretty Foldable    = "Foldable"
+    pretty IsPrintf    = "Printf"
+    pretty Witherable  = "Witherable"
+
+instance Show C where show=show.pretty
+
+-- decl
+data D a = SetFS T.Text
+         | FunDecl (Nm a) [Nm a] (E a)
+         | FlushDecl
+         deriving (Functor)
+
+instance Pretty (D a) where
+    pretty (SetFS bs)       = ":set" <+> "/" <> pretty bs <> "/;"
+    pretty (FunDecl n ns e) = "fn" <+> pretty n <> tupled (pretty <$> ns) <+> ":=" <#> indent 2 (pretty e <> ";")
+    pretty FlushDecl        = ":flush;"
+
+data Program a = Program { decls :: [D a], expr :: E a } deriving (Functor)
+
+instance Pretty (Program a) where
+    pretty (Program ds e) = concatWith (<##>) (pretty <$> ds) <##> pretty e
+
+instance Show (Program a) where show=show.pretty
+
+flushD :: Program a -> Bool
+flushD (Program ds _) = any p ds where p FlushDecl = True; p _ = False
+
+getFS :: Program a -> Maybe T.Text
+getFS (Program ds _) = listToMaybe (concatMap go ds) where go (SetFS bs) = [bs]; go _ = []
+
+mapExpr :: (E a -> E a) -> Program a -> Program a
+mapExpr f (Program ds e) = Program ds (f e)
diff --git a/src/A/E.hs b/src/A/E.hs
new file mode 100644
--- /dev/null
+++ b/src/A/E.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module A.E ( M, nN, eta ) where
+
+import           A
+import           Control.Monad              ((<=<))
+import           Control.Monad.State.Strict (State, get, modify)
+import           Data.Functor               (($>))
+import qualified Data.Text                  as T
+import           Nm
+import           U
+
+type M = State Int
+
+nN :: T.Text -> a -> M (Nm a)
+nN n l = do {i <- get; modify (+1) $> Nm n (U$i+1) l}
+
+doms :: T -> [T]
+doms (TyArr t t') = t:doms t'; doms _ = []
+
+cLam :: E a -> Int
+cLam (Lam _ _ e) = 1+cLam e; cLam _ = 0
+
+tuck :: E a -> (E a -> E a, E a)
+tuck (Lam l n e) = let (f, e') = tuck e in (Lam l n.f, e'); tuck e = (id, e)
+
+unseam :: [T] -> M (E T -> E T, E T -> E T)
+unseam ts = do
+    lApps <- traverse (\t -> do {n <- nN "x" t; pure (\e' -> let t' = eLoc e' in Lam (TyArr t t') n e', \e' -> let TyArr _ cod = eLoc e' in EApp cod e' (Var t n))}) ts
+    let (ls, eApps) = unzip lApps
+    pure (thread ls, thread (reverse eApps))
+    where thread = foldr (.) id
+
+mkLam :: [T] -> E T -> M (E T)
+mkLam ts e = do
+    (lam, app) <- unseam ts
+    pure $ lam (app e)
+
+eta = eM <=< eO
+
+eM :: E T -> M (E T)
+eM (EApp t ho@(BB _ Map) op)     = EApp t ho <$> eta op
+eM (EApp t ho@(BB _ Filter) op)  = EApp t ho <$> eta op
+eM (EApp t ho@(BB _ Prior) op)   = EApp t ho <$> eta op
+eM (EApp t ho@(BB _ DedupOn) op) = EApp t ho <$> eta op
+eM (EApp t ho@(BB _ Fold1) op)   = EApp t ho <$> eta op
+eM (EApp t ho@(TB _ Fold) op)    = EApp t ho <$> eta op
+eM (EApp t ho@(TB _ Scan) op)    = EApp t ho <$> eta op
+eM (EApp t ho@(TB _ ZipW) op)    = EApp t ho <$> eta op
+eM (EApp t e0 e1)                = EApp t <$> eM e0 <*> eM e1
+eM (Cond t p e0 e1)              = Cond t <$> eM p <*> eM e0 <*> eM e1
+eM (OptionVal t e)               = OptionVal t <$> traverse eM e
+eM (Implicit t e)                = Implicit t <$> eM e
+eM (Lam t n e)                   = Lam t n <$> eM e
+eM (Guarded t p e)               = Guarded t <$> eM p <*> eM e
+eM (Tup t es)                    = Tup t <$> traverse eM es
+eM (Anchor t es)                 = Anchor t <$> traverse eM es
+eM (Arr t es)                    = Arr t <$> traverse eM es
+eM (Let t (n, e') e)             = do {e'𝜂 <- eM e'; e𝜂 <- eM e; pure (Let t (n, e'𝜂) e𝜂)}
+eM e                             = pure e
+
+-- outermost
+eO :: E T -> M (E T)
+eO e@(Var t@TyArr{} _)    = mkLam (doms t) e
+eO e@(UB t _)             = mkLam (doms t) e
+eO e@(BB t _)             = mkLam (doms t) e
+eO e@(TB t _)             = mkLam (doms t) e
+eO e@(EApp t@TyArr{} _ _) = mkLam (doms t) e
+eO e@(Lam t@TyArr{} _ _)  = do
+    let l = length (doms t)
+        (preL, e') = tuck e
+    (lam, app) <- unseam (take (l-cLam e) $ doms t)
+    pure (lam (preL (app e')))
+eO e                      = pure e
diff --git a/src/A/I.hs b/src/A/I.hs
new file mode 100644
--- /dev/null
+++ b/src/A/I.hs
@@ -0,0 +1,109 @@
+{-# LANGUAGE FlexibleContexts #-}
+
+module A.I ( RM, UM, ISt (..)
+           , ib
+           , β, lβ
+           , runI
+           ) where
+
+import           A
+import           Control.Monad.State.Strict (State, gets, modify, runState, state)
+import           Data.Bifunctor             (second)
+import           Data.Foldable              (traverse_)
+import qualified Data.IntMap                as IM
+import           Nm
+import           R
+import           Ty
+import           U
+
+data ISt a = ISt { renames :: !Renames
+                 , binds   :: IM.IntMap (E a)
+                 }
+
+instance HasRenames (ISt a) where
+    rename f s = fmap (\x -> s { renames = x }) (f (renames s))
+
+type RM a = State (ISt a); type UM = State Int
+
+bind :: Nm a -> E a -> ISt a -> ISt a
+bind (Nm _ (U u) _) e (ISt r bs) = ISt r (IM.insert u e bs)
+
+runI i = second (max_.renames) . flip runState (ISt (Renames i mempty) mempty)
+
+ib :: Int -> Program T -> (E T, Int)
+ib i = uncurry (flip β).runI i.iP where iP (Program ds e) = traverse_ iD ds *> iE e
+
+β :: Int -> E a -> (E a, Int)
+β i = runI i.bM.(i `seq`)
+
+lβ :: E a -> UM (E a)
+lβ e = state (`β` e)
+
+iD :: D T -> RM T ()
+iD (FunDecl n [] e) = do {eI <- iE e; modify (bind n eI)}
+iD SetFS{} = pure (); iD FlushDecl{} = pure ()
+iD FunDecl{} = desugar
+
+desugar = error "Internal error. Should have been de-sugared in an earlier stage!"
+
+bM :: E a -> RM a (E a)
+bM (EApp _ (EApp _ (Lam _ n (Lam _ n' e')) e'') e) = do
+    eI <- bM e
+    modify (bind n' eI)
+    eI'' <- bM e''
+    modify (bind n eI'')
+    bM e'
+bM (EApp _ (Lam _ n e') e) = do
+    eI <- bM e
+    modify (bind n eI) *> bM e'
+bM (EApp l e0 e1) = do
+    e0' <- bM e0
+    e1' <- bM e1
+    case e0' of
+        Lam{} -> bM (EApp l e0' e1')
+        _     -> pure (EApp l e0' e1')
+bM e@(Var _ (Nm _ (U i) _)) = do
+    st <- gets binds
+    case IM.lookup i st of
+        Just e' -> rE e'
+        Nothing -> pure e
+bM (Let l (n, e') e) = do
+    e'B <- bM e'
+    eB <- bM e
+    pure $ Let l (n, e'B) eB
+bM (Tup l es) = Tup l <$> traverse bM es; bM (Arr l es) = Arr l <$> traverse bM es
+bM (Anchor l es) = Anchor l <$> traverse bM es; bM (OptionVal l es) = OptionVal l <$> traverse bM es
+bM (Lam l n e) = Lam l n <$> bM e
+bM (Implicit l e) = Implicit l <$> bM e
+bM (Guarded l e0 e1) = Guarded l <$> bM e0 <*> bM e1
+bM (Cond l p e0 e1) = Cond l <$> bM p <*> bM e0 <*> bM e1
+bM e@Column{} = pure e; bM e@IParseCol{} = pure e; bM e@FParseCol{} = pure e; bM e@AllField{} = pure e
+bM e@LastField{} = pure e; bM e@Field{} = pure e; bM e@ParseCol{} = pure e; bM e@AllColumn{} = pure e; bM e@RC{} = pure e
+bM e@ILit{} = pure e; bM e@FLit{} = pure e; bM e@StrLit{} = pure e; bM e@RegexLit{} = pure e; bM e@BLit{} = pure e
+bM e@BB{} = pure e; bM e@NB{} = pure e; bM e@UB{} = pure e; bM e@TB{} = pure e
+bM ResVar{} = desugar; bM Dfn{} = desugar; bM Paren{} = desugar
+
+iE :: E T -> RM T (E T)
+iE e@NB{} = pure e; iE e@UB{} = pure e; iE e@BB{} = pure e; iE e@TB{} = pure e
+iE e@Column{} = pure e; iE e@ParseCol{} = pure e; iE e@IParseCol{} = pure e; iE e@FParseCol{} = pure e
+iE e@Field{} = pure e; iE e@LastField{} = pure e; iE e@AllField{} = pure e; iE e@AllColumn{} = pure e
+iE e@ILit{} = pure e; iE e@FLit{} = pure e; iE e@BLit{} = pure e; iE e@StrLit{} = pure e
+iE e@RegexLit{} = pure e; iE e@RC{} = pure e
+iE (EApp t e e') = EApp t <$> iE e <*> iE e'
+iE (Guarded t p e) = Guarded t <$> iE p <*> iE e
+iE (Implicit t e) = Implicit t <$> iE e
+iE (Lam t n e) = Lam t n <$> iE e
+iE (Tup t es) = Tup t <$> traverse iE es
+iE (Arr t es) = Arr t <$> traverse iE es
+iE (Anchor t es) = Anchor t <$> traverse iE es
+iE (OptionVal t es) = OptionVal t <$> traverse iE es
+iE (Cond t p e e') = Cond t <$> iE p <*> iE e <*> iE e'
+iE (Let _ (n, e') e) = do
+    eI <- iE e'
+    modify (bind n eI) *> iE e
+iE e@(Var t (Nm _ (U i) _)) = do
+    st <- gets binds
+    case IM.lookup i st of
+        Just e' -> do {er <- rE e'; pure $ fmap (aT (match (eLoc er) t)) er}
+        Nothing -> pure e
+iE Dfn{} = desugar; iE Paren{} = desugar; iE ResVar{} = desugar
diff --git a/src/Data/List/Ext.hs b/src/Data/List/Ext.hs
deleted file mode 100644
--- a/src/Data/List/Ext.hs
+++ /dev/null
@@ -1,13 +0,0 @@
-module Data.List.Ext ( imap
-                     , ifilter'
-                     , prior
-                     ) where
-
-prior :: (a -> a -> b) -> [a] -> [b]
-prior op xs = zipWith op (tail xs) xs
-
-imap :: (Int -> a -> b) -> [a] -> [b]
-imap f xs = fmap (uncurry f) (zip [1..] xs)
-
-ifilter' :: (Int -> a -> Bool) -> [a] -> [(Int, a)]
-ifilter' p xs = filter (uncurry p) (zip [1..] xs)
diff --git a/src/Data/Vector/Ext.hs b/src/Data/Vector/Ext.hs
deleted file mode 100644
--- a/src/Data/Vector/Ext.hs
+++ /dev/null
@@ -1,7 +0,0 @@
-module Data.Vector.Ext ( priorM_
-                       ) where
-
-import qualified Data.Vector as V
-
-priorM_ :: Monad m => (a -> a -> m b) -> V.Vector a -> m ()
-priorM_ op xs = V.zipWithM_ op (V.tail xs) xs
diff --git a/src/File.hs b/src/File.hs
new file mode 100644
--- /dev/null
+++ b/src/File.hs
@@ -0,0 +1,139 @@
+module File ( tcIO
+            , tySrc
+            , runOnHandle
+            , runOnFile
+            , exprEval
+            ) where
+
+import           A
+import           A.I
+import           Control.Applicative        ((<|>))
+import           Control.Exception          (Exception, throw, throwIO)
+import           Control.Monad              ((<=<))
+import           Control.Monad.IO.Class     (liftIO)
+import           Control.Monad.State.Strict (StateT, get, put, runStateT)
+import           Control.Recursion          (cata, embed)
+import           Data.Bifunctor             (second)
+import qualified Data.ByteString            as BS
+import qualified Data.ByteString.Lazy       as BSL
+import qualified Data.ByteString.Lazy.Char8 as ASCIIL
+import           Data.Foldable              (traverse_)
+import           Data.Functor               (($>))
+import qualified Data.Text                  as T
+import           Data.Text.Encoding         (encodeUtf8)
+import qualified Data.Text.IO               as TIO
+import           Data.Tuple                 (swap)
+import           Include
+import           Jacinda.Backend.Const
+import           Jacinda.Backend.P
+import           Jacinda.Check.Field
+import           Jacinda.Regex
+import           L
+import           Parser
+import           Parser.Rw
+import           R
+import           Regex.Rure                 (RurePtr)
+import           System.IO                  (Handle)
+import           Ty
+
+parseLib :: [FilePath] -> FilePath -> StateT AlexUserState IO [D AlexPosn]
+parseLib incls fp = do
+    contents <- liftIO $ TIO.readFile =<< resolveImport incls fp
+    st <- get
+    case parseLibWithCtx contents st of
+        Left err              -> liftIO (throwIO err)
+        Right (st', ([], ds)) -> put st' $> (rwD <$> ds)
+        Right (st', (is, ds)) -> do { put st' ; dss <- traverse (parseLib incls) is ; pure (concat dss ++ fmap rwD ds) }
+
+parseE :: [FilePath] -> T.Text -> StateT AlexUserState IO (Program AlexPosn)
+parseE incls bs = do
+    st <- get
+    case parseWithCtx bs st of
+        Left err -> liftIO $ throwIO err
+        Right (st', (is, Program ds e)) -> do
+            put st'
+            dss <- traverse (parseLib incls) is
+            pure $ Program (concat dss ++ fmap rwD ds) (rwE e)
+
+-- | Parse + rename (decls)
+parseEWithMax :: [FilePath] -> T.Text -> IO (Program AlexPosn, Int)
+parseEWithMax incls bsl = uncurry rP . swap . second fst3 <$> runStateT (parseE incls bsl) alexInitUserState
+    where fst3 (x, _, _) = x
+
+parseWithMax' :: T.Text -> Either (ParseError AlexPosn) (Program AlexPosn, Int)
+parseWithMax' = fmap (uncurry rP . second (rwP . snd)) . parseWithMax
+
+type FileBS = BS.ByteString
+
+compileR :: FileBS
+         -> E T
+         -> E T
+compileR fp = cata a where
+    a (RegexLitF _ rrϵ) = RC (compileDefault rrϵ)
+    a (NBF _ Fp)        = mkStr fp
+    a x                 = embed x
+
+exprEval :: T.Text -> E T
+exprEval src =
+    case parseWithMax' src of
+        Left err -> throw err
+        Right (ast, m) ->
+            let (typed, i) = yeet $ runTyM m (tyP ast)
+                (inlined, j) = ib i typed
+            in eB j pure (compileR (error "nf not defined.") inlined)
+
+compileFS :: Maybe T.Text -> RurePtr
+compileFS (Just bs) = compileDefault (encodeUtf8 bs)
+compileFS Nothing   = defaultRurePtr
+
+runOnBytes :: [FilePath]
+           -> FilePath -- ^ Data file name, for @nf@
+           -> T.Text -- ^ Program
+           -> Maybe T.Text -- ^ Field separator
+           -> BSL.ByteString
+           -> IO ()
+runOnBytes incls fp src cliFS contents = do
+    incls' <- defaultIncludes <*> pure incls
+    (ast, m) <- parseEWithMax incls' src
+    (typed, i) <- yIO $ runTyM m (tyP ast)
+    let (eI, j) = ib i typed
+    m'Throw $ cF eI
+    cont <- yIO $ runJac (compileFS (cliFS <|> getFS ast)) (flushD typed) j (compileR (encodeUtf8 $ T.pack fp) eI)
+    cont $ fmap BSL.toStrict (ASCIIL.lines contents)
+
+runOnHandle :: [FilePath]
+            -> T.Text -- ^ Program
+            -> Maybe T.Text -- ^ Field separator
+            -> Handle
+            -> IO ()
+runOnHandle is src cliFS = runOnBytes is "(runOnBytes)" src cliFS <=< BSL.hGetContents
+
+runOnFile :: [FilePath]
+          -> T.Text
+          -> Maybe T.Text
+          -> FilePath
+          -> IO ()
+runOnFile is e fs fp = runOnBytes is fp e fs =<< BSL.readFile fp
+
+tcIO :: [FilePath] -> T.Text -> IO ()
+tcIO incls src = do
+    incls' <- defaultIncludes <*> pure incls
+    (ast, m) <- parseEWithMax incls' src
+    (pT, i) <- yIO $ runTyM m (tyP ast)
+    let (eI, _) = ib i pT
+    m'Throw $ cF eI
+
+tySrc :: T.Text -> T
+tySrc src =
+    case parseWithMax' src of
+        Right (ast, m) -> yeet $ fst <$> runTyM m (tyOf (expr ast))
+        Left err       -> throw err
+
+m'Throw :: Exception e => Maybe e -> IO ()
+m'Throw = traverse_ throwIO
+
+yIO :: Exception e => Either e a -> IO a
+yIO = either throwIO pure
+
+yeet :: Exception e => Either e a -> a
+yeet = either throw id
diff --git a/src/Include.hs b/src/Include.hs
new file mode 100644
--- /dev/null
+++ b/src/Include.hs
@@ -0,0 +1,36 @@
+module Include ( defaultIncludes
+               , resolveImport
+               ) where
+
+import           Control.Exception  (Exception, throwIO)
+import           Control.Monad      (filterM)
+import           Data.List.Split    (splitWhen)
+import           Data.Maybe         (listToMaybe)
+import           Paths_jacinda      (getDataDir)
+import           System.Directory   (doesFileExist, getCurrentDirectory)
+import           System.Environment (lookupEnv)
+import           System.FilePath    ((</>))
+
+data ImportError = FileNotFound !FilePath ![FilePath] deriving (Show)
+
+instance Exception ImportError where
+
+defaultIncludes :: IO ([FilePath] -> [FilePath])
+defaultIncludes = do
+    path <- jacPath
+    d <- getDataDir
+    dot <- getCurrentDirectory
+    pure $ (dot:).(d:).(++path)
+
+jacPath :: IO [FilePath]
+jacPath = maybe [] splitEnv <$> lookupEnv "JAC_PATH"
+
+splitEnv :: String -> [FilePath]
+splitEnv = splitWhen (== ':')
+
+resolveImport :: [FilePath] -- ^ Places to look
+              -> FilePath
+              -> IO FilePath
+resolveImport incl fp =
+    maybe (throwIO $ FileNotFound fp incl) pure . listToMaybe
+        =<< (filterM doesFileExist . fmap (</> fp) $ incl)
diff --git a/src/Intern/Name.hs b/src/Intern/Name.hs
deleted file mode 100644
--- a/src/Intern/Name.hs
+++ /dev/null
@@ -1,30 +0,0 @@
-{-# LANGUAGE DeriveFunctor #-}
-
-module Intern.Name ( Name (..)
-                   , TyName
-                   , eqName
-                   ) where
-
-import qualified Data.Text     as T
-import           Intern.Unique
-import           Prettyprinter (Pretty (pretty))
-
-data Name a = Name { name   :: T.Text
-                   , unique :: !Unique
-                   , loc    :: a
-                   } deriving (Functor)
-
--- for testing
-eqName :: Name a -> Name a -> Bool
-eqName (Name n _ _) (Name n' _ _) = n == n'
-
-instance Eq (Name a) where
-    (==) (Name _ u _) (Name _ u' _) = u == u'
-
-instance Pretty (Name a) where
-    pretty (Name t _ _) = pretty t
-
-instance Ord (Name a) where
-    compare (Name _ u _) (Name _ u' _) = compare u u'
-
-type TyName = Name
diff --git a/src/Intern/Unique.hs b/src/Intern/Unique.hs
deleted file mode 100644
--- a/src/Intern/Unique.hs
+++ /dev/null
@@ -1,5 +0,0 @@
-module Intern.Unique ( Unique (..)
-                     ) where
-
-newtype Unique = Unique { unUnique :: Int }
-    deriving (Eq, Ord)
diff --git a/src/Jacinda/AST.hs b/src/Jacinda/AST.hs
deleted file mode 100644
--- a/src/Jacinda/AST.hs
+++ /dev/null
@@ -1,460 +0,0 @@
-{-# LANGUAGE DeriveFoldable    #-}
-{-# LANGUAGE DeriveFunctor     #-}
-{-# LANGUAGE DeriveGeneric     #-}
-{-# LANGUAGE DeriveTraversable #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TypeFamilies      #-}
-
-module Jacinda.AST ( E (..)
-                   , T (..)
-                   , TB (..)
-                   , BBin (..)
-                   , BTer (..)
-                   , BUn (..)
-                   , K (..)
-                   , DfnVar (..)
-                   , D (..)
-                   , Program (..)
-                   , C (..)
-                   , N (..)
-                   , mapExpr
-                   , getFS
-                   -- * Base functors
-                   , EF (..)
-                   ) where
-
-import           Control.Recursion  (Base, Corecursive, Recursive)
-import qualified Data.ByteString    as BS
-import           Data.Maybe         (listToMaybe)
-import           Data.Semigroup     ((<>))
-import           Data.Text.Encoding (decodeUtf8)
-import qualified Data.Vector        as V
-import           GHC.Generics       (Generic)
-import           Intern.Name
-import           Prettyprinter      (Doc, Pretty (..), braces, brackets, concatWith, encloseSep, flatAlt, group, hardline, indent, parens, tupled, (<+>))
-import           Regex.Rure         (RurePtr)
-
-infixr 6 <#>
-infixr 6 <##>
-
-(<#>) :: Doc a -> Doc a -> Doc a
-(<#>) x y = x <> hardline <> y
-
-(<##>) :: Doc a -> Doc a -> Doc a
-(<##>) x y = x <> hardline <> hardline <> y
-
--- kind
-data K = Star
-       | KArr K K
-       deriving (Eq, Ord)
-
-instance Pretty K where
-    pretty Star         = "★"
-    pretty (KArr k0 k1) = parens (pretty k0 <+> "⟶" <+> pretty k1)
-
-data TB = TyInteger
-        | TyFloat
-        | TyDate
-        | TyStr | TyR
-        | TyStream
-        | TyVec
-        | TyBool
-        | TyOption
-        | TyUnit
-        -- TODO: convert float to int
-        deriving (Eq, Ord)
-
--- unicode mathematical angle bracket
-tupledByFunky :: Doc ann -> [Doc ann] -> Doc ann
-tupledByFunky sep = group . encloseSep (flatAlt "⟨ " "⟨") (flatAlt " ⟩" "⟩") sep
-
-tupledBy :: Doc ann -> [Doc ann] -> Doc ann
-tupledBy sep = group . encloseSep (flatAlt "( " "(") (flatAlt " )" ")") sep
-
-jacTup :: Pretty a => [a] -> Doc ann
-jacTup = tupledBy " . " . fmap pretty
-
--- type
-data T a = TyNamed { tLoc :: a, tyName :: TyName a }
-         | TyB { tLoc :: a, tyBuiltin :: TB }
-         | TyApp { tLoc :: a, tyApp0 :: T a, tyApp1 :: T a }
-         | TyArr { tLoc :: a, tyArr0 :: T a, tyArr1 :: T a }
-         | TyVar { tLoc :: a, tyVar :: Name a }
-         | TyTup { tLoc :: a, tyTups :: [T a] } -- in practice, parse only >1
-         deriving (Eq, Ord, Functor) -- this is so we can store consntraints in a set, not alpha-equiv. or anything
-         -- TODO: type vars, products...
-
-instance Pretty TB where
-    pretty TyInteger = "Integer"
-    pretty TyStream  = "Stream"
-    pretty TyBool    = "Bool"
-    pretty TyStr     = "Str"
-    pretty TyFloat   = "Float"
-    pretty TyDate    = "Date"
-    pretty TyVec     = "List"
-    pretty TyOption  = "Optional"
-    pretty TyUnit    = "𝟙"
-    pretty TyR       = "Regex"
-
-instance Pretty (T a) where
-    pretty (TyB _ b)        = pretty b
-    pretty (TyApp _ ty ty') = pretty ty <+> pretty ty'
-    pretty (TyVar _ n)      = pretty n
-    pretty (TyArr _ ty ty') = pretty ty <+> "⟶" <+> pretty ty'
-    pretty (TyTup _ tys)    = jacTup tys
-    pretty (TyNamed _ tn)   = pretty tn
-
-instance Show (T a) where
-    show = show . pretty
-
--- unary
-data BUn = Tally -- length of string field
-         | Const
-         | Not -- ^ Boolean
-         | At Int
-         | Select Int
-         | IParse
-         | FParse
-         | Parse
-         | Floor
-         | Ceiling
-         | Some
-         | Dedup
-         | CatMaybes
-         | Negate
-         | TallyList -- length of vector
-         deriving (Eq)
-
-instance Pretty BUn where
-    pretty Tally      = "#"
-    pretty Const      = "[:"
-    pretty Not        = "!"
-    pretty (At i)     = "." <> pretty i
-    pretty (Select i) = "->" <> pretty i
-    pretty IParse     = ":i"
-    pretty FParse     = ":f"
-    pretty Floor      = "floor"
-    pretty Ceiling    = "ceil"
-    pretty Parse      = ":"
-    pretty Some       = "Some"
-    pretty Dedup      = "~."
-    pretty CatMaybes  = ".?"
-    pretty Negate     = "-."
-    pretty TallyList  = "#*"
-
--- ternary
-data BTer = ZipW
-          | Fold
-          | Scan
-          | Substr
-          | Option
-          | Captures
-          | AllCaptures
-          deriving (Eq)
-
-instance Pretty BTer where
-    pretty ZipW        = ","
-    pretty Fold        = "|"
-    pretty Scan        = "^"
-    pretty Substr      = "substr"
-    pretty Option      = "option"
-    pretty Captures    = "~*"
-    pretty AllCaptures = "captures"
-
--- builtin
-data BBin = Plus
-          | Times
-          | Div
-          | Minus
-          | Eq
-          | Neq
-          | Geq
-          | Gt
-          | Lt
-          | Leq
-          | Map
-          | Matches -- ^ @/pat/ ~ 'string'@
-          | NotMatches
-          | And
-          | Or
-          | Min
-          | Max
-          | Split
-          | Splitc
-          | Prior
-          | Filter
-          | Sprintf
-          | Match
-          | MapMaybe
-          | Fold1
-          -- TODO: floor functions, sqrt, sin, cos, exp. (power)
-          deriving (Eq)
-
-instance Pretty BBin where
-    pretty Plus       = "+"
-    pretty Times      = "*"
-    pretty Div        = "%"
-    pretty Minus      = "-"
-    pretty Eq         = "="
-    pretty Gt         = ">"
-    pretty Lt         = "<"
-    pretty Geq        = ">="
-    pretty Leq        = "<="
-    pretty Neq        = "!="
-    pretty Map        = "\""
-    pretty Matches    = "~"
-    pretty NotMatches = "!~"
-    pretty And        = "&"
-    pretty Or         = "||"
-    pretty Max        = "max"
-    pretty Min        = "min"
-    pretty Prior      = "\\."
-    pretty Filter     = "#."
-    pretty Split      = "split"
-    pretty Splitc     = "splitc"
-    pretty Sprintf    = "sprintf"
-    pretty Match      = "match"
-    pretty MapMaybe   = ":?"
-    pretty Fold1      = "|>"
-
-data DfnVar = X | Y deriving (Eq)
-
-instance Pretty DfnVar where
-    pretty X = "x"
-    pretty Y = "y"
-
--- 0-ary
-data N = Ix
-       | Nf
-       | None
-       | Fp
-       deriving (Eq)
-
--- expression
-data E a = Column { eLoc :: a, col :: Int }
-         | IParseCol { eLoc :: a, col :: Int } -- always a column
-         | FParseCol { eLoc :: a, col :: Int }
-         | ParseCol { eLoc :: a, col :: Int }
-         | Field { eLoc :: a, eField :: Int }
-         | LastField { eLoc :: a }
-         | AllField { eLoc :: a } -- ^ Think @$0@ in awk.
-         | AllColumn { eLoc :: a } -- ^ Think @$0@ in awk.
-         | EApp { eLoc :: a, eApp0 :: E a, eApp1 :: E a }
-         | Guarded { eLoc :: a, eP :: E a, eGuarded :: E a }
-         | Implicit { eLoc :: a, eImplicit :: E a }
-         | Let { eLoc :: a, eBind :: (Name a, E a), eE :: E a }
-         -- TODO: literals type (make pattern matching easier down the road)
-         | Var { eLoc :: a, eVar :: Name a }
-         | IntLit { eLoc :: a, eInt :: !Integer }
-         | BoolLit { eLoc :: a, eBool :: !Bool }
-         | StrLit { eLoc :: a, eStr :: BS.ByteString }
-         | RegexLit { eLoc :: a, eRr :: BS.ByteString }
-         | FloatLit { eLoc :: a, eFloat :: !Double }
-         | Lam { eLoc :: a, eBound :: Name a, lamE :: E a }
-         | Dfn { eLoc :: a, eDfn :: E a }
-         | BBuiltin { eLoc :: a, eBin :: BBin }
-         | TBuiltin { eLoc :: a, eTer :: BTer }
-         | UBuiltin { eLoc :: a, eUn :: BUn }
-         | NBuiltin { eLoc :: a, eNil :: N }
-         | Tup { eLoc :: a, esTup :: [E a] }
-         | ResVar { eLoc :: a, dfnVar :: DfnVar }
-         | RegexCompiled RurePtr -- holds compiled regex after normalization
-         | Arr { eLoc :: a, elems :: V.Vector (E a) }
-         | Anchor { eLoc :: a, eAnchored :: [E a] }
-         | Paren { eLoc :: a, eExpr :: E a }
-         | OptionVal { eLoc :: a, eMaybe :: Maybe (E a) }
-         | Cond { eLoc :: a, eIf :: E a, eThen :: E a, eElse :: E a }
-         deriving (Functor, Generic)
-
-instance Recursive (E a) where
-
-instance Corecursive (E a) where
-
-data EF a x = ColumnF a Int
-            | IParseColF a Int
-            | FParseColF a Int
-            | ParseColF a Int
-            | FieldF a Int
-            | LastFieldF a
-            | AllFieldF a
-            | AllColumnF a
-            | EAppF a x x
-            | GuardedF a x x
-            | ImplicitF a x
-            | LetF a (Name a, x) x
-            | VarF a (Name a)
-            | IntLitF a Integer
-            | BoolLitF a Bool
-            | StrLitF a BS.ByteString
-            | RegexLitF a BS.ByteString
-            | FloatLitF a Double
-            | LamF a (Name a) x
-            | DfnF a x
-            | BBuiltinF a BBin
-            | TBuiltinF a BTer
-            | UBuiltinF a BUn
-            | NBuiltinF a N
-            | TupF a [x]
-            | ResVarF a DfnVar
-            | RegexCompiledF RurePtr
-            | ArrF a (V.Vector x)
-            | AnchorF a [x]
-            | ParenF a x
-            | OptionValF a (Maybe x)
-            | CondF a x x x
-            deriving (Generic, Functor, Foldable, Traversable)
-
-type instance Base (E a) = (EF a)
-
-instance Pretty N where
-    pretty Ix   = "ix"
-    pretty Nf   = "nf"
-    pretty None = "None"
-    pretty Fp   = "fp"
-
-instance Pretty (E a) where
-    pretty (Column _ i)                                                 = "$" <> pretty i
-    pretty AllColumn{}                                                  = "$0"
-    pretty (IParseCol _ i)                                              = "$" <> pretty i <> ":i"
-    pretty (FParseCol _ i)                                              = "$" <> pretty i <> ":f"
-    pretty (ParseCol _ i)                                               = "$" <> pretty i <> ":"
-    pretty AllField{}                                                   = "`0"
-    pretty (Field _ i)                                                  = "`" <> pretty i
-    pretty LastField{}                                                  = "`*"
-    pretty (EApp _ (EApp _ (BBuiltin _ Prior) e) e')                    = pretty e <> "\\." <+> pretty e'
-    pretty (EApp _ (EApp _ (BBuiltin _ Max) e) e')                      = "max" <+> pretty e <+> pretty e'
-    pretty (EApp _ (EApp _ (BBuiltin _ Min) e) e')                      = "min" <+> pretty e <+> pretty e'
-    pretty (EApp _ (EApp _ (BBuiltin _ Split) e) e')                    = "split" <+> pretty e <+> pretty e'
-    pretty (EApp _ (EApp _ (BBuiltin _ Splitc) e) e')                   = "splitc" <+> pretty e <+> pretty e'
-    pretty (EApp _ (EApp _ (BBuiltin _ Match) e) e')                    = "match" <+> pretty e <+> pretty e'
-    pretty (EApp _ (EApp _ (BBuiltin _ Sprintf) e) e')                  = "sprintf" <+> pretty e <+> pretty e'
-    pretty (EApp _ (EApp _ (BBuiltin _ Map) e) e')                      = pretty e <> "\"" <> pretty e'
-    pretty (EApp _ (EApp _ (BBuiltin _ b) e) e')                        = pretty e <+> pretty b <+> pretty e'
-    pretty (EApp _ (BBuiltin _ b) e)                                    = parens (pretty e <> pretty b)
-    pretty (EApp _ (EApp _ (EApp _ (TBuiltin _ Fold) e) e') e'')        = pretty e <> "|" <> pretty e' <+> pretty e''
-    pretty (EApp _ (EApp _ (EApp _ (TBuiltin _ Scan) e) e') e'')        = pretty e <> "^" <> pretty e' <+> pretty e''
-    pretty (EApp _ (EApp _ (EApp _ (TBuiltin _ ZipW) op) e') e'')       = "," <> pretty op <+> pretty e' <+> pretty e''
-    pretty (EApp _ (EApp _ (EApp _ (TBuiltin _ Substr) e) e') e'')      = "substr" <+> pretty e <+> pretty e' <+> pretty e''
-    pretty (EApp _ (EApp _ (EApp _ (TBuiltin _ Option) e) e') e'')      = "option" <+> pretty e <+> pretty e' <+> pretty e''
-    pretty (EApp _ (EApp _ (EApp _ (TBuiltin _ AllCaptures) e) e') e'') = "captures" <+> pretty e <+> pretty e' <+> pretty e''
-    pretty (EApp _ (EApp _ (EApp _ (TBuiltin _ Captures) e) e') e'')    = pretty e <+> "~*" <+> pretty e' <+> pretty e''
-    pretty (EApp _ (UBuiltin _ (At i)) e)                               = pretty e <> "." <> pretty i
-    pretty (EApp _ (UBuiltin _ (Select i)) e)                           = pretty e <> "->" <> pretty i
-    pretty (EApp _ (UBuiltin _ IParse) e')                              = pretty e' <> ":i"
-    pretty (EApp _ (UBuiltin _ FParse) e')                              = pretty e' <> ":f"
-    pretty (EApp _ (UBuiltin _ Parse) e')                               = pretty e' <> ":"
-    pretty (EApp _ e@UBuiltin{} e')                                     = pretty e <> pretty e'
-    pretty (EApp _ e e')                                                = pretty e <+> pretty e'
-    pretty (Var _ n)                                                    = pretty n
-    pretty (IntLit _ i)                                                 = pretty i
-    pretty (RegexLit _ rr)                                              = "/" <> pretty (decodeUtf8 rr) <> "/"
-    pretty (FloatLit _ f)                                               = pretty f
-    pretty (BoolLit _ True)                                             = "#t"
-    pretty (BoolLit _ False)                                            = "#f"
-    pretty (BBuiltin _ b)                                               = parens (pretty b)
-    pretty (UBuiltin _ u)                                               = pretty u
-    pretty (StrLit _ bstr)                                              = pretty (decodeUtf8 bstr)
-    pretty (ResVar _ x)                                                 = pretty x
-    pretty (Tup _ es)                                                   = jacTup es
-    pretty (Lam _ n e)                                                  = parens ("λ" <> pretty n <> "." <+> pretty e)
-    pretty (Dfn _ e)                                                    = brackets (pretty e)
-    pretty (Guarded _ p e)                                              = braces (pretty p) <> braces (pretty e)
-    pretty (Implicit _ e)                                               = braces ("|" <+> pretty e)
-    pretty (NBuiltin _ n)                                               = pretty n
-    pretty RegexCompiled{}                                              = "(compiled regex)"
-    pretty (Let _ (n, b) e)                                             = "let" <+> "val" <+> pretty n <+> ":=" <+> pretty b <+> "in" <+> pretty e <+> "end"
-    pretty (Paren _ e)                                                  = parens (pretty e)
-    pretty (Arr _ es)                                                   = tupledByFunky "," (V.toList $ pretty <$> es)
-    pretty (Anchor _ es)                                                = "&" <> tupledBy "." (pretty <$> es)
-    pretty (OptionVal _ (Just e))                                       = "Some" <+> pretty e
-    pretty (OptionVal _ Nothing)                                        = "None"
-    pretty (Cond _ e0 e1 e2)                                            = "if" <+> pretty e0 <+> "then" <+> pretty e1 <+> "else" <+> pretty e2
-
-instance Show (E a) where
-    show = show . pretty
-
--- for tests
-instance Eq (E a) where
-    (==) (Column _ i) (Column _ j)              = i == j
-    (==) (IParseCol _ i) (IParseCol _ j)        = i == j
-    (==) (FParseCol _ i) (FParseCol _ j)        = i == j
-    (==) (Field _ i) (Field _ j)                = i == j
-    (==) LastField{} LastField{}                = True
-    (==) AllColumn{} AllColumn{}                = True
-    (==) AllField{} AllField{}                  = True
-    (==) (EApp _ e0 e1) (EApp _ e0' e1')        = e0 == e0' && e1 == e1'
-    (==) (Guarded _ p e) (Guarded _ p' e')      = p == p' && e == e'
-    (==) (Implicit _ e) (Implicit _ e')         = e == e'
-    (==) (Let _ (n, eϵ) e) (Let _ (n', eϵ') e') = eqName n n' && e == e' && eϵ == eϵ'
-    (==) (Var _ n) (Var _ n')                   = eqName n n'
-    (==) (Lam _ n e) (Lam _ n' e')              = eqName n n' && e == e'
-    (==) (IntLit _ i) (IntLit _ j)              = i == j
-    (==) (FloatLit _ u) (FloatLit _ v)          = u == v
-    (==) (StrLit _ str) (StrLit _ str')         = str == str'
-    (==) (RegexLit _ rr) (RegexLit _ rr')       = rr == rr'
-    (==) (BoolLit _ b) (BoolLit _ b')           = b == b'
-    (==) (BBuiltin _ b) (BBuiltin _ b')         = b == b'
-    (==) (TBuiltin _ b) (TBuiltin _ b')         = b == b'
-    (==) (UBuiltin _ unOp) (UBuiltin _ unOp')   = unOp == unOp'
-    (==) (NBuiltin _ x) (NBuiltin _ y)          = x == y
-    (==) (Tup _ es) (Tup _ es')                 = es == es'
-    (==) (ResVar _ x) (ResVar _ y)              = x == y
-    (==) (Dfn _ f) (Dfn _ g)                    = f == g -- we're testing for lexical equivalence
-    (==) RegexCompiled{} _                      = error "Cannot compare compiled regex!"
-    (==) _ RegexCompiled{}                      = error "Cannot compare compiled regex!"
-    (==) (Paren _ e) e'                         = e == e'
-    (==) e (Paren _ e')                         = e == e'
-    (==) _ _                                    = False
-
-data C = IsNum
-       | IsEq
-       | IsOrd
-       | IsParseable
-       | IsSemigroup
-       | Functor -- ^ For map (@"@)
-       | Foldable
-       | IsPrintf
-       | HasField Int (T K)
-       | Witherable
-       deriving (Eq, Ord)
-
-instance Pretty C where
-    pretty IsNum           = "Num"
-    pretty IsEq            = "Eq"
-    pretty IsOrd           = "Ord"
-    pretty IsParseable     = "Parseable"
-    pretty IsSemigroup     = "Semigroup"
-    pretty Functor         = "Functor"
-    pretty Foldable        = "Foldable"
-    pretty IsPrintf        = "Printf"
-    pretty (HasField i ty) = "HasField" <+> pretty i <+> "~" <+> pretty ty
-    pretty Witherable      = "Witherable"
-
-instance Show C where
-    show = show . pretty
-
--- decl
-data D a = SetFS BS.ByteString
-         | FunDecl (Name a) [Name a] (E a)
-         deriving (Functor)
-
-instance Pretty (D a) where
-    pretty (SetFS bs)       = ":set" <+> "/" <> pretty (decodeUtf8 bs) <> "/"
-    pretty (FunDecl n ns e) = "fn" <+> pretty n <> tupled (pretty <$> ns) <+> ":=" <#> indent 2 (pretty e <> ";")
-
--- TODO: fun decls (type decls)
-data Program a = Program { decls :: [D a], expr :: E a } deriving (Functor)
-
-instance Pretty (Program a) where
-    pretty (Program ds e) = concatWith (<##>) (pretty <$> ds) <##> pretty e
-
-instance Show (Program a) where
-    show = show . pretty
-
-getFS :: Program a -> Maybe BS.ByteString
-getFS (Program ds _) = listToMaybe (concatMap go ds) where
-    go (SetFS bs) = [bs]
-    go _          = []
-
-mapExpr :: (E a -> E a) -> Program a -> Program a
-mapExpr f (Program ds e) = Program ds (f e)
diff --git a/src/Jacinda/Backend/Const.hs b/src/Jacinda/Backend/Const.hs
new file mode 100644
--- /dev/null
+++ b/src/Jacinda/Backend/Const.hs
@@ -0,0 +1,17 @@
+module Jacinda.Backend.Const ( mkI, mkF, mkStr, mkB ) where
+
+import           A
+import qualified Data.ByteString as BS
+import           Ty.Const
+
+mkI :: Integer -> E T
+mkI = ILit tyI
+
+mkF :: Double -> E T
+mkF = FLit tyF
+
+mkB :: Bool -> E T
+mkB = BLit tyB
+
+mkStr :: BS.ByteString -> E T
+mkStr = StrLit tyStr
diff --git a/src/Jacinda/Backend/Normalize.hs b/src/Jacinda/Backend/Normalize.hs
deleted file mode 100644
--- a/src/Jacinda/Backend/Normalize.hs
+++ /dev/null
@@ -1,483 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module Jacinda.Backend.Normalize ( eClosed
-                                 , closedProgram
-                                 , readDigits
-                                 , readFloat
-                                 , mkI
-                                 , mkF
-                                 , mkStr
-                                 , parseAsEInt
-                                 , parseAsF
-                                 , the
-                                 , asTup
-                                 , EvalError (..)
-                                 -- * Monad
-                                 , runEvalM
-                                 , eNorm
-                                 ) where
-
-import           Control.Exception          (Exception, throw)
-import           Control.Monad.State.Strict (State, evalState, gets, modify)
-import qualified Data.ByteString            as BS
-import qualified Data.ByteString.Char8      as ASCII
-import           Data.Foldable              (traverse_)
-import qualified Data.IntMap                as IM
-import           Data.Semigroup             ((<>))
-import qualified Data.Vector                as V
-import           Data.Word                  (Word8)
-import           Intern.Name
-import           Intern.Unique
-import           Jacinda.AST
-import           Jacinda.Backend.Printf
-import           Jacinda.Regex
-import           Jacinda.Rename
-import           Jacinda.Ty.Const
-import           Regex.Rure                 (RureMatch (..))
-
-data EvalError = EmptyFold
-               | IndexOutOfBounds Int
-               deriving (Show)
-
-instance Exception EvalError where
-
-mkI :: Integer -> E (T K)
-mkI = IntLit tyI
-
-mkF :: Double -> E (T K)
-mkF = FloatLit tyF
-
-mkStr :: BS.ByteString -> E (T K)
-mkStr = StrLit tyStr
-
-parseAsEInt :: BS.ByteString -> E (T K)
-parseAsEInt = mkI . readDigits
-
-parseAsF :: BS.ByteString -> E (T K)
-parseAsF = FloatLit tyF . readFloat
-
-readDigits :: BS.ByteString -> Integer
-readDigits = ASCII.foldl' (\seed x -> 10 * seed + f x) 0
-    where f '0' = 0
-          f '1' = 1
-          f '2' = 2
-          f '3' = 3
-          f '4' = 4
-          f '5' = 5
-          f '6' = 6
-          f '7' = 7
-          f '8' = 8
-          f '9' = 9
-          f c   = error (c:" is not a valid digit!")
-
-the :: BS.ByteString -> Word8
-the bs = case BS.uncons bs of
-    Nothing     -> error "Empty splitc char!"
-    Just (c,"") -> c
-    Just _      -> error "Splitc takes only one char!"
-
-readFloat :: BS.ByteString -> Double
-readFloat = read . ASCII.unpack
-
-desugar :: a
-desugar = error "Should have been desugared by this stage."
-
-data LetCtx = LetCtx { binds    :: IM.IntMap (E (T K))
-                     , renames_ :: Renames
-                     }
-
-instance HasRenames LetCtx where
-    rename f s = fmap (\x -> s { renames_ = x }) (f (renames_ s))
-
-mapBinds :: (IM.IntMap (E (T K)) -> IM.IntMap (E (T K))) -> LetCtx -> LetCtx
-mapBinds f (LetCtx b r) = LetCtx (f b) r
-
-type EvalM = State LetCtx
-
-mkLetCtx :: Int -> LetCtx
-mkLetCtx i = LetCtx IM.empty (Renames i IM.empty)
-
-runEvalM :: Int
-         -> EvalM a
-         -> a
-runEvalM i = flip evalState (mkLetCtx i)
-
-eClosed :: Int
-        -> E (T K)
-        -> E (T K)
-eClosed i = runEvalM i . eNorm
-
-closedProgram :: Int
-              -> Program (T K)
-              -> E (T K)
-closedProgram i (Program ds e) = runEvalM i $
-    traverse_ processDecl ds *> eNorm e
-
-processDecl :: D (T K)
-            -> EvalM ()
-processDecl SetFS{} = pure ()
-processDecl (FunDecl (Name _ (Unique i) _) [] e) = do
-    e' <- eNorm e
-    modify (mapBinds (IM.insert i e'))
-
-asTup :: Maybe RureMatch -> E (T K)
-asTup Nothing                = OptionVal undefined Nothing
-asTup (Just (RureMatch s e)) = OptionVal undefined (Just $ Tup undefined (mkI . fromIntegral <$> [s, e]))
-
--- don't need to rename op because it's being used in a map, can't affect etc.
-applyUn :: E (T K)
-        -> E (T K)
-        -> EvalM (E (T K))
-applyUn unOp e =
-    case eLoc unOp of
-        TyArr _ _ res -> eNorm (EApp res unOp e)
-        _             -> error "Internal error?"
-
-applyOp :: E (T K)
-        -> E (T K)
-        -> E (T K)
-        -> EvalM (E (T K))
-applyOp op@BBuiltin{}  e e' = eNorm (EApp undefined (EApp undefined op e) e') -- short-circuit if not a lambda, don't need rename
-applyOp op e e'             = do { op' <- renameE op ; eNorm (EApp undefined (EApp undefined op' e) e') }
-
-foldE :: E (T K)
-      -> E (T K)
-      -> V.Vector (E (T K))
-      -> EvalM (E (T K))
-foldE op = V.foldM' (applyOp op)
-
--- TODO: equality on tuples, lists
-eNorm :: E (T K)
-      -> EvalM (E (T K))
-eNorm e@Field{}       = pure e
-eNorm e@IntLit{}      = pure e
-eNorm e@FloatLit{}    = pure e
-eNorm e@BoolLit{}     = pure e
-eNorm e@StrLit{}      = pure e
-eNorm e@RegexLit{}    = pure e
-eNorm e@RegexCompiled{} = pure e
-eNorm e@UBuiltin{}    = pure e
-eNorm e@Column{}      = pure e
-eNorm e@AllColumn{}   = pure e
-eNorm e@IParseCol{}   = pure e
-eNorm e@FParseCol{}   = pure e
-eNorm e@ParseCol{}    = pure e
-eNorm e@AllField{}    = pure e
-eNorm e@LastField{}   = pure e
-eNorm (Guarded ty pe e) = Guarded ty <$> eNorm pe <*> eNorm e
-eNorm (Implicit ty e) = Implicit ty <$> eNorm e
-eNorm (Lam ty n e)    = Lam ty n <$> eNorm e
-eNorm e@BBuiltin{}    = pure e
-eNorm e@TBuiltin{}    = pure e
-eNorm (Tup tys es)    = Tup tys <$> traverse eNorm es
-eNorm (Anchor ty es)  = Anchor ty <$> traverse eNorm es
-eNorm (NBuiltin ty None) = pure $ OptionVal ty Nothing
-eNorm e@NBuiltin{}    = pure e
-eNorm (EApp ty op@BBuiltin{} e) = EApp ty op <$> eNorm e
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Matches) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (StrLit _ str, RegexCompiled re) -> BoolLit tyBool (isMatch' re str)
-        _                                -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ NotMatches) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (StrLit _ str, RegexCompiled re) -> BoolLit tyBool (not $ isMatch' re str)
-        _                                -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin (TyArr _ (TyB _ TyInteger) _) Max) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j) -> i `seq` j `seq` IntLit tyI (max i j)
-        _                        -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin (TyArr _ (TyB _ TyInteger) _) Min) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j) -> i `seq` j `seq` IntLit tyI (min i j)
-        _                        -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin (TyArr _ (TyB _ TyFloat) _) Max) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (FloatLit _ x, FloatLit _ y) -> x `seq` y `seq` FloatLit tyF (max x y)
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin (TyArr _ (TyB _ TyFloat) _) Min) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (FloatLit _ x, FloatLit _ y) -> x `seq` y `seq` FloatLit tyF (min x y)
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Split) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (StrLit l str, RegexCompiled re) -> let bss = splitBy re str in Arr undefined (StrLit l <$> bss)
-        _                                -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Splitc) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (StrLit l str, StrLit _ c) -> let bss = BS.split (the c) str in Arr undefined (StrLit l <$> V.fromList bss)
-        _                          -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty op@(UBuiltin _ Floor) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (FloatLit _ f) -> mkI (floor f)
-        _              -> EApp ty op eI
-eNorm (EApp ty op@(UBuiltin _ Ceiling) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (FloatLit _ f) -> mkI (ceiling f)
-        _              -> EApp ty op eI
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ Minus) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j)     -> i `seq` j `seq` IntLit tyI (i-j)
-        (FloatLit _ i, FloatLit _ j) -> i `seq` j `seq` FloatLit tyF (i-j)
-        _                            -> EApp ty0 (EApp ty1 op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Times) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j)     -> i `seq` j `seq` IntLit tyI (i*j)
-        (FloatLit _ i, FloatLit _ j) -> i `seq` j `seq` FloatLit tyF (i*j)
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Plus) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j)        -> i `seq` j `seq` IntLit tyI (i+j)
-        (StrLit _ s, StrLit _ s')       -> StrLit tyStr (s <> s') -- TODO: copy?
-        (RegexLit _ rr, RegexLit _ rr') -> RegexLit tyStr (rr <> rr')
-        (FloatLit _ i, FloatLit _ j)    -> i `seq` j `seq` FloatLit tyF (i+j)
-        _                               -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Div) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (FloatLit _ i, FloatLit _ j) -> i `seq` j `seq` FloatLit tyF (i/j)
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (UBuiltin ty' Tally) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        StrLit _ str -> IntLit tyI (fromIntegral $ BS.length str)
-        _            -> EApp ty (UBuiltin ty' Tally) eI
-eNorm (EApp ty op@(UBuiltin _ TallyList) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (Arr _ xs) -> mkI $ fromIntegral $ V.length xs
-        _          -> EApp ty op eI
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Lt) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j)     -> BoolLit tyBool (i < j)
-        (FloatLit _ i, FloatLit _ j) -> BoolLit tyBool (i < j)
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Gt) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j)     -> BoolLit tyBool (i > j)
-        (FloatLit _ i, FloatLit _ j) -> BoolLit tyBool (i > j)
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Eq) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j)     -> BoolLit tyBool (i == j)
-        (FloatLit _ i, FloatLit _ j) -> BoolLit tyBool (i == j)
-        (BoolLit _ b, BoolLit _ b')  -> BoolLit tyBool (b == b')
-        (StrLit _ i, StrLit _ j)     -> BoolLit tyBool (i == j)
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Neq) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j)     -> BoolLit tyBool (i /= j)
-        (FloatLit _ i, FloatLit _ j) -> BoolLit tyBool (i /= j)
-        (StrLit _ i, StrLit _ j)     -> BoolLit tyBool (i /= j)
-        (BoolLit _ b, BoolLit _ b')  -> BoolLit tyBool (b /= b')
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Leq) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j)     -> BoolLit tyBool (i <= j)
-        (FloatLit _ i, FloatLit _ j) -> BoolLit tyBool (i <= j)
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin _ Geq) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (IntLit _ i, IntLit _ j)     -> BoolLit tyBool (i >= j)
-        (FloatLit _ i, FloatLit _ j) -> BoolLit tyBool (i >= j)
-        _                            -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ And) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (BoolLit _ b, BoolLit _ b') -> b `seq` b' `seq` BoolLit tyBool (b && b')
-        _                           -> EApp ty0 (EApp ty1 op eI) eI'
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ Or) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (BoolLit _ b, BoolLit _ b') -> b `seq` b' `seq` BoolLit tyBool (b || b')
-        _                           -> EApp ty0 (EApp ty1 op eI) eI'
-eNorm (EApp _ (EApp _ (UBuiltin _ Const) e) _) = eNorm e
-eNorm (EApp ty op@(UBuiltin _ Const) e) = EApp ty op <$> eNorm e
-eNorm (EApp ty op@(UBuiltin _ Dedup) e) = EApp ty op <$> eNorm e
-eNorm (EApp ty op@(UBuiltin _ (At i)) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (Arr _ es) -> es V.! (i-1)
-        _          -> EApp ty op eI
-eNorm (EApp ty op@(UBuiltin _ (Select i)) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (Tup _ es) -> es !! (i-1)
-        _          -> EApp ty op eI
-eNorm (EApp ty op@(UBuiltin _ Negate) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (FloatLit _ f) -> mkF $ negate f
-        (IntLit _ i)   -> mkI $ negate i
-        _              -> EApp ty op eI
-eNorm (EApp ty op@(UBuiltin _ Not) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (BoolLit _ b) -> BoolLit tyBool (not b)
-        _             -> EApp ty op eI
-eNorm (EApp ty op@(UBuiltin _ IParse) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (StrLit _ str) -> parseAsEInt str
-        _              -> EApp ty op eI
-eNorm (EApp ty op@(UBuiltin _ FParse) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (StrLit _ str) -> parseAsF str
-        _              -> EApp ty op eI
-eNorm (EApp ty op@(UBuiltin (TyArr _ _ (TyB _ TyFloat)) Parse) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (StrLit _ str) -> parseAsF str
-        _              -> EApp ty op eI
-eNorm (EApp ty op@(UBuiltin (TyArr _ _ (TyB _ TyInteger)) Parse) e) = do
-    eI <- eNorm e
-    pure $ case eI of
-        (StrLit _ str) -> parseAsEInt str
-        _              -> EApp ty op eI
-eNorm (EApp ty (UBuiltin _ Some) e) = do
-    eI <- eNorm e
-    pure $ OptionVal ty (Just eI)
--- catMaybes only works for streams atm
-eNorm (EApp ty op@(UBuiltin _ CatMaybes) e) = EApp ty op <$> eNorm e
-eNorm Dfn{} = desugar
-eNorm ResVar{} = desugar
-eNorm (Let _ (Name _ (Unique i) _, b) e) = do
-    b' <- eNorm b
-    modify (mapBinds (IM.insert i b'))
-    eNorm e
-eNorm e@(Var _ (Name _ (Unique i) _)) = do
-    st <- gets binds
-    case IM.lookup i st of
-        Just e'@Var{} -> eNorm e' -- no cyclic binds
-        Just e'       -> renameE e' -- FIXME: set outermost type to be type of var...
-        Nothing       -> pure e -- default to e in case var was bound in a lambda
-eNorm (EApp ty e@Var{} e') = eNorm =<< (EApp ty <$> eNorm e <*> pure e')
-eNorm (EApp _ (Lam _ (Name _ (Unique i) _) e) e') = do
-    e'' <- eNorm e'
-    modify (mapBinds (IM.insert i e''))
-    eNorm e
-eNorm (EApp ty0 (EApp ty1 (EApp ty2 (TBuiltin ty3 Substr) e0) e1) e2) = do
-    e0' <- eNorm e0
-    e1' <- eNorm e1
-    e2' <- eNorm e2
-    pure $ case (e0', e1', e2') of
-        (StrLit _ str, IntLit _ i, IntLit _ j) -> mkStr (substr str (fromIntegral i) (fromIntegral j))
-        _                                      -> EApp ty0 (EApp ty1 (EApp ty2 (TBuiltin ty3 Substr) e0') e1') e2'
-eNorm (EApp ty0 (EApp ty1 (EApp ty2 op@(TBuiltin _ Captures) e0) e1) e2) = do
-    e0' <- eNorm e0
-    e1' <- eNorm e1
-    e2' <- eNorm e2
-    pure $ case (e0', e1', e2') of
-        (StrLit _ str, IntLit _ ix, RegexCompiled re) -> OptionVal (tyOpt tyStr) (mkStr <$> findCapture re str (fromIntegral ix))
-        _                                             -> EApp ty0 (EApp ty1 (EApp ty2 op e0') e1') e2'
-eNorm (EApp ty0 (EApp ty1 (EApp ty2 op@(TBuiltin _ AllCaptures) e0) e1) e2) = do
-    e0' <- eNorm e0
-    e1' <- eNorm e1
-    e2' <- eNorm e2
-    pure $ case (e0', e1', e2') of
-        (StrLit _ str, IntLit _ ix, RegexCompiled re) -> Arr (mkVec tyStr) (mkStr <$> V.fromList (captures' re str (fromIntegral ix)))
-        _                                             -> EApp ty0 (EApp ty1 (EApp ty2 op e0') e1') e2'
-eNorm (EApp ty0 (EApp ty1 (EApp ty2 op@(TBuiltin _ Option) e0) e1) e2) = do
-    e0' <- eNorm e0
-    e1' <- eNorm e1
-    e2' <- eNorm e2
-    case e2' of
-        (OptionVal _ Nothing)  -> pure e0'
-        (OptionVal _ (Just e)) -> eNorm (EApp undefined e1' e)
-        _                      -> pure $ EApp ty0 (EApp ty1 (EApp ty2 op e0') e1') e2'
-eNorm (EApp ty1 (EApp ty2 op@(TBuiltin _ Option) e0) e1) = do
-    e0' <- eNorm e0
-    e1' <- eNorm e1
-    pure $ EApp ty1 (EApp ty2 op e0') e1'
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ Match) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (StrLit _ str, RegexCompiled re) -> asTup (find' re str)
-        _                                -> EApp ty0 (EApp ty1 op eI) eI'
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ Sprintf) e) e') = do
-    eI <- eNorm e
-    eI' <- eNorm e'
-    pure $ case (eI, eI') of
-        (StrLit _ fmt, _) | isReady eI' -> mkStr $ sprintf fmt eI'
-        _                               -> EApp ty0 (EApp ty1 op eI) eI'
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyVec) _))) Map) x) y) = do
-    x' <- eNorm x
-    y' <- eNorm y
-    case y' of
-        Arr _ es -> Arr undefined <$> traverse (applyUn x') es -- TODO: undefined?
-        _        -> pure $ EApp ty0 (EApp ty1 op x') y'
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyOption) _))) Map) x) y) = do
-    x' <- eNorm x
-    y' <- eNorm y
-    case y' of
-        OptionVal _ e -> OptionVal undefined <$> traverse (applyUn x') e -- TODO: undefined?
-        _             -> pure $ EApp ty0 (EApp ty1 op x') y'
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyVec) _) _)) Fold1) f) x) = do
-    f' <- eNorm f
-    x' <- eNorm x
-    case x' of
-        Arr _ es -> case V.uncons es of { Just (y, ys) -> foldE f' y ys ; Nothing -> throw EmptyFold }
-        _        -> pure $ EApp ty0 (EApp ty1 op f') x'
-eNorm (EApp ty0 (EApp ty1 (EApp ty2 op@(TBuiltin (TyArr _ _ (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyVec) _) _))) Fold) f) x) y) = do
-    f' <- eNorm f
-    x' <- eNorm x
-    y' <- eNorm y
-    case y' of
-        Arr _ es -> foldE f' x' es
-        _        -> pure $ EApp ty0 (EApp ty1 (EApp ty2 op f') x') y'
-eNorm (EApp ty0 (EApp ty1 (EApp ty2 op@TBuiltin{} f) x) y) = EApp ty0 <$> (EApp ty1 <$> (EApp ty2 op <$> eNorm f) <*> eNorm x) <*> eNorm y
--- we include this in case (+) has type (a->a->a) (for instance) if it is
--- normalizing a decl (which can be ambiguous/general)
-eNorm (EApp ty0 (EApp ty1 op@BBuiltin{} e) e') = EApp ty0 <$> (EApp ty1 op <$> eNorm e) <*> eNorm e'
--- FIXME: monomorphize types after inlining
-eNorm (EApp ty e@EApp{} e') =
-    eNorm =<< (EApp ty <$> eNorm e <*> pure e')
-eNorm (Arr ty es) = Arr ty <$> traverse eNorm es
-eNorm (OptionVal ty e) = OptionVal ty <$> traverse eNorm e
-eNorm (Cond ty p e0 e1) = do
-    p' <- eNorm p
-    case p' of
-        BoolLit _ True  -> eNorm e0
-        BoolLit _ False -> eNorm e1
-        _               -> Cond ty p' <$> eNorm e0 <*> eNorm e1 -- needed to perform substitutions
-eNorm e = error ("Internal error: " ++ show e)
diff --git a/src/Jacinda/Backend/P.hs b/src/Jacinda/Backend/P.hs
new file mode 100644
--- /dev/null
+++ b/src/Jacinda/Backend/P.hs
@@ -0,0 +1,453 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Jacinda.Backend.P ( EvalErr (..), runJac, eB ) where
+
+import           A
+import           A.I
+import           Control.Exception          (Exception, throw)
+import           Control.Monad              (foldM, (<=<))
+import           Control.Monad.State.Strict (State, evalState, get, modify, runState)
+import           Data.Bifunctor             (bimap)
+import qualified Data.ByteString            as BS
+import qualified Data.ByteString.Char8      as ASCII
+import           Data.Containers.ListUtils  (nubOrdOn)
+import           Data.Foldable              (traverse_)
+import qualified Data.IntMap                as IM
+import           Data.List                  (scanl', transpose, uncons, unzip4)
+import           Data.Maybe                 (catMaybes, mapMaybe)
+import           Data.Semigroup             ((<>))
+import qualified Data.Vector                as V
+import           Data.Word                  (Word8)
+import           Jacinda.Backend.Const
+import           Jacinda.Backend.Parse
+import           Jacinda.Backend.Printf
+import           Jacinda.Fuse
+import           Jacinda.Regex
+import           Nm
+import           Prettyprinter              (hardline, pretty)
+import           Prettyprinter.Render.Text  (putDoc)
+import           Regex.Rure                 (RureMatch (RureMatch), RurePtr)
+import           System.IO                  (hFlush, stdout)
+import           Ty.Const
+import           U
+
+φ1 :: E T -> Int
+φ1 (BB (TyArr _ (TyArr (TyApp (TyB TyStream) _) _)) Fold1) = 1
+φ1 (EApp _ e0 e1)                                          = φ1 e0+φ1 e1
+φ1 (Tup _ es)                                              = sum (φ1<$>es)
+φ1 (OptionVal _ (Just e))                                  = φ1 e
+φ1 (Cond _ p e0 e1)                                        = φ1 p+φ1 e0+φ1 e1
+φ1 (Lam _ _ e)                                             = φ1 e
+φ1 _                                                       = 0
+
+
+φ :: E T -> Int
+φ (TB (TyArr _ (TyArr _ (TyArr (TyApp (TyB TyStream) _) _))) Fold) = 1
+φ (EApp _ e0 e1)                                                   = φ e0+φ e1
+φ (Tup _ es)                                                       = sum (φ<$>es)
+φ (OptionVal _ (Just e))                                           = φ e
+φ (Cond _ p e0 e1)                                                 = φ p+φ e0+φ e1
+φ (Lam _ _ e)                                                      = φ e
+φ _                                                                = 0
+
+noleak :: E T -> Bool
+noleak e = φ e > 1 && φ1 e < 1
+
+runJac :: RurePtr -- ^ Record separator
+       -> Bool -- ^ Flush output?
+       -> Int
+       -> E T
+       -> Either StreamError ([BS.ByteString] -> IO ())
+runJac re f i e = ϝ (bsProcess re f) (if noleak e then fuse i e else (e, i)) where ϝ = uncurry.flip
+
+data StreamError = NakedField deriving (Show)
+
+instance Exception StreamError where
+
+data EvalErr = EmptyFold
+             | IndexOutOfBounds Int
+             | InternalCoercionError (E T) TB
+             | ExpectedTup (E T)
+             | BadHole (Nm T)
+             deriving (Show)
+
+instance Exception EvalErr where
+
+(!) :: V.Vector a -> Int -> a
+v ! ix = case v V.!? ix of {Just x  -> x; Nothing -> throw $ IndexOutOfBounds ix}
+
+parseAsEInt :: BS.ByteString -> E T
+parseAsEInt = mkI . readDigits
+
+parseAsF :: BS.ByteString -> E T
+parseAsF = FLit tyF . readFloat
+
+readFloat :: BS.ByteString -> Double
+readFloat = read . ASCII.unpack
+
+the :: BS.ByteString -> Word8
+the bs = case BS.uncons bs of
+    Nothing                -> error "Empty splitc char!"
+    Just (c,b) | BS.null b -> c
+    Just _                 -> error "Splitc takes only one char!"
+
+asTup :: Maybe RureMatch -> E T
+asTup Nothing                = OptionVal undefined Nothing
+asTup (Just (RureMatch s e)) = OptionVal undefined (Just (Tup undefined (mkI . fromIntegral <$> [s, e])))
+
+mkFoldVar :: Int -> b -> E b
+mkFoldVar i l = Var l (Nm "fold_placeholder" (U i) l)
+
+takeConcatMap :: (a -> [b]) -> [a] -> [b]
+takeConcatMap f = concat . transpose . fmap f
+
+-- this relies on all streams being the same length stream which in turn relies
+-- on the fuse step (fold-of-filter->fold)
+foldAll :: Int -> RurePtr -> [(Int, E T, E T, E T)] -> [BS.ByteString] -> ([(Int, E T)], Int)
+foldAll i r xs bs = runState (foldMultiple seeds streams ctxStream ixStream) i
+    where (ns, ops, seeds, es) = unzip4 xs
+          mkStream e = eStream i r e bs
+          streams = mkStream<$>es
+          ctxStream = [(b, splitBy r b) | b <- bs]
+          ixStream = [1..]
+
+          foldMultiple seedsϵ esϵ (ctx:ctxes) (ix:ixes) = allHeads esϵ `seq` do {es' <- sequence$zipWith3 (c2Mϵ (pure.eCtx ctx ix)) ops seedsϵ (head<$>esϵ); foldMultiple es' (tail<$>esϵ) ctxes ixes}
+          -- TODO: sanity check same length all streams
+          foldMultiple seedsϵ _ [] _ = pure$zip ns seedsϵ
+
+          allHeads = foldr seq ()
+
+gf :: E T -> State (Int, [(Int, E T, E T, E T)]) (E T)
+gf (EApp _ (EApp _ (EApp _ (TB _ Fold) op) seed) stream) | t@(TyApp (TyB TyStream) _) <- eLoc stream = do
+    (i,_) <- get
+    modify (bimap (+1) ((i, op, seed, stream) :))
+    pure $ mkFoldVar i t
+gf (EApp ty e0 e1) = EApp ty <$> gf e0 <*> gf e1
+gf (Tup ty es) = Tup ty <$> traverse gf es
+gf (Arr ty es) = Arr ty <$> traverse gf es
+gf (OptionVal ty e) = OptionVal ty <$> traverse gf e
+gf (Cond ty p e e') = Cond ty <$> gf p <*> gf e <*> gf e'
+gf (Lam t n e) = Lam t n <$> gf e
+gf e@BB{} = pure e; gf e@TB{} = pure e; gf e@UB{} = pure e; gf e@NB{} = pure e
+gf e@StrLit{} = pure e; gf e@FLit{} = pure e; gf e@ILit{} = pure e; gf e@BLit{} = pure e
+gf e@RC{} = pure e; gf e@Var{} = pure e
+
+ug :: IM.IntMap (E T) -> E T -> E T
+ug st (Var _ n@(Nm _ (U i) _)) =
+    IM.findWithDefault (throw (BadHole n)) i st
+ug _ e = e
+
+bsProcess :: RurePtr
+          -> Bool -- ^ Flush output?
+          -> Int -- ^ Unique context
+          -> E T
+          -> Either StreamError ([BS.ByteString] -> IO ())
+bsProcess _ _ _ AllField{} = Left NakedField
+bsProcess _ _ _ Field{}    = Left NakedField
+bsProcess _ _ _ (NB _ Ix)  = Left NakedField
+bsProcess r f u e | (TyApp (TyB TyStream) _) <- eLoc e = Right (pS f.eStream u r e)
+bsProcess r f u (Anchor _ es) = Right (\bs -> pS f $ takeConcatMap (\e -> eStream u r e bs) es)
+bsProcess r _ u e =
+    Right $ \bs -> pDocLn (eF u r e bs)
+
+pDocLn = putDoc.(<>hardline).pretty
+
+pS p = traverse_ g where g | p = (*>fflush).pDocLn | otherwise = pDocLn
+                         fflush = hFlush stdout
+
+scanM :: Monad m => (b -> a -> m b) -> b -> [a] -> m [b]
+scanM op seed xs = sequence $
+    scanl' go (pure seed) xs where go seedϵ x = do {seedϵ' <- seedϵ; op seedϵ' x}
+
+eF :: Int -> RurePtr -> E T -> [BS.ByteString] -> E T
+eF u r e | noleak e = \bs ->
+    let (eHoley, (_, folds)) = runState (gf e) (0, [])
+        (filledHoles, u') = foldAll u r folds bs
+        in eB u' (pure.ug (IM.fromList filledHoles)) eHoley
+        | otherwise = \bs ->
+        eB u (go bs) e
+            where go bb (EApp _ (EApp _ (EApp _ (TB _ Fold) op) seed) xs) = do
+                      op' <- eBM pure op
+                      seed' <- eBM pure seed
+                      let xsϵ=eStream u r xs bb
+                      foldM (c2M op') seed' xsϵ
+                  go bb (EApp _ (EApp _ (BB _ Fold1) op) xs) = do
+                      op' <- eBM pure op
+                      let (seed',xsϵ)=case uncons $ eStream u r xs bb of {Just s -> s; Nothing -> throw EmptyFold}
+                      foldM (c2M op') seed' xsϵ
+                  go _ eϵ = pure eϵ
+
+
+a1 :: E T -> E T -> UM (E T)
+a1 f x | TyArr _ cod <- eLoc f = lβ (EApp cod f x)
+
+a2 :: E T -> E T -> E T -> UM (E T)
+a2 op x0 x1 | TyArr _ t@(TyArr _ t') <- eLoc op = lβ (EApp t' (EApp t op x0) x1)
+
+c1 :: Int -> E T -> E T -> E T
+c1 i f x = evalState (eBM pure =<< a1 f x) i
+
+c2M op x0 x1 = eBM pure =<< a2 op x0 x1
+c2Mϵ f g e e' = eBM f =<< a2 g e e'
+
+c2 :: Int -> E T -> E T -> E T -> E T
+c2 i op x0 x1 = evalState (c2M op x0 x1) i
+
+eStream :: Int -> RurePtr -> E T -> [BS.ByteString] -> [E T]
+eStream u r (EApp _ (EApp _ (EApp _ (TB _ Scan) op) seed) xs) bs =
+    let op'=eB u pure op; seed'=eB u pure seed; xsϵ=eStream u r xs bs
+    in evalState (scanM (c2M op') seed' xsϵ) u
+eStream i r (EApp _ (UB _ CatMaybes) e) bs = mapMaybe asM$eStream i r e bs
+eStream u r (Implicit _ e) bs = zipWith (\fs i -> eB u (pure.eCtx fs i) e) [(b, splitBy r b) | b <- bs] [1..]
+eStream _ _ AllColumn{} bs = mkStr<$>bs
+eStream _ r (Column _ i) bs = mkStr.(! (i-1)).splitBy r<$>bs
+eStream _ r (IParseCol _ n) bs = [parseAsEInt (splitBy r b ! (n-1)) | b <- bs]
+eStream _ r (ParseCol (TyApp _ (TyB TyInteger)) n) bs = [parseAsEInt (splitBy r b ! (n-1)) | b <- bs]
+eStream _ r (FParseCol _ n) bs = [parseAsF (splitBy r b ! (n-1)) | b <- bs]
+eStream _ r (ParseCol (TyApp _ (TyB TyFloat)) n) bs = [parseAsF (splitBy r b ! (n-1)) | b <- bs]
+eStream i r (EApp _ (EApp _ (BB _ MapMaybe) f) e) bs = let xs = eStream i r e bs in mapMaybe (asM.c1 i f) xs
+eStream i r (EApp _ (EApp _ (BB _ Map) f) e) bs = let xs=eStream i r e bs in fmap (c1 i f) xs
+eStream i r (EApp _ (EApp _ (BB _ Prior) op) e) bs = let xs=eStream i r e bs in zipWith (c2 i op) (tail xs) xs
+eStream i r (EApp _ (EApp _ (BB _ Filter) p) e) bs = let xs=eStream i r e bs; ps=fmap (asB.c1 i p) xs in [x | (pϵ,x) <- zip ps xs, pϵ]
+eStream i r (EApp _ (EApp _ (EApp _ (TB _ ZipW) f) e0) e1) bs = let xs0=eStream i r e0 bs; xs1=eStream i r e1 bs in zipWith (c2 i f) xs0 xs1
+eStream i r (EApp (TyApp _ (TyB TyStr)) (UB _ Dedup) e) bs = let s = eStream i r e bs in nubOrdOn asS s
+eStream i r (EApp (TyApp _ (TyB TyInteger)) (UB _ Dedup) e) bs = let s = eStream i r e bs in nubOrdOn asI s
+eStream i r (EApp (TyApp _ (TyB TyFloat)) (UB _ Dedup) e) bs = let s = eStream i r e bs in nubOrdOn asF s
+eStream i r (EApp _ (EApp _ (BB _ DedupOn) op) e) bs | TyArr _ (TyB TyStr) <- eLoc op = let xs = eStream i r e bs in nubOrdOn (asS.c1 i op) xs
+eStream i r (EApp _ (EApp _ (BB _ DedupOn) op) e) bs | TyArr _ (TyB TyInteger) <- eLoc op = let xs = eStream i r e bs in nubOrdOn (asI.c1 i op) xs
+eStream i r (EApp _ (EApp _ (BB _ DedupOn) op) e) bs | TyArr _ (TyB TyFloat) <- eLoc op = let xs = eStream i r e bs in nubOrdOn (asF.c1 i op) xs
+eStream u r (Guarded _ p e) bs =
+    let bss=(\b -> (b, splitBy r b))<$>bs
+    in catMaybes $ zipWith (\fs i -> if asB (eB u (pure.eCtx fs i) p) then Just (eB u (pure.eCtx fs i) e) else Nothing) bss [1..]
+
+asS :: E T -> BS.ByteString
+asS (StrLit _ s) = s; asS e = throw (InternalCoercionError e TyStr)
+
+asI :: E T -> Integer
+asI (ILit _ i) = i; asI e = throw (InternalCoercionError e TyInteger)
+
+asF :: E T -> Double
+asF (FLit _ x) = x; asF e = throw (InternalCoercionError e TyFloat)
+
+asR :: E T -> RurePtr
+asR (RC r) = r; asR e = throw (InternalCoercionError e TyR)
+
+asM :: E T -> Maybe (E T)
+asM (OptionVal _ e) = e; asM e = throw (InternalCoercionError e TyOption)
+
+asB :: E T -> Bool
+asB (BLit _ b) = b; asB e = throw (InternalCoercionError e TyBool)
+
+asV :: E T -> V.Vector (E T)
+asV (Arr _ v) = v; asV e = throw (InternalCoercionError e TyVec)
+
+asT :: E T -> [E T]
+asT (Tup _ es) = es; asT e = throw (ExpectedTup e)
+
+eCtx :: (BS.ByteString, V.Vector BS.ByteString) -- ^ Line, split by field separator
+     -> Integer -- ^ Line number
+     -> E T -> E T
+eCtx ~(f, _) _ AllField{}  = mkStr f
+eCtx (_, fs) _ (Field _ i) = mkStr (fs ! (i-1))
+eCtx (_, fs) _ LastField{} = mkStr (V.last fs)
+eCtx _ i (NB _ Ix)         = mkI i
+eCtx (_, fs) _ (NB _ Nf)   = mkI (fromIntegral$V.length fs)
+eCtx _ _ e                 = e
+
+eB :: Int -> (E T -> UM (E T)) -> E T -> E T
+eB i f x = evalState (eBM f x) i
+
+{-# SCC eBM #-}
+eBM :: (E T -> UM (E T)) -> E T -> UM (E T)
+eBM f (EApp t (EApp _ (EApp _ (TB _ Captures) s) i) r) = do
+    s' <- eBM f s; i' <- eBM f i; r' <- eBM f r
+    pure $ OptionVal t (mkStr <$> findCapture (asR r') (asS s') (fromIntegral$asI i'))
+eBM f (EApp t (EApp _ (EApp _ (TB _ AllCaptures) s) i) r) = do
+    s' <- eBM f s; i' <- eBM f i; r' <- eBM f r
+    pure $ Arr t (V.fromList (mkStr <$> captures' (asR r') (asS s') (fromIntegral$asI i')))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Max) x0) x1) = do
+    x0' <- asI<$>eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkI (max x0' x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Min) x0) x1) = do
+    x0' <- asI<$>eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkI (min x0' x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Min) x0) x1) = do
+    x0' <- asF<$>eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkF (min x0' x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Max) x0) x1) = do
+    x0' <- asF<$>eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkF (max x0' x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyStr) _) Min) x0) x1) = do
+    x0' <- asS<$>eBM f x0; x1' <- asS<$>eBM f x1
+    pure (mkStr (min x0' x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyStr) _) Max) x0) x1) = do
+    x0' <- asS<$>eBM f x0; x1' <- asS<$>eBM f x1
+    pure (mkStr (max x0' x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Plus) x0) x1) = do
+    x0' <- asI <$> eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkI (x0'+x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Minus) x0) x1) = do
+    x0' <- asI <$> eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkI (x0'-x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Times) x0) x1) = do
+    x0' <- asI <$> eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkI (x0'*x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Plus) x0) x1) = do
+    x0' <- asF <$> eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkF (x0'+x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Minus) x0) x1) = do
+    x0' <- asF <$> eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkF (x0'-x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Times) x0) x1) = do
+    x0' <- asF <$> eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkF (x0'*x1'))
+eBM f (EApp _ (EApp _ (BB _ Div) x0) x1) = do
+    x0' <- asF <$> eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkF (x0'/x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Eq) x0) x1) = do
+    x0' <- asI<$>eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkB (x0'==x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Neq) x0) x1) = do
+    x0' <- asI<$>eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkB (x0'/=x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Gt) x0) x1) = do
+    x0' <- asI<$>eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkB (x0'>x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Lt) x0) x1) = do
+    x0' <- asI<$>eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkB (x0'<x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Leq) x0) x1) = do
+    x0' <- asI<$>eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkB (x0'<=x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Geq) x0) x1) = do
+    x0' <- asI<$>eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkB (x0'>=x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Gt) x0) x1) = do
+    x0' <- asF<$>eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkB (x0'>x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Lt) x0) x1) = do
+    x0' <- asF<$>eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkB (x0'<x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Eq) x0) x1) = do
+    x0' <- asF<$>eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkB (x0'==x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Neq) x0) x1) = do
+    x0' <- asF<$>eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkB (x0'/=x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Geq) x0) x1) = do
+    x0' <- asF<$>eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkB (x0'>=x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Leq) x0) x1) = do
+    x0' <- asF<$>eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkB (x0'<=x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyStr) _) Eq) x0) x1) = do
+    x0' <- asS<$>eBM f x0; x1' <- asS<$>eBM f x1
+    pure (mkB (x0'==x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyStr) _) Neq) x0) x1) = do
+    x0' <- asS<$>eBM f x0; x1' <- asS<$>eBM f x1
+    pure (mkB (x0'/=x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyStr) _) Gt) x0) x1) = do
+    x0' <- asS<$>eBM f x0; x1' <- asS<$>eBM f x1
+    pure (mkB (x0'>x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyStr) _) Geq) x0) x1) = do
+    x0' <- asS<$>eBM f x0; x1' <- asS<$>eBM f x1
+    pure (mkB (x0'>=x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyStr) _) Lt) x0) x1) = do
+    x0' <- asS<$>eBM f x0; x1' <- asS<$>eBM f x1
+    pure (mkB (x0'<x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyStr) _) Leq) x0) x1) = do
+    x0' <- asS<$>eBM f x0; x1' <- asS<$>eBM f x1
+    pure (mkB (x0'<=x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyInteger) _) Exp) x0) x1) = do
+    x0' <- asI <$> eBM f x0; x1' <- asI<$>eBM f x1
+    pure (mkI (x0'^x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyFloat) _) Exp) x0) x1) = do
+    x0' <- asF <$> eBM f x0; x1' <- asF<$>eBM f x1
+    pure (mkF (x0'**x1'))
+eBM f (EApp _ (EApp _ (BB (TyArr (TyApp (TyB TyVec) t) _) Eq) x0) x1) = do
+    x0' <- asV<$>eBM f x0; x1' <- asV<$>eBM f x1
+    mkB <$> if V.length x0'==V.length x1'
+        then all asB <$> V.zipWithM (c2Mϵ f op) x0' x1'
+        else pure False
+    where op = BB (TyArr t (TyArr t tyB)) Eq
+eBM f (EApp _ (EApp _ (BB (TyArr (TyApp (TyB TyOption) t) _) Eq) x0) x1) = do
+    x0' <- asM<$>eBM f x0; x1' <- asM<$>eBM f x1
+    case (x0',x1') of
+        (Nothing, Nothing) -> pure (mkB True)
+        (Nothing, Just{})  -> pure (mkB False)
+        (Just{}, Nothing)  -> pure (mkB False)
+        (Just e0, Just e1) -> c2Mϵ f op e0 e1
+    where op = BB (TyArr t (TyArr t tyB)) Eq
+eBM f (EApp _ (EApp _ (BB (TyArr (TyB TyStr) _) Plus) x0) x1) = do
+    x0' <- asS <$> eBM f x0; x1' <- asS<$>eBM f x1
+    pure (mkStr (x0'<>x1'))
+eBM f (EApp _ (EApp _ (BB _ And) x0) x1) = do
+    x0' <- asB<$>eBM f x0; x1' <- asB<$>eBM f x1
+    pure (mkB (x0'&&x1'))
+eBM f (EApp _ (EApp _ (BB _ Or) x0) x1) = do
+    x0' <- asB<$>eBM f x0; x1' <- asB<$>eBM f x1
+    pure (mkB (x0'||x1'))
+eBM f (EApp _ (UB _ Not) b) = do {b' <- asB<$>eBM f b; pure $ mkB (not b')}
+eBM f (EApp _ (EApp _ (BB _ Matches) s) r) = {-# SCC "eBMMatch" #-} do
+    s' <- asS<$>eBM f s; r' <- asR<$>eBM f r
+    pure $ mkB (isMatch' r' s')
+eBM f (EApp _ (EApp _ (BB _ NotMatches) s) r) = do
+    s' <- asS<$>eBM f s; r' <- asR<$>eBM f r
+    pure $ mkB (not$isMatch' r' s')
+eBM f (EApp _ (EApp _ (BB _ Split) s) r) = do
+    s' <- asS<$>eBM f s; r' <- asR<$>eBM f r
+    pure (Arr (tyV tyStr) (mkStr<$>splitBy r' s'))
+eBM f (EApp _ (EApp _ (BB _ Splitc) s) c) = do
+    s' <- asS<$>eBM f s; c' <- the.asS<$>eBM f c
+    pure (Arr (tyV tyStr) (mkStr <$> V.fromList (BS.split c' s')))
+eBM f (EApp _ (UB _ FParse) x) = do {x' <- eBM f x; pure (parseAsF (asS x'))}
+eBM f (EApp _ (UB _ IParse) x) = do {x' <- eBM f x; pure (parseAsEInt (asS x'))}
+eBM f (EApp (TyB TyInteger) (UB _ Parse) x) = do {x' <- eBM f x; pure (parseAsEInt (asS x'))}
+eBM f (EApp (TyB TyFloat) (UB _ Parse) x) = do {x' <- eBM f x; pure (parseAsF (asS x'))}
+eBM f (EApp _ (UB _ (At i)) v) = do {v' <- eBM f v; pure (asV v'!(i-1))}
+eBM f (EApp _ (UB _ (Select i)) x) = do {x' <- eBM f x; pure (asT x' !! (i-1))}
+eBM f (EApp _ (UB _ Floor) x) = do {xr <- asF<$>eBM f x; pure $ mkI (floor xr)}
+eBM f (EApp _ (UB _ Ceiling) x) = do {xr <- asF<$>eBM f x; pure $ mkI (ceiling xr)}
+eBM f (EApp (TyB TyInteger) (UB _ Negate) i) = do {i' <- eBM f i; pure $ mkI (negate (asI i'))}
+eBM f (EApp (TyB TyFloat) (UB _ Negate) x) = do {x' <- eBM f x; pure $ mkF (negate (asF x'))}
+eBM f (EApp t (UB _ Some) e) = do {e' <- eBM f e; pure (OptionVal t (Just e'))}
+eBM _ (NB t None) = pure (OptionVal t Nothing)
+eBM f (EApp _ (UB _ Tally) e) = do
+    s' <- eBM f e
+    let r =fromIntegral (BS.length$asS s')
+    pure (mkI r)
+eBM f (EApp _ (UB _ TallyList) e) = do
+    e' <- eBM f e
+    let r=fromIntegral (V.length$asV e')
+    pure (mkI r)
+eBM f (EApp _ (EApp _ (UB _ Const) e) _) = eBM f e
+eBM f (EApp _ (EApp _ (BB _ Sprintf) fs) s) = do
+    fs' <- eBM f fs; s' <- eBM f s
+    pure $ mkStr (sprintf (asS fs') s')
+eBM f (EApp _ (EApp _ (BB _ Match) s) r) = do
+    s' <- eBM f s; r' <- eBM f r
+    pure $ asTup (find' (asR r') (asS s'))
+eBM f (EApp _ (EApp _ (EApp _ (TB _ Fold) op) seed) xs) | TyApp (TyB TyVec) _ <- eLoc xs = do
+    op' <- eBM f op; seed' <- eBM f seed; xs' <- eBM f xs
+    V.foldM (c2Mϵ f op') seed' (asV xs')
+eBM f (EApp _ (EApp _ (BB _ Fold1) op) xs) | TyApp (TyB TyVec) _ <- eLoc xs = do
+    op' <- eBM f op; xs' <- eBM f xs
+    let xsV=asV xs'; Just (seed, xs'') = V.uncons xsV
+    V.foldM (c2Mϵ f op') seed xs''
+eBM f (EApp yT@(TyApp (TyB TyOption) _) (EApp _ (BB _ Map) g) x) = do
+    g' <- eBM f g; x' <- eBM f x
+    OptionVal yT <$> traverse (eBM f <=< a1 g') (asM x')
+eBM f (EApp yT@(TyApp (TyB TyVec) _) (EApp _ (BB _ Map) g) x) = do
+    g' <- eBM f g; x' <- eBM f x
+    Arr yT <$> traverse (eBM f <=< a1 g') (asV x')
+eBM f (EApp t (EApp _ (EApp _ (TB _ Option) x) g) y) = do
+    x' <- eBM f x; g' <- eBM f g; y' <- eBM f y
+    case asM y' of
+        Nothing -> pure x'
+        Just yϵ -> eBM f =<< lβ (EApp t g' yϵ)
+eBM f (EApp _ (EApp _ (EApp _ (TB _ Substr) s) i0) i1) = do
+    i0' <- eBM f i0; i1' <- eBM f i1; s' <- eBM f s
+    pure $ mkStr (substr (asS s') (fromIntegral$asI i0') (fromIntegral$asI i1'))
+eBM f (Cond _ p e e') = do {p' <- eBM f p; if asB p' then eBM f e else eBM f e'}
+eBM f (Tup t es) = Tup t <$> traverse (eBM f) es
+eBM f e = f e
diff --git a/src/Jacinda/Backend/Parse.hs b/src/Jacinda/Backend/Parse.hs
new file mode 100644
--- /dev/null
+++ b/src/Jacinda/Backend/Parse.hs
@@ -0,0 +1,12 @@
+module Jacinda.Backend.Parse ( readDigits ) where
+
+import qualified Data.ByteString       as BS
+import qualified Data.ByteString.Char8 as ASCII
+
+readDigits :: BS.ByteString -> Integer
+readDigits b | Just (45, bs) <- BS.uncons b = negate $ readDigits bs
+readDigits b = ASCII.foldl' (\seed x -> 10 * seed + f x) 0 b
+    where f '0' = 0; f '1' = 1; f '2' = 2; f '3' = 3;
+          f '4' = 4; f '5' = 5; f '6' = 6; f '7' = 7;
+          f '8' = 8; f '9' = 9
+          f c   = error (c:" is not a valid digit!")
diff --git a/src/Jacinda/Backend/Printf.hs b/src/Jacinda/Backend/Printf.hs
--- a/src/Jacinda/Backend/Printf.hs
+++ b/src/Jacinda/Backend/Printf.hs
@@ -1,23 +1,14 @@
 {-# LANGUAGE OverloadedStrings #-}
 
 module Jacinda.Backend.Printf ( sprintf
-                              , isReady
                               ) where
 
+import           A
 import qualified Data.ByteString    as BS
 import           Data.Semigroup     ((<>))
 import qualified Data.Text          as T
 import           Data.Text.Encoding (decodeUtf8, encodeUtf8)
-import           Jacinda.AST
 
-isReady :: E a -> Bool
-isReady FloatLit{} = True
-isReady StrLit{}   = True
-isReady IntLit{}   = True
-isReady BoolLit{}  = True
-isReady (Tup _ es) = all isReady es
-isReady _          = False
-
 sprintf :: BS.ByteString -- ^ Format string
         -> E a
         -> BS.ByteString
@@ -28,10 +19,10 @@
 -- TODO: interpret precision, like %0.6f %.6
 
 sprintf' :: T.Text -> E a -> T.Text
-sprintf' fmt (FloatLit _ f) =
+sprintf' fmt (FLit _ f) =
     let (prefix, fmt') = T.breakOn "%f" fmt
         in prefix <> T.pack (show f) <> T.drop 2 fmt'
-sprintf' fmt (IntLit _ i) =
+sprintf' fmt (ILit _ i) =
     let (prefix, fmt') = T.breakOn "%i" fmt
         in prefix <> T.pack (show i) <> T.drop 2 fmt'
 sprintf' fmt (StrLit _ bs) =
@@ -41,7 +32,7 @@
 sprintf' fmt (Tup l (e:es)) =
     let nextFmt = sprintf' fmt e
         in sprintf' nextFmt (Tup l es)
-sprintf' fmt (BoolLit _ b) =
+sprintf' fmt (BLit _ b) =
     let (prefix, fmt') = T.breakOn "%b" fmt
         in prefix <> showBool b <> T.drop 2 fmt'
     where showBool True  = "true"
diff --git a/src/Jacinda/Backend/TreeWalk.hs b/src/Jacinda/Backend/TreeWalk.hs
deleted file mode 100644
--- a/src/Jacinda/Backend/TreeWalk.hs
+++ /dev/null
@@ -1,591 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
--- | Tree-walking interpreter
-module Jacinda.Backend.TreeWalk ( runJac
-                                ) where
-
--- TODO: normalize before mapping?
-
-import           Control.Exception          (Exception, throw)
-import           Control.Monad.State.Strict (State, get, modify, runState)
-import           Data.Bifunctor             (bimap)
-import qualified Data.ByteString            as BS
-import           Data.Containers.ListUtils  (nubIntOn, nubOrdOn)
-import           Data.Foldable              (foldl', traverse_)
-import qualified Data.IntMap                as IM
-import           Data.List                  (scanl', transpose, unzip4)
-import           Data.List.Ext
-import           Data.Maybe                 (mapMaybe)
-import           Data.Semigroup             ((<>))
-import qualified Data.Vector                as V
-import           Intern.Name                (Name (Name))
-import           Intern.Unique              (Unique (Unique))
-import           Jacinda.AST
-import           Jacinda.Backend.Normalize
-import           Jacinda.Backend.Printf
-import           Jacinda.Regex              (captures', find', findCapture, isMatch', splitBy, substr)
-import           Jacinda.Ty.Const
-import           Regex.Rure                 (RurePtr)
-
-data StreamError = NakedField
-                 | UnevalFun
-                 | TupOfStreams -- ^ Reject a tuple of streams
-                 | BadCtx
-                 | InternalError
-                 deriving (Show)
-
-instance Exception StreamError where
-
-(!) :: V.Vector a -> Int -> a
-v ! ix = case v V.!? ix of
-    Just x  -> x
-    Nothing -> throw $ IndexOutOfBounds ix
-
-noRes :: E b -> String -> a
-noRes e ty = error ("Internal error: " ++ show e ++ " did not normalize to appropriate type, expected " ++ ty)
-
-badSugar :: a
-badSugar = error "Internal error: dfn syntactic sugar at a stage where it should not be."
-
-asInt :: E a -> Integer
-asInt (IntLit _ i) = i
-asInt e            = noRes e "Int"
-
-asBool :: E a -> Bool
-asBool (BoolLit _ b) = b
-asBool e             = noRes e "Bool"
-
-asStr :: E a -> BS.ByteString
-asStr (StrLit _ str) = str
-asStr e              = noRes e "Str"
-
-asFloat :: E a -> Double
-asFloat (FloatLit _ f) = f
-asFloat e              = noRes e "Float"
-
-asRegex :: E a -> RurePtr
-asRegex (RegexCompiled re) = re
-asRegex e                  = noRes e "Regex"
-
-asArr :: E a -> V.Vector (E a)
-asArr (Arr _ es) = es
-asArr e          = noRes e "List"
-
-asOpt :: E a -> Maybe (E a)
-asOpt (OptionVal _ e) = e
-asOpt e               = noRes e "Option"
-
--- eval
-eEval :: (Int, BS.ByteString, V.Vector BS.ByteString) -- ^ Field context (for that line)
-      -> E (T K)
-      -> E (T K)
-eEval (ix, line, ctx) = go where
-    go b@BoolLit{} = b
-    go i@IntLit{} = i
-    go f@FloatLit{} = f
-    go str@StrLit{} = str
-    go rr@RegexLit{} = rr
-    go reϵ@RegexCompiled{} = reϵ
-    go op@BBuiltin{} = op
-    go op@UBuiltin{} = op
-    go op@TBuiltin{} = op
-    go (NBuiltin _ Nf) = mkI (fromIntegral $ V.length ctx)
-    go (EApp ty op@BBuiltin{} e) = EApp ty op (go e)
-    go (NBuiltin _ Ix) = mkI (fromIntegral ix)
-    go (NBuiltin _ None) = OptionVal undefined Nothing
-    go (EApp ty (UBuiltin _ Some) e) =
-        let eI = go e
-            in OptionVal ty (Just eI)
-    go AllField{} = StrLit tyStr line
-    go (Field _ i) = StrLit tyStr (ctx ! (i-1)) -- cause vector indexing starts at 0
-    go LastField{} = StrLit tyStr (V.last ctx)
-    go (EApp _ (UBuiltin _ IParse) e) =
-        let eI = asStr (go e)
-            in parseAsEInt eI
-    go (EApp _ (UBuiltin (TyArr _ (TyB _ TyInteger) _) Negate) e) =
-        let eI = asInt (go e)
-            in mkI (negate eI)
-    go (EApp _ (UBuiltin (TyArr _ (TyB _ TyFloat) _) Negate) e) =
-        let eI = asFloat (go e)
-            in mkF (negate eI)
-    go (EApp _ (UBuiltin _ FParse) e) =
-        let eI = asStr (go e)
-            in parseAsF eI
-    go (EApp _ (UBuiltin (TyArr _ _ (TyB _ TyInteger)) Parse) e) =
-        let eI = asStr (go e)
-            in parseAsEInt eI
-    go (EApp _ (UBuiltin (TyArr _ _ (TyB _ TyFloat)) Parse) e) =
-        let eI = asStr (go e)
-            in parseAsF eI
-    go (EApp _ (EApp _ (BBuiltin _ Matches) e) e') =
-        let eI = go e
-            eI' = go e'
-        in case (eI, eI') of
-            (StrLit _ strϵ, RegexCompiled reϵ) -> BoolLit tyBool (isMatch' reϵ strϵ)
-            (StrLit{}, _)                      -> noRes eI' "Regex"
-            _                                  -> noRes eI "Str"
-    go (EApp _ (EApp _ (BBuiltin _ NotMatches) e) e') =
-        let eI = go e
-            eI' = go e'
-        in case (eI, eI') of
-            (StrLit _ strϵ, RegexCompiled reϵ) -> BoolLit tyBool (not $ isMatch' reϵ strϵ)
-            (StrLit{}, _)                      -> noRes eI' "Regex"
-            _                                  -> noRes eI "Str"
-    go (EApp _ (EApp _ (BBuiltin _ Match) e) e') =
-        let eI = asRegex (go e)
-            eI' = asStr (go e')
-        in asTup (find' eI eI')
-    go (EApp _ (EApp _ (EApp _ (TBuiltin _ Captures) e0) e1) e2) =
-        let e0' = asStr (go e0)
-            e1' = asInt (go e1)
-            e2' = asRegex (go e2)
-            in OptionVal (tyOpt tyStr) (mkStr <$> findCapture e2' e0' (fromIntegral e1'))
-    go (EApp _ (EApp _ (EApp _ (TBuiltin _ AllCaptures) e0) e1) e2) =
-        let e0' = asStr (go e0)
-            e1' = asInt (go e1)
-            e2' = asRegex (go e2)
-            in Arr (mkVec tyStr) (mkStr <$> V.fromList (captures' e2' e0' (fromIntegral e1')))
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Plus) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in mkI (eI + eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Minus) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in mkI (eI - eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Times) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in mkI (eI * eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyStr) _) Plus) e) e') =
-        let eI = asStr (go e)
-            eI' = asStr (go e')
-            -- TODO: copy??
-            in mkStr (eI <> eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyStr) _) Eq) e) e') =
-        let eI = asStr (go e)
-            eI' = asStr (go e')
-            in BoolLit tyBool (eI == eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Gt) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in BoolLit tyBool (eI > eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Lt) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in BoolLit tyBool (eI < eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Eq) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in BoolLit tyBool (eI == eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Neq) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in BoolLit tyBool (eI == eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyStr) _) Neq) e) e') =
-        let eI = asStr (go e)
-            eI' = asStr (go e')
-            in BoolLit tyBool (eI /= eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Leq) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in BoolLit tyBool (eI <= eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Geq) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in BoolLit tyBool (eI <= eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Eq) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in BoolLit tyBool (eI == eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Neq) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in BoolLit tyBool (eI /= eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Lt) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in BoolLit tyBool (eI < eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Gt) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in BoolLit tyBool (eI > eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Geq) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in BoolLit tyBool (eI >= eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Leq) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in BoolLit tyBool (eI <= eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Plus) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in mkF (eI + eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Minus) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in mkF (eI - eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Times) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in FloatLit tyF (eI * eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyBool) _) Eq) e) e') =
-        let eI = asBool (go e)
-            eI' = asBool (go e')
-            in BoolLit tyBool (eI == eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyBool) _) Neq) e) e') =
-        let eI = asBool (go e)
-            eI' = asBool (go e')
-            in BoolLit tyBool (eI /= eI')
-    go (EApp _ (EApp _ (BBuiltin _ Div) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in FloatLit tyF (eI / eI')
-    go (EApp _ (EApp _ (BBuiltin _ And) e) e') =
-        let b = asBool (go e)
-            b' = asBool (go e')
-            in BoolLit tyBool (b && b')
-    go (EApp _ (EApp _ (BBuiltin _ Or) e) e') =
-        let b = asBool e
-            b' = asBool e'
-            in BoolLit tyBool (b || b')
-    go (EApp _ (UBuiltin _ Tally) e) =
-        mkI (fromIntegral $ BS.length str)
-        where str = asStr (go e)
-    go (EApp _ (UBuiltin _ Floor) e) =
-        let f = asFloat e
-        in mkI (floor f)
-    go (EApp _ (UBuiltin _ Ceiling) e) =
-        let f = asFloat e
-        in mkI (ceiling f)
-    go (Tup ty es) = Tup ty (go <$> es)
-    go (EApp _ (EApp _ (BBuiltin _ Split) e) e') =
-        let str = asStr (go e)
-            re = asRegex (go e')
-            bss = splitBy re str
-            in Arr undefined (mkStr <$> bss)
-    go (EApp _ (EApp _ (BBuiltin _ Splitc) e) e') =
-        let str = asStr (go e)
-            c = the (asStr (go e'))
-            bss = BS.split c str
-            in Arr undefined (mkStr <$> V.fromList bss)
-    go (EApp _ (EApp _ (EApp _ (TBuiltin _ Substr) e0) e1) e2) =
-        let eI0 = asStr (go e0)
-            eI1 = asInt (go e1)
-            eI2 = asInt (go e2)
-        in mkStr (substr eI0 (fromIntegral eI1) (fromIntegral eI2))
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Max) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in mkF (max eI eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyFloat) _) Min) e) e') =
-        let eI = asFloat (go e)
-            eI' = asFloat (go e')
-            in mkF (min eI eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Max) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in mkI (max eI eI')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ (TyB _ TyInteger) _) Min) e) e') =
-        let eI = asInt (go e)
-            eI' = asInt (go e')
-            in mkI (min eI eI')
-    go (EApp _ (UBuiltin _ Not) e) =
-        let eI = asBool (go e)
-        in BoolLit tyBool (not eI)
-    go (EApp _ (UBuiltin _ (At i)) e) =
-        let eI = go e
-            in case eI of
-                (Arr _ es) -> go (es V.! (i-1))
-                _          -> noRes eI "List"
-    go (EApp _ (UBuiltin _ (Select i)) e) =
-        let eI = go e
-            in case eI of
-                (Tup _ es) -> go (es !! (i-1))
-                _          -> noRes eI "Tuple"
-    go (EApp _ (EApp _ (BBuiltin _ Sprintf) e) e') =
-        let eI = asStr (go e)
-            eI' = go e'
-        in mkStr (sprintf eI eI')
-    go (OptionVal ty e) =
-        OptionVal ty (go <$> e)
-    go (EApp _ (EApp _ (EApp _ (TBuiltin _ Option) e0) e1) e2) =
-        let e0' = go e0
-            e1' = go e1
-            e2' = go e2
-        in case asOpt e2' of
-                Nothing -> e0'
-                Just e  -> go (EApp undefined e1' e)
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyVec) _))) Map) x) y) =
-        let x' = go x
-            y' = asArr (go y)
-        in Arr undefined (applyUn' x' <$> y')
-        where applyUn' :: E (T K) -> E (T K) -> E (T K)
-              applyUn' e e' = go (EApp undefined e e')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyOption) _))) Map) x) y) =
-        let x' = go x
-            y' = asOpt (go y)
-        in OptionVal undefined (applyUn' x' <$> y')
-        where applyUn' :: E (T K) -> E (T K) -> E (T K)
-              applyUn' e e' = go (EApp undefined e e')
-    go (EApp _ (EApp _ (EApp _ (TBuiltin (TyArr _ _ (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyVec) _) _))) Fold) f) seed) xs) =
-        let f' = go f
-            seed' = go seed
-            xs' = asArr (go xs)
-        in foldE f' seed' xs'
-        where foldE op = V.foldl' (applyOp' op)
-              applyOp' op e e' = go (EApp undefined (EApp undefined op e) e')
-    go (EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyVec) _) _)) Fold1) f) xs) =
-        let f' = go f
-            xs' = asArr (go xs)
-        in
-            case V.uncons xs' of
-                Just (y, ys) -> foldE f' y ys
-                Nothing      -> throw EmptyFold
-        where foldE op = V.foldl' (applyOp' op)
-              applyOp' op e e' = go (EApp undefined (EApp undefined op e) e')
-    go (Arr ty es) = Arr ty (go <$> es)
-    go (Cond _ p e0 e1) =
-        let p' = asBool (go p)
-            in if p' then go e0 else go e1
-    go (EApp _ (UBuiltin _ TallyList) e) =
-        let xs = asArr (go e)
-            in mkI $ fromIntegral $ V.length xs
-    go e = error ("Internal error: " ++ show e)
-
--- just shove some big number into the renamer and hope it doesn't clash (bad,
--- hack, this is why we got kicked out of the garden of Eden)
-reprehensible :: Int
-reprehensible = (maxBound :: Int) `div` 2
-
-applyOp :: E (T K) -- ^ Operator
-        -> E (T K)
-        -> E (T K)
-        -> E (T K)
-applyOp op e e' = eClosed reprehensible (EApp undefined (EApp undefined op e) e') -- FIXME: undefined is ??
-
-atField :: RurePtr
-        -> Int
-        -> BS.ByteString -- ^ Line
-        -> BS.ByteString
-atField re i = (! (i-1)) . splitBy re
-
-mkCtx :: RurePtr -> Int -> BS.ByteString -> (Int, BS.ByteString, V.Vector BS.ByteString)
-mkCtx re ix line = (ix, line, splitBy re line)
-
-applyUn :: E (T K)
-        -> E (T K)
-        -> E (T K)
-applyUn unOp e =
-    case eLoc unOp of
-        TyArr _ _ res -> eClosed reprehensible (EApp res unOp e)
-        _             -> error "Internal error?"
-
--- | Turn an expression representing a stream into a stream of expressions (using line as context)
-ir :: RurePtr
-   -> E (T K)
-   -> [BS.ByteString]
-   -> [E (T K)] -- TODO: include chunks/context too?
-ir _ AllColumn{} = fmap mkStr
-ir re (Column _ i) = fmap (mkStr . atField re i)
-ir re (IParseCol _ i) = fmap (parseAsEInt . atField re i)
-ir re (FParseCol _ i) = fmap (parseAsF . atField re i)
-ir re (ParseCol ty@(TyApp _ _ (TyB _ TyFloat)) i) = ir re (FParseCol ty i)
-ir re (ParseCol ty@(TyApp _ _ (TyB _ TyInteger)) i) = ir re (IParseCol ty i)
-ir re (Implicit _ e) =
-    imap (\ix line -> eEval (mkCtx re ix line) e)
-ir re (Guarded _ pe e) =
-    -- TODO: normalize before stream
-    fmap (\(ix, line) -> eEval (mkCtx re ix line) e) . ifilter' (\ix line -> asBool (eEval (mkCtx re ix line) pe))
-ir re (EApp _ (EApp _ (BBuiltin _ Map) op) stream) = fmap (applyUn op) . ir re stream
-ir re (EApp _ (EApp _ (BBuiltin _ Filter) op) stream) =
-    filter (asBool . applyUn op) . ir re stream
-ir re (EApp _ (EApp _ (BBuiltin _ MapMaybe) op) stream) =
-    mapMaybe (asOpt . applyUn op) . ir re stream
-ir re (EApp _ (UBuiltin _ CatMaybes) stream) =
-    mapMaybe asOpt . ir re stream
-ir re (EApp _ (EApp _ (BBuiltin _ Prior) op) stream) = prior (applyOp op) . ir re stream
-ir re (EApp _ (EApp _ (EApp _ (TBuiltin _ ZipW) op) streaml) streamr) = \lineStream ->
-    let
-        irl = ir re streaml lineStream
-        irr = ir re streamr lineStream
-    in zipWith (applyOp op) irl irr
-ir re (EApp _ (EApp _ (EApp _ (TBuiltin _ Scan) op) seed) xs) =
-    scanl' (applyOp op) seed . ir re xs
-ir re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyStr)) _) Dedup) e) =
-    nubOrdOn asStr . ir re e
-ir re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyInteger)) _) Dedup) e) =
-    nubIntOn (fromIntegral . asInt) . ir re e
-ir re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyFloat)) _) Dedup) e) =
-    nubIntOn (fromEnum . asFloat) . ir re e
-ir re (EApp _ (UBuiltin (TyArr _ (TyApp _ _ (TyB _ TyBool)) _) Dedup) e) =
-    nubIntOn (fromEnum . asBool) . ir re e
-
--- | Output stream that prints each entry (expression)
-printStream :: [E (T K)] -> IO ()
-printStream = traverse_ print
-
-foldWithCtx :: RurePtr
-            -> E (T K)
-            -> E (T K)
-            -> E (T K)
-            -> [BS.ByteString]
-            -> E (T K)
-foldWithCtx re op seed streamExpr = foldl' (applyOp op) seed . ir re streamExpr
-
-fold1 :: RurePtr
-      -> E (T K)
-      -> E (T K)
-      -> [BS.ByteString]
-      -> E (T K)
-fold1 re op streamExpr bs =
-    case ir re streamExpr bs of
-        e:es -> foldl' (applyOp op) e es
-        _    -> throw EmptyFold
-
-runJac :: RurePtr -- ^ Record separator
-       -> Int
-       -> Program (T K)
-       -> Either StreamError ([BS.ByteString] -> IO ())
-runJac re i e = fileProcessor re (closedProgram i e)
-
-foldAll :: RurePtr
-        -> [(Int, E (T K), E (T K), E (T K))]
-        -> [BS.ByteString]
-        -> [(Int, E (T K))]
-foldAll re foldExprs bs = evalAll seeds (mkStreams streamExprs) where
-    (is, ops, seeds, streamExprs) = unzip4 foldExprs
-    mkStreams = fmap (\streamExpr -> ir re streamExpr bs)
-
-    evalAll seedsϵ ess | not (any null ess) = let es' = zipWith3 applyOp' ops seedsϵ (headMaybe <$> ess) in es' `seqAll` evalAll es' (tail' <$> ess)
-                       -- if I try to use the (all null ess) criterion it space
-                       -- leaks like crazy so... inspect only when we need?
-                       --
-                       -- (still leaks space... but less)
-                       | not (all null ess) = let es' = zipWith3 applyOp' ops seedsϵ (headMaybe <$> ess) in es' `seqAll` evalAll es' (tail' <$> ess)
-                       | otherwise = zip is seedsϵ
-
-    seqAll (e:es) z = foldr seq e es `seq` z
-    seqAll [] z     = z
-
-    applyOp' op seed (Just e) = applyOp op seed e
-    applyOp' _ seed Nothing   = seed
-
-    headMaybe []    = Nothing
-    headMaybe (x:_) = Just x
-
-    tail' []     = []
-    tail' (_:xs) = xs
-
-ungather :: IM.IntMap (E (T K)) -> E (T K) -> E (T K)
-ungather st (Var _ (Name _ (Unique i) _)) =
-    case IM.lookup i st of
-        Just res -> res
-        Nothing  -> throw InternalError
-ungather st (EApp ty e0 e1)  = EApp ty (ungather st e0) (ungather st e1)
-ungather st (Tup ty es)      = Tup ty (ungather st <$> es)
-ungather st (Arr ty es)      = Arr ty (ungather st <$> es)
-ungather st (OptionVal ty e) = OptionVal ty (ungather st <$> e)
-ungather _ e@BBuiltin{}      = e
-ungather _ e@UBuiltin{}      = e
-ungather _ (NBuiltin _ None) = OptionVal undefined Nothing
-ungather _ e@NBuiltin{}      = e
-ungather _ e@TBuiltin{}      = e
-ungather _ e@StrLit{}        = e
-ungather _ e@BoolLit{}       = e
-ungather _ e@FloatLit{}      = e
-ungather _ e@IntLit{}        = e
-
-mkFoldVar :: Int -> b -> E b
-mkFoldVar i l = Var l (Name "fold_placeholder" (Unique i) l)
-
-gatherFoldsM :: E (T K) -> State (Int, [(Int, E (T K), E (T K), E (T K))]) (E (T K))
-gatherFoldsM (EApp _ (EApp _ (EApp _ (TBuiltin (TyArr _ _ (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _))) Fold) op) seed) stream) = do
-    (i,_) <- get
-    modify (bimap (+1) ((i, op, seed, stream) :))
-    pure $ mkFoldVar i undefined
-gatherFoldsM (EApp ty e0 e1) = EApp ty <$> gatherFoldsM e0 <*> gatherFoldsM e1
-gatherFoldsM (Tup ty es) = Tup ty <$> traverse gatherFoldsM es
-gatherFoldsM (Arr ty es) = Arr ty <$> traverse gatherFoldsM es
-gatherFoldsM (OptionVal ty e) = OptionVal ty <$> traverse gatherFoldsM e
-gatherFoldsM (Cond ty p e e') = Cond ty <$> gatherFoldsM p <*> gatherFoldsM e <*> gatherFoldsM e'
-gatherFoldsM (NBuiltin _ None) = pure $ OptionVal undefined Nothing
-gatherFoldsM e@BBuiltin{} = pure e
-gatherFoldsM e@TBuiltin{} = pure e
-gatherFoldsM e@UBuiltin{} = pure e
-gatherFoldsM e@NBuiltin{} = pure e
-gatherFoldsM e@StrLit{} = pure e
-gatherFoldsM e@FloatLit{} = pure e
-gatherFoldsM e@IntLit{} = pure e
-gatherFoldsM e@BoolLit{} = pure e
-
-eWith :: RurePtr -> E (T K) -> [BS.ByteString] -> E (T K)
-eWith re (EApp _ (EApp _ (EApp _ (TBuiltin (TyArr _ _ (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _))) Fold) op) seed) stream) = foldWithCtx re op seed stream
-eWith re (EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _)) Fold1) op) stream)                          = fold1 re op stream
-eWith _ e@BBuiltin{}                                                                                                                 = const e
-eWith _ e@UBuiltin{}                                                                                                                 = const e
-eWith _ e@TBuiltin{}                                                                                                                 = const e
-eWith _ e@StrLit{}                                                                                                                   = const e
-eWith _ e@FloatLit{}                                                                                                                 = const e
-eWith _ e@IntLit{}                                                                                                                   = const e
-eWith _ e@BoolLit{}                                                                                                                  = const e
-eWith re e = \bs ->
-    let (eHoles, (_, folds)) = runState (gatherFoldsM e) (0, []) -- 0 state, should contain no vars by now
-        in eClosed undefined $ ungather (IM.fromList $ foldAll re folds bs) eHoles
-
-takeConcatMap :: (a -> [b]) -> [a] -> [b]
-takeConcatMap f = concat . transpose . fmap f
-
--- | Given an expression, turn it into a function which will process the file.
-fileProcessor :: RurePtr
-              -> E (T K)
-              -> Either StreamError ([BS.ByteString] -> IO ())
-fileProcessor _ AllField{}    = Left NakedField
-fileProcessor _ Field{}       = Left NakedField
-fileProcessor _ (NBuiltin _ Ix) = Left NakedField
-fileProcessor re e@AllColumn{} = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@Column{} = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@IParseCol{} = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@FParseCol{} = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@ParseCol{} = Right $ \inp -> printStream $ ir re e inp
-fileProcessor re e@Guarded{} = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@Implicit{} = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@(EApp _ (EApp _ (BBuiltin _ Filter) _) _) = Right $ \inp ->
-    printStream $ ir re e inp
--- at the moment, catMaybes only works on streams
-fileProcessor re e@(EApp _ (UBuiltin _ CatMaybes) _) = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@(EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyStream) _))) Map) _) _) = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@(EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ _ (TyApp _ (TyB _ TyStream) _))) MapMaybe) _) _) = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@(EApp _ (EApp _ (BBuiltin _ Prior) _) _) = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@(EApp _ (EApp _ (EApp _ (TBuiltin _ Scan) _) _) _) = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@(EApp _ (EApp _ (EApp _ (TBuiltin _ ZipW) _) _) _) = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re e@(EApp _ (UBuiltin _ Dedup) _) = Right $ \inp ->
-    printStream $ ir re e inp
-fileProcessor re (Anchor _ es) = Right $ \inp ->
-    printStream $ takeConcatMap (\e -> ir re e inp) es
-fileProcessor _ Var{} = error "Internal error?"
-fileProcessor _ e@IntLit{} = Right $ const (print e)
-fileProcessor _ e@BoolLit{} = Right $ const (print e)
-fileProcessor _ e@StrLit{} = Right $ const (print e)
-fileProcessor _ e@FloatLit{} = Right $ const (print e)
-fileProcessor _ e@RegexLit{} = Right $ const (print e)
-fileProcessor _ Lam{} = Left UnevalFun
-fileProcessor _ Dfn{} = badSugar
-fileProcessor _ ResVar{} = badSugar
-fileProcessor _ BBuiltin{} = Left UnevalFun
-fileProcessor _ UBuiltin{} = Left UnevalFun
-fileProcessor _ TBuiltin{} = Left UnevalFun
-fileProcessor re e = Right $ print . eWith re e
diff --git a/src/Jacinda/Check/Field.hs b/src/Jacinda/Check/Field.hs
new file mode 100644
--- /dev/null
+++ b/src/Jacinda/Check/Field.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Jacinda.Check.Field ( cF, LErr (..) ) where
+
+import           A
+import           Control.Applicative (Alternative (..))
+import           Control.Exception   (Exception)
+import           Data.Foldable       (asum)
+import           Prettyprinter       (Pretty (..), squotes, (<+>))
+
+data LErr = NF (E T) | TS (E T)
+
+instance Pretty LErr where
+    pretty (NF e) = "Naked field in expression" <+> squotes (pretty e)
+    pretty (TS e) = squotes (pretty e) <+> "Tuples cannot have streams."
+
+instance Show LErr where show=show.pretty
+
+instance Exception LErr where
+
+cF :: E T -> Maybe LErr
+cF e@(Tup (TyTup ts) _) | any isS ts = Just (TS e)
+cF e@Field{} = Just (NF e); cF e@AllField{} = Just (NF e); cF e@LastField{} = Just (NF e)
+cF e@(NB _ Ix) = Just (NF e); cF e@(NB _ Nf) = Just (NF e)
+cF IParseCol{} = Nothing; cF FParseCol{} = Nothing; cF ParseCol{} = Nothing; cF Column{} = Nothing
+cF AllColumn{} = Nothing; cF Guarded{} = Nothing; cF Implicit{} = Nothing; cF ILit{} = Nothing
+cF BLit{} = Nothing; cF RegexLit{} = Nothing; cF FLit{} = Nothing; cF StrLit{} = Nothing
+cF NB{} = Nothing; cF UB{} = Nothing; cF BB{} = Nothing; cF TB{} = Nothing
+cF Var{} = Nothing; cF (Tup _ es) = foldMapAlternative cF es; cF (Anchor _ es) = foldMapAlternative cF es
+cF (Arr _ es) = foldMapAlternative cF es; cF (EApp _ e e') = cF e <|> cF e'
+cF (Cond _ p e e') = cF p <|> cF e <|> cF e'; cF (OptionVal _ e) = foldMapAlternative cF e
+cF (Lam _ _ e) = cF e; cF Let{} = error "Inlining unexpectedly failed?"
+cF RC{} = error "Sanity check failed. Regex should not be compiled at this time."
+cF Dfn{} = desugar; cF Paren{} = desugar; cF ResVar{} = desugar
+
+isS :: T -> Bool
+isS (TyApp (TyB TyStream) _) = True; isS _ = False
+
+foldMapAlternative :: (Traversable t, Alternative f) => (a -> f b) -> t a -> f b
+foldMapAlternative f xs = asum (f <$> xs)
+
+desugar = error "Internal error. Should have been desugared by now."
diff --git a/src/Jacinda/File.hs b/src/Jacinda/File.hs
deleted file mode 100644
--- a/src/Jacinda/File.hs
+++ /dev/null
@@ -1,136 +0,0 @@
-module Jacinda.File ( tcIO
-                    , tySrc
-                    , runOnHandle
-                    , runOnFile
-                    , exprEval
-                    ) where
-
-import           Control.Applicative        ((<|>))
-import           Control.Exception          (Exception, throw, throwIO)
-import           Control.Monad              ((<=<))
-import           Control.Monad.IO.Class     (liftIO)
-import           Control.Monad.State.Strict (StateT, get, put, runStateT)
-import           Control.Recursion          (cata, embed)
-import           Data.Bifunctor             (second)
-import qualified Data.ByteString            as BS
-import qualified Data.ByteString.Char8      as ASCII
-import qualified Data.ByteString.Lazy       as BSL
-import qualified Data.ByteString.Lazy.Char8 as ASCIIL
-import           Data.Functor               (void, ($>))
-import           Data.Tuple                 (swap)
-import           Jacinda.AST
-import           Jacinda.Backend.Normalize
-import           Jacinda.Backend.TreeWalk
-import           Jacinda.Include
-import           Jacinda.Lexer
-import           Jacinda.Parser
-import           Jacinda.Parser.Rewrite
-import           Jacinda.Regex
-import           Jacinda.Rename
-import           Jacinda.Ty
-import           Regex.Rure                 (RurePtr)
-import           System.IO                  (Handle)
-
-parseLib :: [FilePath] -> FilePath -> StateT AlexUserState IO [D AlexPosn]
-parseLib incls fp = do
-    contents <- liftIO $ BSL.readFile =<< resolveImport incls fp
-    st <- get
-    case parseLibWithCtx contents st of
-        Left err              -> liftIO (throwIO err)
-        Right (st', ([], ds)) -> put st' $> (rewriteD <$> ds)
-        Right (st', (is, ds)) -> do { put st' ; dss <- traverse (parseLib incls) is ; pure (concat dss ++ fmap rewriteD ds) }
-
-parseE :: [FilePath] -> BSL.ByteString -> StateT AlexUserState IO (Program AlexPosn)
-parseE incls bs = do
-    st <- get
-    case parseWithCtx bs st of
-        Left err -> liftIO $ throwIO err
-        Right (st', (is, Program ds e)) -> do
-            put st'
-            dss <- traverse (parseLib incls) is
-            pure $ Program (concat dss ++ fmap rewriteD ds) (rewriteE e)
-
--- | Parse + rename (decls)
-parseEWithMax :: [FilePath] -> BSL.ByteString -> IO (Program AlexPosn, Int)
-parseEWithMax incls bsl = uncurry renamePGlobal . swap . second fst3 <$> runStateT (parseE incls bsl) alexInitUserState
-    where fst3 (x, _, _) = x
-
--- | Parse + rename (globally)
-parseWithMax' :: BSL.ByteString -> Either (ParseError AlexPosn) (Program AlexPosn, Int)
-parseWithMax' = fmap (uncurry renamePGlobal . second (rewriteProgram . snd)) . parseWithMax
-
-type FileBS = BS.ByteString
-
--- fill in regex with compiled.
-compileR :: FileBS
-         -> E (T K)
-         -> E (T K)
-compileR fp = cata a where
-    a (RegexLitF _ rrϵ) = RegexCompiled (compileDefault rrϵ)
-    a (NBuiltinF _ Fp)  = mkStr fp
-    a x                 = embed x
-
-compileIn :: FileBS -> Program (T K) -> Program (T K)
-compileIn fp (Program ds e) = Program (compileD fp <$> ds) (compileR fp e)
-
-compileD :: FileBS -> D (T K) -> D (T K)
-compileD _ d@SetFS{}        = d
-compileD fp (FunDecl n l e) = FunDecl n l (compileR fp e)
-
-exprEval :: BSL.ByteString -> E (T K)
-exprEval src =
-    case parseWithMax' src of
-        Left err -> throw err
-        Right (ast, m) ->
-            let (typed, i) = yeet $ runTypeM m (tyProgram ast)
-            in closedProgram i (compileIn undefined typed)
-
-compileFS :: Maybe BS.ByteString -> RurePtr
-compileFS (Just bs) = compileDefault bs
-compileFS Nothing   = defaultRurePtr
-
-runOnBytes :: [FilePath]
-           -> FilePath -- ^ Data file name, for @nf@
-           -> BSL.ByteString -- ^ Program
-           -> Maybe BS.ByteString -- ^ Field separator
-           -> BSL.ByteString
-           -> IO ()
-runOnBytes incls fp src cliFS contents = do
-    incls' <- defaultIncludes <*> pure incls
-    (ast, m) <- parseEWithMax incls' src
-    (typed, i) <- yeetIO $ runTypeM m (tyProgram ast)
-    cont <- yeetIO $ runJac (compileFS (cliFS <|> getFS ast)) i (compileIn (ASCII.pack fp) typed)
-    cont $ fmap BSL.toStrict (ASCIIL.lines contents)
-    -- TODO: BSL.split, BSL.splitWith for arbitrary record separators
-
-runOnHandle :: [FilePath]
-            -> BSL.ByteString -- ^ Program
-            -> Maybe BS.ByteString -- ^ Field separator
-            -> Handle
-            -> IO ()
-runOnHandle is src cliFS = runOnBytes is "(runOnBytes)" src cliFS <=< BSL.hGetContents
-
-runOnFile :: [FilePath]
-          -> BSL.ByteString
-          -> Maybe BS.ByteString
-          -> FilePath
-          -> IO ()
-runOnFile is e fs fp = runOnBytes is fp e fs =<< BSL.readFile fp
-
-tcIO :: [FilePath] -> BSL.ByteString -> IO ()
-tcIO incls src = do
-    incls' <- defaultIncludes <*> pure incls
-    (ast, m) <- parseEWithMax incls' src
-    yeetIO $ void $ runTypeM m (tyProgram ast)
-
-tySrc :: BSL.ByteString -> T K
-tySrc src =
-    case parseWithMax' src of
-        Right (ast, m) -> yeet $ fst <$> runTypeM m (tyOf (expr ast))
-        Left err       -> throw err
-
-yeetIO :: Exception e => Either e a -> IO a
-yeetIO = either throwIO pure
-
-yeet :: Exception e => Either e a -> a
-yeet = either throw id
diff --git a/src/Jacinda/Fuse.hs b/src/Jacinda/Fuse.hs
new file mode 100644
--- /dev/null
+++ b/src/Jacinda/Fuse.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Jacinda.Fuse ( fuse ) where
+
+import           A
+import           A.E
+import           Control.Monad.State.Strict (runState)
+import           Ty.Const
+
+fuse :: Int -> E T -> (E T, Int)
+fuse i = flip runState i.fM
+
+-- fold1: needs a special "form" for fold1-of-map (pick seed through map)
+-- also "filter-of-fold1" b/c we need to pick a seed that isn't filtered.
+
+fM :: E T -> M (E T)
+fM (EApp t0 (EApp t1 (EApp t2 ho@(TB _ Fold) op) seed) stream) | TyApp (TyB TyStream) _ <- eLoc stream = do
+    stream' <- fM stream
+    case stream' of
+        (EApp _ (EApp _ (BB _ Filter) p) xs) -> do
+            let opTy@(TyArr sTy popTy@(TyArr xTy _)) = eLoc op
+            s <- nN "seed" sTy; x <- nN "x" xTy
+            let sE=Var sTy s; xE=Var xTy x
+            let fop=Lam opTy s (Lam popTy x (Cond sTy (EApp tyB p xE) (EApp sTy (EApp popTy op sE) xE) sE)) in pure (EApp t0 (EApp t1 (EApp t2 ho fop) seed) xs)
+        (Guarded t p e) -> do
+            let opTy@(TyArr sTy popTy@(TyArr xTy _)) = eLoc op
+            s <- nN "seed" sTy; x <- nN "x" xTy
+            let sE=Var sTy s; xE=Var xTy x
+            let fop=Lam opTy s (Lam popTy x (Cond sTy p (EApp sTy (EApp popTy op sE) xE) sE)) in pure (EApp t0 (EApp t1 (EApp t2 ho fop) seed) (Implicit t e))
+            -- FIXME: does this evaluate e? (could be exception)
+        (EApp _ (EApp _ (BB _ Map) f) xs) -> do
+            let (TyArr xTy yTy) = eLoc f
+                (TyArr sTy _) = eLoc op
+            s <- nN "seed" sTy; x <- nN "x" xTy
+            let sE=Var sTy s; xE=Var xTy x
+                popT=TyArr xTy sTy; fopT=TyArr sTy popT
+                fop=Lam fopT s (Lam popT x (EApp undefined (EApp undefined op sE) (EApp yTy f xE)))
+            fM (EApp sTy (EApp undefined (EApp undefined (TB (TyArr fopT (TyArr sTy (TyArr (TyApp (TyB TyStream) xTy) sTy))) Fold) fop) seed) xs)
+        (EApp _ (EApp _ (BB _ MapMaybe) f) xs) -> do
+            -- op | seed (f:?xs) -> [option x (x `op`) (f y)] | seed xs
+            let TyArr xT yT=eLoc f
+                sT=eLoc seed
+            s <- nN "seed" sT; x <- nN "x" xT
+            let sE=Var sT s; xE=Var xT x
+                popT=TyArr xT sT; fopT=TyArr sT popT
+                fop=Lam fopT s (Lam popT x (EApp sT (EApp undefined (EApp undefined (TB (TyArr sT (TyArr undefined (TyArr yT sT))) Option) sE) (EApp undefined op sE)) (EApp yT f xE)))
+            fM (EApp sT (EApp undefined (EApp undefined (TB (TyArr fopT (TyArr sT (TyArr (TyApp (TyB TyStream) xT) sT))) Fold) fop) seed) xs)
+        (EApp _ (UB _ CatMaybes) xs) -> do
+            -- op | seed (.? xs) -> [option x (x `op`) y] | seed xs
+            let TyArr _ (TyArr xTy _)=eLoc op
+                xMT=tyOpt xTy
+                sTy=eLoc seed
+            s <- nN "seed" sTy; x <- nN "x" xMT
+            let sE=Var sTy s; xE=Var xMT x
+                popT=TyArr xMT sTy; fopT=TyArr sTy popT
+                fop=Lam fopT s (Lam popT x (EApp sTy (EApp undefined (EApp undefined (TB (TyArr sTy (TyArr undefined (TyArr xMT sTy))) Option) sE) (EApp undefined op sE)) xE))
+            fM (EApp sTy (EApp undefined (EApp undefined (TB (TyArr fopT (TyArr sTy (TyArr (TyApp (TyB TyStream) xMT) sTy))) Fold) fop) seed) xs)
+        _ -> pure (EApp t0 (EApp t1 (EApp t2 ho op) seed) stream')
+fM (Tup t es) = Tup t <$> traverse fM es
+fM (EApp t e0 e1) = EApp t <$> fM e0 <*> fM e1
+fM (Lam t n e) = Lam t n <$> fM e
+fM e = pure e
diff --git a/src/Jacinda/Include.hs b/src/Jacinda/Include.hs
deleted file mode 100644
--- a/src/Jacinda/Include.hs
+++ /dev/null
@@ -1,37 +0,0 @@
-module Jacinda.Include ( defaultIncludes
-                       , resolveImport
-                       ) where
-
-import           Control.Exception  (Exception, throwIO)
-import           Control.Monad      (filterM)
-import           Data.List.Split    (splitWhen)
-import           Data.Maybe         (listToMaybe)
-import           Paths_jacinda      (getDataDir)
-import           System.Directory   (doesFileExist, getCurrentDirectory)
-import           System.Environment (lookupEnv)
-import           System.FilePath    ((</>))
-
-data ImportError = FileNotFound !FilePath ![FilePath] deriving (Show)
-
-instance Exception ImportError where
-
-defaultIncludes :: IO ([FilePath] -> [FilePath])
-defaultIncludes = do
-    path <- jacPath
-    d <- getDataDir
-    dot <- getCurrentDirectory
-    pure $ (dot:) . (d:) . (++path)
-
--- | Parsed @JAC_PATH@
-jacPath :: IO [FilePath]
-jacPath = maybe [] splitEnv <$> lookupEnv "JAC_PATH"
-
-splitEnv :: String -> [FilePath]
-splitEnv = splitWhen (== ':')
-
-resolveImport :: [FilePath] -- ^ Places to look
-              -> FilePath
-              -> IO FilePath
-resolveImport incl fp =
-    maybe (throwIO $ FileNotFound fp incl) pure . listToMaybe
-        =<< (filterM doesFileExist . fmap (</> fp) $ incl)
diff --git a/src/Jacinda/Lexer.x b/src/Jacinda/Lexer.x
deleted file mode 100644
--- a/src/Jacinda/Lexer.x
+++ /dev/null
@@ -1,487 +0,0 @@
-{
-    {-# LANGUAGE OverloadedStrings #-}
-    {-# LANGUAGE StandaloneDeriving #-}
-    module Jacinda.Lexer ( alexMonadScan
-                         , alexInitUserState
-                         , runAlex
-                         , runAlexSt
-                         , withAlexSt
-                         , freshName
-                         , AlexPosn (..)
-                         , Alex (..)
-                         , Token (..)
-                         , Keyword (..)
-                         , Sym (..)
-                         , Builtin (..)
-                         , Var (..)
-                         , AlexUserState
-                         ) where
-
-import Control.Arrow ((&&&))
-import Data.Bifunctor (first)
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as BSL
-import qualified Data.ByteString.Lazy.Char8 as ASCII
-import Data.Functor (($>))
-import qualified Data.IntMap as IM
-import qualified Data.Map as M
-import Data.Semigroup ((<>))
-import qualified Data.Text as T
-import Data.Text.Encoding (decodeUtf8, encodeUtf8)
-import Intern.Name
-import Intern.Unique
-import Prettyprinter (Pretty (pretty), (<+>), colon, squotes)
-
-}
-
-%wrapper "monadUserState-bytestring"
-
-$digit = [0-9]
-
-$latin = [a-zA-Z]
-
-@follow_char = [$latin $digit \_]
-
-$str_special = [\\\']
-
-@escape_str = \\ [$str_special nt]
-
-@string = \' ([^ $str_special] | @escape_str)* \'
-
-@name = [a-z] @follow_char*
-@tyname = [A-Z] @follow_char*
-
-@float = $digit+\.$digit+
-
-tokens :-
-
-    <dfn> {
-        x                        { mkRes VarX }
-        y                        { mkRes VarY }
-    }
-
-    <0> "["                      { mkSym LSqBracket `andBegin` dfn } -- FIXME: this doesn't allow nested
-
-    <0,dfn> {
-
-        $white+                  ;
-
-        "{.".*                   ;
-        "#!".*                   ; -- shebang
-
-        ":="                     { mkSym DefEq }
-        "≔"                      { mkSym DefEq }
-        "{"                      { mkSym LBrace }
-        "}"                      { mkSym RBrace }
-
-        "#."                     { mkSym FilterTok }
-
-        -- symbols/operators
-        "%"                      { mkSym PercentTok }
-        "*"                      { mkSym TimesTok }
-        "+"                      { mkSym PlusTok }
-        "-"                      { mkSym MinusTok }
-
-        "|"                      { mkSym FoldTok }
-        \"                       { mkSym Quot }
-        ¨                        { mkSym Quot }
-        "^"                      { mkSym Caret }
-        "|>"                     { mkSym Fold1Tok }
-
-        "="                      { mkSym EqTok }
-        "!="                     { mkSym NeqTok }
-        "<="                     { mkSym LeqTok }
-        "<"                      { mkSym LtTok }
-        ">="                     { mkSym GeqTok }
-        ">"                      { mkSym GtTok }
-        "&"                      { mkSym AndTok }
-        "||"                     { mkSym OrTok }
-        "("                      { mkSym LParen }
-        ")"                      { mkSym RParen }
-        "&("                     { mkSym LAnchor }
-        "{%"                     { mkSym LBracePercent }
-        "{|"                     { mkSym LBraceBar }
-        "]"                      { mkSym RSqBracket `andBegin` 0 }
-        "~"                      { mkSym Tilde }
-        "!~"                     { mkSym NotMatchTok }
-        ","                      { mkSym Comma }
-        "."                      { mkSym Dot }
-        "#"                      { mkSym TallyTok }
-        "#*"                     { mkSym LengthTok }
-        "[:"                     { mkSym ConstTok }
-        "!"                      { mkSym Exclamation }
-        ":"                      { mkSym Colon }
-        ";"                      { mkSym Semicolon }
-        "\."                     { mkSym BackslashDot }
-        \\                       { mkSym Backslash }
-        λ                        { mkSym Backslash }
-        "|`"                     { mkSym CeilSym }
-        "|."                     { mkSym FloorSym }
-        "~."                     { mkSym DedupTok }
-        ".?"                     { mkSym CatMaybesTok }
-        ":?"                     { mkSym MapMaybeTok }
-        "~*"                     { mkSym CapTok }
-        "-."                     { mkSym NegTok }
-        "`*"                     { mkSym LastFieldTok }
-
-        in                       { mkKw KwIn }
-        let                      { mkKw KwLet }
-        val                      { mkKw KwVal }
-        end                      { mkKw KwEnd }
-        :set                     { mkKw KwSet }
-        fn                       { mkKw KwFn }
-        "@include"               { mkKw KwInclude }
-        if                       { mkKw KwIf }
-        then                     { mkKw KwThen }
-        else                     { mkKw KwElse }
-
-        fs                       { mkRes VarFs }
-        ix                       { mkRes VarIx }
-        ⍳                        { mkRes VarIx }
-        nf                       { mkRes VarNf }
-        -- TODO: does this uncover an alex bug?
-        -- ⍳                        { mkRes VarIx }
-        -- ¨                        { mkSym Quot }
-        min                      { mkRes VarMin }
-        max                      { mkRes VarMax }
-
-        substr                   { mkBuiltin BuiltinSubstr }
-        split                    { mkBuiltin BuiltinSplit }
-        splitc                   { mkBuiltin BuiltinSplitc }
-        sprintf                  { mkBuiltin BuiltinSprintf }
-        option                   { mkBuiltin BuiltinOption }
-        floor                    { mkBuiltin BuiltinFloor }
-        ceil                     { mkBuiltin BuiltinCeil }
-        match                    { mkBuiltin BuiltinMatch }
-        captures                 { mkBuiltin BuiltinCaptures }
-        Some                     { mkBuiltin BuiltinSome }
-        None                     { mkBuiltin BuiltinNone }
-        fp                       { mkBuiltin BuiltinFp }
-
-        ":i"                     { mkBuiltin BuiltinIParse }
-        ":f"                     { mkBuiltin BuiltinFParse }
-
-        "#t"                     { tok (\p _ -> alex $ TokBool p True) }
-        "#f"                     { tok (\p _ -> alex $ TokBool p False) }
-
-        \$$digit+                { tok (\p s -> alex $ TokStreamLit p (read $ ASCII.unpack $ BSL.tail s)) }
-        `$digit+                 { tok (\p s -> alex $ TokFieldLit p (read $ ASCII.unpack $ BSL.tail s)) }
-
-        "."$digit+               { tok (\p s -> alex $ TokAccess p (read $ ASCII.unpack $ ASCII.tail s)) }
-        "->"$digit+              { tok (\p s -> alex $ TokSelect p (read $ ASCII.unpack $ ASCII.drop 2 s)) }
-        $digit+                  { tok (\p s -> alex $ TokInt p (read $ ASCII.unpack s)) }
-        _$digit+                 { tok (\p s -> alex $ TokInt p (negate $ read $ ASCII.unpack $ BSL.tail s)) }
-
-        $digit+\.$digit+         { tok (\p s -> alex $ TokFloat p (read $ ASCII.unpack s)) }
-        _$digit+\.$digit+        { tok (\p s -> alex $ TokFloat p (negate $ read $ ASCII.unpack $ BSL.tail s)) }
-
-        @string                  { tok (\p s -> alex $ TokStr p (escReplace' $ BSL.init $ BSL.tail s)) }
-
-        -- TODO: allow chars to be escaped
-        "/"[^\/]*"/"             { tok (\p s -> alex $ TokRR p (BSL.init $ BSL.tail s)) }
-
-        @name                    { tok (\p s -> TokName p <$> newIdentAlex p (mkText s)) }
-        @tyname                  { tok (\p s -> TokTyName p <$> newIdentAlex p (mkText s)) }
-
-    }
-
-{
-
-dropQuotes :: BSL.ByteString -> BSL.ByteString
-dropQuotes = BSL.init . BSL.tail
-
-alex :: a -> Alex a
-alex = pure
-
-tok f (p,_,s,_) len = f p (BSL.take len s)
-
-constructor c t = tok (\p _ -> alex $ c p t)
-
-mkRes = constructor TokResVar
-
-mkKw = constructor TokKeyword
-
-mkSym = constructor TokSym
-
-mkBuiltin = constructor TokBuiltin
-
-escReplace' :: BSL.ByteString -> BS.ByteString
-escReplace' = encodeUtf8 . escReplace . decodeUtf8 . BSL.toStrict
-
--- this is inefficient but w/e
-escReplace :: T.Text -> T.Text
-escReplace =
-      T.replace "\\\"" "\""
-    . T.replace "\\n" "\n"
-    . T.replace "\\t" "\t"
-
-mkText :: BSL.ByteString -> T.Text
-mkText = decodeUtf8 . BSL.toStrict
-
-instance Pretty AlexPosn where
-    pretty (AlexPn _ line col) = pretty line <> colon <> pretty col
-
-deriving instance Ord AlexPosn
-
--- functional bimap?
-type AlexUserState = (Int, M.Map T.Text Int, IM.IntMap (Name AlexPosn))
-
-alexInitUserState :: AlexUserState
-alexInitUserState = (0, mempty, mempty)
-
-gets_alex :: (AlexState -> a) -> Alex a
-gets_alex f = Alex (Right . (id &&& f))
-
-get_ust :: Alex AlexUserState
-get_ust = gets_alex alex_ust
-
-get_pos :: Alex AlexPosn
-get_pos = gets_alex alex_pos
-
-set_ust :: AlexUserState -> Alex ()
-set_ust st = Alex (Right . (go &&& (const ())))
-    where go s = s { alex_ust = st }
-
-alexEOF = EOF <$> get_pos
-
-data Sym = PlusTok
-         | MinusTok
-         | PercentTok
-         | FoldTok
-         | Fold1Tok
-         | Quot
-         | TimesTok
-         | DefEq
-         | Colon
-         | LBrace
-         | RBrace
-         | LParen
-         | LAnchor
-         | RParen
-         | LSqBracket
-         | RSqBracket
-         | Semicolon
-         | Underscore
-         | EqTok
-         | LeqTok
-         | LtTok
-         | NeqTok
-         | GeqTok
-         | GtTok
-         | AndTok
-         | OrTok
-         | Tilde
-         | NotMatchTok
-         | Comma
-         | Dot
-         | TallyTok
-         | LengthTok
-         | ConstTok
-         | LBracePercent
-         | LBraceBar
-         | Exclamation
-         | Caret
-         | Backslash
-         | BackslashDot
-         | FilterTok
-         | FloorSym
-         | CeilSym
-         | DedupTok
-         | CatMaybesTok
-         | MapMaybeTok
-         | CapTok
-         | NegTok
-         | LastFieldTok
-
-instance Pretty Sym where
-    pretty PlusTok       = "+"
-    pretty MinusTok      = "-"
-    pretty PercentTok    = "%"
-    pretty FoldTok       = "|"
-    pretty Fold1Tok      = "|>"
-    pretty TimesTok      = "*"
-    pretty DefEq         = ":="
-    pretty Colon         = ":"
-    pretty LBrace        = "{"
-    pretty RBrace        = "}"
-    pretty Semicolon     = ";"
-    pretty Underscore    = "_"
-    pretty EqTok         = "="
-    pretty LeqTok        = "<="
-    pretty LtTok         = "<"
-    pretty NeqTok        = "!="
-    pretty GeqTok        = ">="
-    pretty GtTok         = ">"
-    pretty AndTok        = "&"
-    pretty OrTok         = "||"
-    pretty LParen        = "("
-    pretty RParen        = ")"
-    pretty LAnchor       = "&("
-    pretty LSqBracket    = "["
-    pretty RSqBracket    = "]"
-    pretty Tilde         = "~"
-    pretty NotMatchTok   = "!~"
-    pretty Comma         = ","
-    pretty Dot           = "."
-    pretty TallyTok      = "#"
-    pretty LengthTok     = "#*"
-    pretty Quot          = "\""
-    pretty Caret         = "^"
-    pretty ConstTok      = "[:"
-    pretty LBracePercent = "{%"
-    pretty LBraceBar     = "{|"
-    pretty Exclamation   = "!"
-    pretty Backslash     = "\\"
-    pretty BackslashDot  = "\\."
-    pretty FilterTok     = "#."
-    pretty FloorSym      = "|."
-    pretty CeilSym       = "|`"
-    pretty DedupTok      = "~."
-    pretty CatMaybesTok  = ".?"
-    pretty MapMaybeTok   = ":?"
-    pretty CapTok        = "~*"
-    pretty NegTok        = "-."
-    pretty LastFieldTok  = "`*"
-
-data Keyword = KwLet
-             | KwIn
-             | KwVal
-             | KwEnd
-             | KwSet
-             | KwFn
-             | KwInclude
-             | KwIf
-             | KwThen
-             | KwElse
-
--- | Reserved/special variables
-data Var = VarX
-         | VarY
-         | VarFs
-         | VarIx
-         | VarMin
-         | VarMax
-         | VarNf
-
-instance Pretty Var where
-    pretty VarX     = "x"
-    pretty VarY     = "y"
-    pretty VarFs    = "fs"
-    pretty VarIx    = "ix"
-    pretty VarNf    = "nf"
-    pretty VarMin   = "min"
-    pretty VarMax   = "max"
-    -- TODO: exp, log, sqrt, floor ...
-
-instance Pretty Keyword where
-    pretty KwLet     = "let"
-    pretty KwIn      = "in"
-    pretty KwVal     = "val"
-    pretty KwEnd     = "end"
-    pretty KwSet     = ":set"
-    pretty KwFn      = "fn"
-    pretty KwInclude = "@include"
-    pretty KwIf      = "if"
-    pretty KwThen    = "then"
-    pretty KwElse    = "else"
-
-data Builtin = BuiltinIParse
-             | BuiltinFParse
-             | BuiltinSubstr
-             | BuiltinSplit
-             | BuiltinSplitc
-             | BuiltinOption
-             | BuiltinSprintf
-             | BuiltinFloor
-             | BuiltinCeil
-             | BuiltinMatch
-             | BuiltinCaptures
-             | BuiltinSome
-             | BuiltinNone
-             | BuiltinFp
-
-instance Pretty Builtin where
-    pretty BuiltinIParse   = ":i"
-    pretty BuiltinFParse   = ":f"
-    pretty BuiltinSubstr   = "substr"
-    pretty BuiltinSplit    = "split"
-    pretty BuiltinOption   = "option"
-    pretty BuiltinSplitc   = "splitc"
-    pretty BuiltinSprintf  = "sprintf"
-    pretty BuiltinFloor    = "floor"
-    pretty BuiltinCeil     = "ceil"
-    pretty BuiltinMatch    = "match"
-    pretty BuiltinSome     = "Some"
-    pretty BuiltinNone     = "None"
-    pretty BuiltinFp       = "fp"
-    pretty BuiltinCaptures = "captures"
-
-data Token a = EOF { loc :: a }
-             | TokSym { loc :: a, _sym :: Sym }
-             | TokName { loc :: a, _name :: Name a }
-             | TokTyName { loc :: a, _tyName :: TyName a }
-             | TokBuiltin { loc :: a, _builtin :: Builtin }
-             | TokKeyword { loc :: a, _kw :: Keyword }
-             | TokResVar { loc :: a, _var :: Var }
-             | TokInt { loc :: a, int :: Integer }
-             | TokFloat { loc :: a, float :: Double }
-             | TokBool { loc :: a, boolTok :: Bool }
-             | TokStr { loc :: a, strTok :: BS.ByteString }
-             | TokStreamLit { loc :: a, ix :: Int }
-             | TokFieldLit { loc :: a, ix :: Int }
-             | TokRR { loc :: a, rr :: BSL.ByteString }
-             | TokAccess { loc :: a, ix :: Int }
-             | TokSelect { loc :: a, field :: Int }
-
-instance Pretty (Token a) where
-    pretty EOF{}              = "(eof)"
-    pretty (TokSym _ s)       = "symbol" <+> squotes (pretty s)
-    pretty (TokName _ n)      = "identifier" <+> squotes (pretty n)
-    pretty (TokTyName _ tn)   = "identifier" <+> squotes (pretty tn)
-    pretty (TokBuiltin _ b)   = "builtin" <+> squotes (pretty b)
-    pretty (TokKeyword _ kw)  = "keyword" <+> squotes (pretty kw)
-    pretty (TokInt _ i)       = pretty i
-    pretty (TokStr _ str)     = squotes (pretty $ decodeUtf8 str)
-    pretty (TokStreamLit _ i) = "$" <> pretty i
-    pretty (TokFieldLit _ i)  = "`" <> pretty i
-    pretty (TokRR _ rr')      = "/" <> pretty (mkText rr') <> "/"
-    pretty (TokResVar _ v)    = "reserved variable" <+> squotes (pretty v)
-    pretty (TokBool _ True)   = "#t"
-    pretty (TokBool _ False)  = "#f"
-    pretty (TokAccess _ i)    = "." <> pretty i
-    pretty (TokFloat _ f)     = pretty f
-    pretty (TokSelect _ i)    = "->" <> pretty i
-
-freshName :: T.Text -> Alex (Name AlexPosn)
-freshName t = do
-    pos <- get_pos
-    newIdentAlex pos t
-
-newIdentAlex :: AlexPosn -> T.Text -> Alex (Name AlexPosn)
-newIdentAlex pos t = do
-    st <- get_ust
-    let (st', n) = newIdent pos t st
-    set_ust st' $> (n $> pos)
-
-newIdent :: AlexPosn -> T.Text -> AlexUserState -> (AlexUserState, Name AlexPosn)
-newIdent pos t pre@(max', names, uniqs) =
-    case M.lookup t names of
-        Just i -> (pre, Name t (Unique i) pos)
-        Nothing -> let i = max' + 1
-            in let newName = Name t (Unique i) pos
-                in ((i, M.insert t i names, IM.insert i newName uniqs), newName)
-
-runAlexSt :: BSL.ByteString -> Alex a -> Either String (AlexUserState, a)
-runAlexSt inp = withAlexSt inp alexInitUserState
-
-withAlexSt :: BSL.ByteString -> AlexUserState -> Alex a -> Either String (AlexUserState, a)
-withAlexSt inp ust (Alex f) = first alex_ust <$> f
-    (AlexState { alex_bpos = 0
-               , alex_pos = alexStartPos
-               , alex_inp = inp
-               , alex_chr = '\n'
-               , alex_ust = ust
-               , alex_scd = 0
-               })
-
-}
diff --git a/src/Jacinda/Parser.y b/src/Jacinda/Parser.y
deleted file mode 100644
--- a/src/Jacinda/Parser.y
+++ /dev/null
@@ -1,354 +0,0 @@
-{
-    {-# LANGUAGE OverloadedStrings #-}
-    module Jacinda.Parser ( parse
-                          , parseWithMax
-                          , parseWithInitCtx
-                          , parseWithCtx
-                          , parseLibWithCtx
-                          , ParseError (..)
-                          -- * Type synonyms
-                          , File
-                          , Library
-                          ) where
-
-import Control.Exception (Exception)
-import Control.Monad.Except (ExceptT, runExceptT, throwError)
-import Control.Monad.Trans.Class (lift)
-import Data.Bifunctor (first)
-import qualified Data.ByteString.Lazy as BSL
-import qualified Data.ByteString.Char8 as ASCII
-import qualified Data.Text as T
-import Data.Typeable (Typeable)
-import qualified Intern.Name as Name
-import Intern.Name hiding (loc)
-import Jacinda.AST
-import Jacinda.Lexer
-import Prettyprinter (Pretty (pretty), (<+>))
-
-}
-
-%name parseF File
-%name parseLib Library
-%tokentype { Token AlexPosn }
-%error { parseError }
-%monad { Parse } { (>>=) } { pure }
-%lexer { lift alexMonadScan >>= } { EOF _ }
-
-%token
-
-    defEq { TokSym $$ DefEq }
-    colon { TokSym $$ Colon }
-    lbrace { TokSym $$ LBrace }
-    rbrace { TokSym $$ RBrace }
-    lsqbracket { TokSym $$ LSqBracket }
-    rsqbracket { TokSym $$ RSqBracket }
-    lparen { TokSym $$ LParen }
-    lanchor { TokSym $$ LAnchor }
-    rparen { TokSym $$ RParen }
-    semicolon { TokSym $$ Semicolon }
-    backslash { TokSym $$ Backslash }
-    tilde { TokSym $$ Tilde }
-    notMatch { TokSym $$ NotMatchTok }
-    dot { TokSym $$ Dot }
-    lbracePercent { TokSym $$ LBracePercent }
-    lbraceBar { TokSym $$ LBraceBar }
-    tally { TokSym $$ TallyTok }
-    tallyL { TokSym $$ LengthTok }
-    const { TokSym $$ ConstTok }
-    filter { TokSym $$ FilterTok }
-    exclamation { TokSym $$ Exclamation }
-    backslashdot { TokSym $$ BackslashDot }
-    at { $$@(TokAccess _ _) }
-    select { $$@(TokSelect _ _) }
-    floorSym { TokSym $$ FloorSym }
-    ceilSym { TokSym $$ CeilSym }
-    dedup { TokSym $$ DedupTok }
-
-    plus { TokSym $$ PlusTok }
-    minus { TokSym $$ MinusTok }
-    times { TokSym $$ TimesTok }
-    percent { TokSym $$ PercentTok }
-
-    comma { TokSym $$ Comma }
-    fold { TokSym $$ FoldTok }
-    fold1 { TokSym $$ Fold1Tok }
-    caret { TokSym $$ Caret }
-    quot { TokSym $$ Quot }
-    mapMaybe { TokSym $$ MapMaybeTok }
-    catMaybes { TokSym $$ CatMaybesTok }
-    capture { TokSym $$ CapTok }
-    neg { TokSym $$ NegTok }
-
-    eq { TokSym $$ EqTok }
-    neq { TokSym $$ NeqTok }
-    leq { TokSym $$ LeqTok }
-    lt { TokSym $$ LtTok }
-    geq { TokSym $$ GeqTok }
-    gt { TokSym $$ GtTok }
-
-    and { TokSym $$ AndTok }
-    or { TokSym $$ OrTok }
-
-    name { TokName _ $$ }
-    tyName { TokTyName  _ $$ }
-
-    intLit { $$@(TokInt _ _) }
-    floatLit { $$@(TokFloat _ _) }
-    boolLit { $$@(TokBool _ _) }
-    strLit { $$@(TokStr _ _) }
-    allColumn { TokStreamLit $$ 0 }
-    allField { TokFieldLit $$ 0 }
-    column { $$@(TokStreamLit _ _) }
-    field { $$@(TokFieldLit _ _) }
-    lastField { TokSym $$ LastFieldTok } -- TokSym is maybe insensible but whatever
-
-    let { TokKeyword $$ KwLet }
-    in { TokKeyword $$ KwIn }
-    val { TokKeyword $$ KwVal }
-    end { TokKeyword $$ KwEnd }
-    set { TokKeyword $$ KwSet }
-    fn { TokKeyword $$ KwFn }
-    include { TokKeyword $$ KwInclude }
-    if { TokKeyword $$ KwIf }
-    then { TokKeyword $$ KwThen }
-    else { TokKeyword $$ KwElse }
-
-    x { TokResVar $$ VarX }
-    y { TokResVar $$ VarY }
-
-    min { TokResVar $$ VarMin }
-    max { TokResVar $$ VarMax }
-    ix { TokResVar $$ VarIx }
-    nf { TokResVar $$ VarNf }
-    fs { TokResVar $$ VarFs }
-
-    split { TokBuiltin $$ BuiltinSplit }
-    splitc { TokBuiltin $$ BuiltinSplitc }
-    substr { TokBuiltin $$ BuiltinSubstr }
-    sprintf { TokBuiltin $$ BuiltinSprintf }
-    floor { TokBuiltin $$ BuiltinFloor }
-    ceil { TokBuiltin $$ BuiltinCeil }
-    option { TokBuiltin $$ BuiltinOption }
-    match { TokBuiltin $$ BuiltinMatch }
-    some { TokBuiltin $$ BuiltinSome }
-    none { TokBuiltin $$ BuiltinNone }
-    fp { TokBuiltin $$ BuiltinFp }
-    captures { TokBuiltin $$ BuiltinCaptures }
-
-    iParse { TokBuiltin $$ BuiltinIParse }
-    fParse { TokBuiltin $$ BuiltinFParse }
-
-    rr { $$@(TokRR _ _) }
-
-%right const
-%left paren iParse fParse
-%nonassoc leq geq gt lt neq eq
-
-%%
-
-many(p)
-    : many(p) p { $2 : $1 }
-    | { [] }
-
-sepBy(p,q)
-    : sepBy(p,q) q p { $3 : $1 }
-    | p q p { $3 : [$1] }
-
-braces(p)
-    : lbrace p rbrace { $2 }
-
-brackets(p)
-    : lsqbracket p rsqbracket { $2 }
-
-parens(p)
-    : lparen p rparen { $2 }
-
--- binary operator
-BBin :: { BBin }
-     : plus { Plus }
-     | times { Times }
-     | minus { Minus }
-     | percent { Div }
-     | gt { Gt }
-     | lt { Lt }
-     | geq { Geq }
-     | leq { Leq }
-     | eq { Eq }
-     | neq { Neq }
-     | quot { Map }
-     | mapMaybe { MapMaybe }
-     | tilde { Matches }
-     | notMatch { NotMatches }
-     | and { And }
-     | or { Or }
-     | backslashdot { Prior }
-     | filter { Filter }
-     | fold1 { Fold1 }
-
-Bind :: { (Name AlexPosn, E AlexPosn) }
-     : val name defEq E { ($2, $4) }
-
-Args :: { [(Name AlexPosn)] }
-     : lparen rparen { [] }
-     | parens(name) { [$1] }
-     | parens(sepBy(name, comma)) { reverse $1 }
-
-D :: { D AlexPosn }
-  : set fs defEq rr semicolon { SetFS (BSL.toStrict $ rr $4) }
-  | fn name Args defEq E semicolon { FunDecl $2 $3 $5 }
-  | fn name defEq E semicolon { FunDecl $2 [] $4 }
-
-Include :: { FilePath }
-        : include strLit { ASCII.unpack (strTok $2) }
-
-File :: { ([FilePath], Program AlexPosn) }
-     : many(Include) Program { (reverse $1, $2) }
-
-Library :: { Library }
-        : many(Include) many(D) { (reverse $1, reverse $2) }
-
-Program :: { Program AlexPosn }
-        : many(D) E { Program (reverse $1) $2 }
-
-E :: { E AlexPosn }
-  : name { Var (Name.loc $1) $1 }
-  | intLit { IntLit (loc $1) (int $1) }
-  | floatLit { FloatLit (loc $1) (float $1) }
-  | boolLit { BoolLit (loc $1) (boolTok $1) }
-  | strLit { StrLit (loc $1) (strTok $1) }
-  | column { Column (loc $1) (ix $1) }
-  | field { Field (loc $1) (ix $1) }
-  | allColumn { AllColumn $1 }
-  | allField { AllField $1 }
-  | lastField { LastField $1 }
-  | field iParse { EApp (loc $1) (UBuiltin $2 IParse) (Field (loc $1) (ix $1)) }
-  | field fParse { EApp (loc $1) (UBuiltin $2 FParse) (Field (loc $1) (ix $1)) }
-  | name iParse { EApp (Name.loc $1) (UBuiltin $2 IParse) (Var (Name.loc $1) $1) }
-  | name fParse { EApp (Name.loc $1) (UBuiltin $2 FParse) (Var (Name.loc $1) $1) }
-  | field colon { EApp (loc $1) (UBuiltin $2 Parse) (Field (loc $1) (ix $1)) }
-  | name colon { EApp (Name.loc $1) (UBuiltin $2 Parse) (Var (Name.loc $1) $1) }
-  | lastField iParse { EApp $1 (UBuiltin $2 IParse) (LastField $1) }
-  | lastField fParse { EApp $1 (UBuiltin $2 FParse) (LastField $1) }
-  | lastField colon { EApp $1 (UBuiltin $2 Parse) (LastField $1) }
-  | x colon { EApp $1 (UBuiltin $2 Parse) (ResVar $1 X) }
-  | y colon { EApp $1 (UBuiltin $2 Parse) (ResVar $1 Y) }
-  | x iParse { EApp $1 (UBuiltin $2 IParse) (ResVar $1 X) }
-  | x fParse { EApp $1 (UBuiltin $2 FParse) (ResVar $1 X) }
-  | y iParse { EApp $1 (UBuiltin $2 IParse) (ResVar $1 Y) }
-  | y fParse { EApp $1 (UBuiltin $2 FParse) (ResVar $1 Y) }
-  | column iParse { IParseCol (loc $1) (ix $1) }
-  | column fParse { FParseCol (loc $1) (ix $1) }
-  | column colon { ParseCol (loc $1) (ix $1) }
-  | parens(iParse) { UBuiltin $1 IParse }
-  | parens(fParse) { UBuiltin $1 FParse }
-  | parens(colon) { UBuiltin $1 Parse }
-  | lparen BBin rparen { BBuiltin $1 $2 }
-  | lparen E BBin rparen { EApp $1 (BBuiltin $1 $3) $2 }
-  | lparen BBin E rparen {% do { n <- lift $ freshName "x" ; pure (Lam $1 n (EApp $1 (EApp $1 (BBuiltin $1 $2) (Var (Name.loc n) n)) $3)) } }
-  | E BBin E { EApp (eLoc $1) (EApp (eLoc $3) (BBuiltin (eLoc $1) $2) $1) $3 }
-  | E fold E E { EApp (eLoc $1) (EApp (eLoc $1) (EApp $2 (TBuiltin $2 Fold) $1) $3) $4 }
-  | E capture E E { EApp (eLoc $1) (EApp (eLoc $1) (EApp $2 (TBuiltin $2 Captures) $1) $3) $4 }
-  | E caret E E { EApp (eLoc $1) (EApp (eLoc $1) (EApp $2 (TBuiltin $2 Scan) $1) $3) $4 }
-  | comma E E E { EApp $1 (EApp $1 (EApp $1 (TBuiltin $1 ZipW) $2) $3) $4 }
-  | lbrace E rbrace braces(E) { Guarded $1 $2 $4 }
-  | lbracePercent E rbrace braces(E) { let tl = eLoc $2 in Guarded $1 (EApp tl (EApp tl (BBuiltin tl Matches) (AllField tl)) $2) $4 }
-  | lbraceBar E rbrace { Implicit $1 $2 }
-  | let many(Bind) in E end { mkLet $1 (reverse $2) $4 }
-  | lparen sepBy(E, dot) rparen { Tup $1 (reverse $2) }
-  | lanchor sepBy(E, dot) rparen { Anchor $1 (reverse $2) }
-  | E E { EApp (eLoc $1) $1 $2 }
-  | tally { UBuiltin $1 Tally }
-  | tallyL { UBuiltin $1 TallyList }
-  | const { UBuiltin $1 Const }
-  | exclamation { UBuiltin $1 Not }
-  | lsqbracket E rsqbracket { Dfn $1 $2 }
-  | x { ResVar $1 X }
-  | y { ResVar $1 Y }
-  | rr { RegexLit (loc $1) (BSL.toStrict $ rr $1) }
-  | min { BBuiltin $1 Min }
-  | max { BBuiltin $1 Max }
-  | split { BBuiltin $1 Split }
-  | match { BBuiltin $1 Match }
-  | splitc { BBuiltin $1 Splitc }
-  | substr { TBuiltin $1 Substr }
-  | sprintf { BBuiltin $1 Sprintf }
-  | option { TBuiltin $1 Option }
-  | captures { TBuiltin $1 AllCaptures }
-  | floor { UBuiltin $1 Floor }
-  | ceil { UBuiltin $1 Ceiling }
-  | floorSym { UBuiltin $1 Floor }
-  | ceilSym { UBuiltin $1 Ceiling }
-  | dedup { UBuiltin $1 Dedup }
-  | some { UBuiltin $1 Some }
-  | catMaybes { UBuiltin $1 CatMaybes }
-  | neg { UBuiltin $1 Negate }
-  | ix { NBuiltin $1 Ix }
-  | nf { NBuiltin $1 Nf }
-  | none { NBuiltin $1 None }
-  | fp { NBuiltin $1 Fp }
-  | parens(at) { UBuiltin (loc $1) (At $ ix $1) }
-  | parens(select) { UBuiltin (loc $1) (Select $ field $1) }
-  | E at { EApp (eLoc $1) (UBuiltin (loc $2) (At $ ix $2)) $1 }
-  | E select { EApp (eLoc $1) (UBuiltin (loc $2) (Select $ field $2)) $1 }
-  | backslash name dot E { Lam $1 $2 $4 }
-  | parens(E) { Paren (eLoc $1) $1 }
-  | if E then E else E { Cond $1 $2 $4 $6 }
-
-{
-
-type File = ([FilePath], Program AlexPosn)
-
-type Library = ([FilePath], [D AlexPosn])
-
-parseError :: Token AlexPosn -> Parse a
-parseError = throwError . Unexpected
-
-mkLet :: a -> [(Name a, E a)] -> E a -> E a
-mkLet _ [] e     = e
-mkLet l (b:bs) e = Let l b (mkLet l bs e)
-
-data ParseError a = Unexpected (Token a)
-                  | LexErr String
-
-instance Pretty a => Pretty (ParseError a) where
-    pretty (Unexpected tok)  = pretty (loc tok) <+> "Unexpected" <+> pretty tok
-    pretty (LexErr str)      = pretty (T.pack str)
-
-instance Pretty a => Show (ParseError a) where
-    show = show . pretty
-
-instance (Pretty a, Typeable a) => Exception (ParseError a)
-
-type Parse = ExceptT (ParseError AlexPosn) Alex
-
-parse :: BSL.ByteString -> Either (ParseError AlexPosn) File
-parse = fmap snd . runParse parseF
-
-parseWithMax :: BSL.ByteString -> Either (ParseError AlexPosn) (Int, File)
-parseWithMax = fmap (first fst3) . runParse parseF
-    where fst3 (x, _, _) = x
-
-parseWithInitCtx :: BSL.ByteString -> Either (ParseError AlexPosn) (AlexUserState, File)
-parseWithInitCtx bsl = parseWithCtx bsl alexInitUserState
-
-parseWithCtx :: BSL.ByteString -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, File)
-parseWithCtx = parseWithInitSt parseF
-
-parseLibWithCtx :: BSL.ByteString -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, Library)
-parseLibWithCtx = parseWithInitSt parseLib
-
-runParse :: Parse a -> BSL.ByteString -> Either (ParseError AlexPosn) (AlexUserState, a)
-runParse parser str = liftErr $ runAlexSt str (runExceptT parser)
-
-parseWithInitSt :: Parse a -> BSL.ByteString -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, a)
-parseWithInitSt parser str st = liftErr $ withAlexSt str st (runExceptT parser)
-    where liftErr (Left err)            = Left (LexErr err)
-          liftErr (Right (_, Left err)) = Left err
-          liftErr (Right (i, Right x))  = Right (i, x)
-
-liftErr :: Either String (b, Either (ParseError a) c) -> Either (ParseError a) (b, c)
-liftErr (Left err)            = Left (LexErr err)
-liftErr (Right (_, Left err)) = Left err
-liftErr (Right (i, Right x))  = Right (i, x)
-
-}
diff --git a/src/Jacinda/Parser/Rewrite.hs b/src/Jacinda/Parser/Rewrite.hs
deleted file mode 100644
--- a/src/Jacinda/Parser/Rewrite.hs
+++ /dev/null
@@ -1,60 +0,0 @@
-module Jacinda.Parser.Rewrite ( rewriteProgram
-                              , rewriteD
-                              , rewriteE
-                              ) where
-
-
-import           Control.Recursion (cata, embed)
-import           Jacinda.AST
-
-rewriteProgram :: Program a -> Program a
-rewriteProgram (Program ds e) = Program (rewriteD <$> ds) (rewriteE e)
-
-rewriteD :: D a -> D a
-rewriteD d@SetFS{}        = d
-rewriteD (FunDecl n bs e) = FunDecl n bs (rewriteE e)
-
-rewriteE :: E a -> E a
-rewriteE = cata a where
-    a (EAppF l e0@(UBuiltin _ Tally) (EApp lϵ (EApp lϵϵ e1@BBuiltin{} e2) e3))                      = EApp l (EApp lϵ e1 (EApp lϵϵ e0 e2)) e3
-    a (EAppF l e0@(UBuiltin _ Const) (EApp lϵ (EApp lϵϵ e1@(BBuiltin _ Map) e2) e3))                = EApp l (EApp lϵ e1 (EApp lϵϵ e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Eq) _) (EApp l1 (EApp l2 e1@(BBuiltin _ And) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Eq) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Or) e2) e3))          = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Neq) _) (EApp l1 (EApp l2 e1@(BBuiltin _ And) e2) e3))        = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Neq) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Or) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Gt) _) (EApp l1 (EApp l2 e1@(BBuiltin _ And) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Gt) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Or) e2) e3))          = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Lt) _) (EApp l1 (EApp l2 e1@(BBuiltin _ And) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Lt) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Or) e2) e3))          = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Leq) _) (EApp l1 (EApp l2 e1@(BBuiltin _ And) e2) e3))        = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Leq) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Or) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Geq) _) (EApp l1 (EApp l2 e1@(BBuiltin _ And) e2) e3))        = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Geq) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Or) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Matches) _) (EApp l1 (EApp l2 e1@(BBuiltin _ And) e2) e3))    = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Matches) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Or) e2) e3))     = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ NotMatches) _) (EApp l1 (EApp l2 e1@(BBuiltin _ And) e2) e3)) = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ NotMatches) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Or) e2) e3))  = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Fold1) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Eq) e2) e3))       = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Fold1) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Neq) e2) e3))      = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Fold1) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Gt) e2) e3))       = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Fold1) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Geq) e2) e3))      = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Fold1) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Leq) e2) e3))      = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@(EApp _ (BBuiltin _ Fold1) _) (EApp l1 (EApp l2 e1@(BBuiltin _ Lt) e2) e3))       = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
-    a (EAppF l e0@Var{} (EApp lϵ (EApp lϵϵ e1 e2) e3))                                              = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
-    -- TODO rewrite dfn
-    a (EAppF l e0@Var{} (EApp l0 e1 (EApp l1 (EApp l2 op@BBuiltin{} e2) e3)))                       = EApp l1 (EApp l2 op (EApp l (EApp l0 e0 e1) e2)) e3
-    a (EAppF l e0@Var{} (EApp lϵ e1 e2))                                                            = EApp l (EApp lϵ e0 e1) e2
-    a (EAppF l e0@(BBuiltin _ Max) (EApp lϵ e1 e2))                                                 = EApp l (EApp lϵ e0 e1) e2
-    a (EAppF l e0@(BBuiltin _ Min) (EApp lϵ e1 e2))                                                 = EApp l (EApp lϵ e0 e1) e2
-    a (EAppF l e0@(BBuiltin _ Split) (EApp lϵ e1 e2))                                               = EApp l (EApp lϵ e0 e1) e2
-    a (EAppF l e0@(BBuiltin _ Match) (EApp lϵ e1 e2))                                               = EApp l (EApp lϵ e0 e1) e2
-    a (EAppF l e0@(BBuiltin _ Splitc) (EApp lϵ e1 e2))                                              = EApp l (EApp lϵ e0 e1) e2
-    a (EAppF l e0@(BBuiltin _ Sprintf) (EApp lϵ e1 e2))                                             = EApp l (EApp lϵ e0 e1) e2
-    a (EAppF l e0@(TBuiltin _ Substr) (EApp lϵ (EApp lϵϵ e1 e2) e3))                                = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
-    a (EAppF l e0@(TBuiltin _ Substr) (EApp lϵ e1 (EApp lϵϵ e2 e3)))                                = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
-    a (EAppF l e0@(TBuiltin _ Option) (EApp lϵ (EApp lϵϵ e1 e2) e3))                                = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
-    a (EAppF l e0@(TBuiltin _ Option) (EApp lϵ e1 (EApp lϵϵ e2 e3)))                                = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
-    a (EAppF l e0@(TBuiltin _ Option) (EApp lϵ e1 e2))                                              = EApp l (EApp lϵ e0 e1) e2
-    a (EAppF l e0@(TBuiltin _ AllCaptures) (EApp lϵ (EApp lϵϵ e1 e2) e3))                           = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
-    a (EAppF l e0@(TBuiltin _ AllCaptures) (EApp lϵ e1 (EApp lϵϵ e2 e3)))                           = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
-    a x                                                                                             = embed x
diff --git a/src/Jacinda/Regex.hs b/src/Jacinda/Regex.hs
--- a/src/Jacinda/Regex.hs
+++ b/src/Jacinda/Regex.hs
@@ -21,17 +21,17 @@
 import           Regex.Rure               (RureMatch (..), RurePtr, captures, compile, find, findCaptures, isMatch, matches', rureDefaultFlags, rureFlagDotNL)
 import           System.IO.Unsafe         (unsafeDupablePerformIO, unsafePerformIO)
 
--- see: https://docs.rs/regex/latest/regex/#perl-character-classes-unicode-friendly
+-- https://docs.rs/regex/latest/regex/#perl-character-classes-unicode-friendly
 defaultFs :: BS.ByteString
 defaultFs = "\\s+"
 
 {-# NOINLINE defaultRurePtr #-}
 defaultRurePtr :: RurePtr
-defaultRurePtr = unsafePerformIO $ yeetRureIO =<< compile genFlags defaultFs
-    where genFlags = rureDefaultFlags <> rureFlagDotNL -- in case they want to use a weird custom record separator
+defaultRurePtr = unsafePerformIO $ yIO =<< compile genFlags defaultFs
+    where genFlags = rureDefaultFlags <> rureFlagDotNL -- in case they want to use a custom record separator
 
 substr :: BS.ByteString -> Int -> Int -> BS.ByteString
-substr (BS.BS fp l) begin endϵ | endϵ >= begin = BS.BS (fp `plusForeignPtr` begin) (min l endϵ - begin)
+substr (BS.BS fp l) begin endϵ | endϵ >= begin = BS.BS (fp `plusForeignPtr` begin) (min l endϵ-begin)
                                | otherwise = "error: invalid substring indices."
 
 captures' :: RurePtr -> BS.ByteString -> CSize -> [BS.ByteString]
@@ -74,11 +74,11 @@
 isMatch' re haystack = unsafeDupablePerformIO $ isMatch re haystack 0
 
 compileDefault :: BS.ByteString -> RurePtr
-compileDefault = unsafeDupablePerformIO . (yeetRureIO <=< compile rureDefaultFlags) -- TODO: rureFlagDotNL? in case they have weird records
+compileDefault = unsafeDupablePerformIO . (yIO <=< compile rureDefaultFlags) -- TODO: rureFlagDotNL
 
 newtype RureExe = RegexCompile String deriving (Show)
 
 instance Exception RureExe where
 
-yeetRureIO :: Either String a -> IO a
-yeetRureIO = either (throwIO . RegexCompile) pure
+yIO :: Either String a -> IO a
+yIO = either (throwIO . RegexCompile) pure
diff --git a/src/Jacinda/Rename.hs b/src/Jacinda/Rename.hs
deleted file mode 100644
--- a/src/Jacinda/Rename.hs
+++ /dev/null
@@ -1,161 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module Jacinda.Rename ( renameE
-                      , renameProgram
-                      , runRenameM
-                      , renamePGlobal
-                      , RenameM
-                      , Renames (..)
-                      , HasRenames (..)
-                      ) where
-
-import           Control.Monad.State.Strict (MonadState, State, runState)
-import           Control.Recursion          (cata, embed)
-import           Data.Bifunctor             (second)
-import qualified Data.IntMap                as IM
-import qualified Data.Text                  as T
-import           Intern.Name
-import           Intern.Unique
-import           Jacinda.AST
-import           Lens.Micro                 (Lens')
-import           Lens.Micro.Mtl             (modifying, use, (%=), (.=))
-
-data Renames = Renames { max_ :: Int, bound :: IM.IntMap Int }
-
--- TODO: instance Pretty Renames for debug?
-
-class HasRenames a where
-    rename :: Lens' a Renames
-
-instance HasRenames Renames where
-    rename = id
-
-boundLens :: Lens' Renames (IM.IntMap Int)
-boundLens f s = fmap (\x -> s { bound = x }) (f (bound s))
-
-maxLens :: Lens' Renames Int
-maxLens f s = fmap (\x -> s { max_ = x }) (f (max_ s))
-
-type RenameM = State Renames
-
-renamePGlobal :: Int -> Program a -> (Program a, Int)
-renamePGlobal i = runRenameM i . renameProgram
-
-runRenameM :: Int -> RenameM x -> (x, Int)
-runRenameM i act = second max_ (runState act (Renames i IM.empty))
-
--- Make sure you don't have cycles in the renames map!
-replaceUnique :: (MonadState s m, HasRenames s) => Unique -> m Unique
-replaceUnique u@(Unique i) = do
-    rSt <- use (rename.boundLens)
-    case IM.lookup i rSt of
-        Nothing -> pure u
-        Just j  -> replaceUnique (Unique j)
-
-replaceVar :: (MonadState s m, HasRenames s) => Name a -> m (Name a)
-replaceVar (Name n u l) = do
-    u' <- replaceUnique u
-    pure $ Name n u' l
-
-dummyName :: (MonadState s m, HasRenames s) => a -> T.Text -> m (Name a)
-dummyName l n = do
-    st <- use (rename.maxLens)
-    Name n (Unique $ st+1) l
-        <$ modifying (rename.maxLens) (+1)
-
--- allows us to work with a temporary change to the renamer state, tracking the
--- max sensibly
-withRenames :: (HasRenames s, MonadState s m) => (Renames -> Renames) -> m a -> m a
-withRenames modSt act = do
-    preSt <- use rename
-    rename %= modSt
-    res <- act
-    postMax <- use (rename.maxLens)
-    rename .= setMax postMax preSt
-    pure res
-
-withName :: (HasRenames s, MonadState s m) => Name a -> m (Name a, Renames -> Renames)
-withName (Name t (Unique i) l) = do
-    m <- use (rename.maxLens)
-    let newUniq = m+1
-    rename.maxLens .= newUniq
-    pure (Name t (Unique newUniq) l, mapBound (IM.insert i (m+1)))
-
-mapBound :: (IM.IntMap Int -> IM.IntMap Int) -> Renames -> Renames
-mapBound f (Renames m b) = Renames m (f b)
-
-setMax :: Int -> Renames -> Renames
-setMax i (Renames _ b) = Renames i b
-
--- | Desguar top-level functions as lambdas
-mkLam :: [Name a] -> E a -> E a
-mkLam ns e = foldr (\n -> Lam (loc n) n) e ns
-
--- | A dfn could be unary or binary - here we guess if it is binary
-hasY :: E a -> Bool
-hasY = cata a where
-    a (ResVarF _ Y)           = True
-    a (TupF _ es)             = or es
-    a (EAppF _ e e')          = e || e'
-    a (LamF _ _ e)            = e
-    a DfnF{}                  = error "Not supported yet."
-    a (LetF _ b e)            = e || snd b
-    a (GuardedF _ p b)        = b || p
-    a (ImplicitF _ e)         = e
-    a (ParenF _ e)            = e
-    a (ArrF _ es)             = or es
-    a (AnchorF _ es)          = or es
-    a (OptionValF _ (Just e)) = e
-    a (CondF _ p e e')        = p || e || e'
-    a _                       = False
-
-replaceXY :: (a -> Name a) -- ^ @x@
-          -> (a -> Name a) -- ^ @y@
-          -> E a
-          -> E a
-replaceXY nX nY = cata a where
-    a (ResVarF l X) = Var l (nX l)
-    a (ResVarF l Y) = Var l (nY l)
-    a x             = embed x
-
-replaceX :: (a -> Name a) -> E a -> E a
-replaceX n = cata a where
-    a (ResVarF l X) = Var l (n l)
-    a x             = embed x
-
-renameD :: D a -> RenameM (D a)
-renameD d@SetFS{}        = pure d
-renameD (FunDecl n ns e) = FunDecl n [] <$> renameE (mkLam ns e)
-
-renameProgram :: Program a -> RenameM (Program a)
-renameProgram (Program ds e) = Program <$> traverse renameD ds <*> renameE e
-
-{-# INLINABLE renameE #-}
-renameE :: (HasRenames s, MonadState s m) => E a -> m (E a)
-renameE (EApp l e e')   = EApp l <$> renameE e <*> renameE e'
-renameE (Tup l es)      = Tup l <$> traverse renameE es
-renameE (Var l n)       = Var l <$> replaceVar n
-renameE (Lam l n e)     = do
-    (n', modR) <- withName n
-    Lam l n' <$> withRenames modR (renameE e)
-renameE (Dfn l e) | {-# SCC "hasY" #-} hasY e = do
-    x@(Name nX uX _) <- dummyName l "x"
-    y@(Name nY uY _) <- dummyName l "y"
-    Lam l x . Lam l y <$> renameE ({-# SCC "replaceXY" #-} replaceXY (Name nX uX) (Name nY uY) e)
-                  | otherwise = do
-    x@(Name n u _) <- dummyName l "x"
-    -- no need for withName... withRenames because this is fresh/globally unique
-    Lam l x <$> renameE ({-# SCC "replaceX" #-} replaceX (Name n u) e)
-renameE (Guarded l p e) = Guarded l <$> renameE p <*> renameE e
-renameE (Implicit l e) = Implicit l <$> renameE e
-renameE ResVar{} = error "Bare reserved variable."
-renameE (Let l (n, eϵ) e') = do
-    eϵ' <- renameE eϵ
-    (n', modR) <- withName n
-    Let l (n', eϵ') <$> withRenames modR (renameE e')
-renameE (Paren _ e) = renameE e
-renameE (Arr l es) = Arr l <$> traverse renameE es
-renameE (Anchor l es) = Anchor l <$> traverse renameE es
-renameE (OptionVal l e) = OptionVal l <$> traverse renameE e
-renameE (Cond l p e e') = Cond l <$> renameE p <*> renameE e <*> renameE e'
-renameE e = pure e -- literals &c.
diff --git a/src/Jacinda/Ty.hs b/src/Jacinda/Ty.hs
deleted file mode 100644
--- a/src/Jacinda/Ty.hs
+++ /dev/null
@@ -1,684 +0,0 @@
-{-# LANGUAGE FlexibleContexts  #-}
-{-# LANGUAGE OverloadedStrings #-}
-
-module Jacinda.Ty ( TypeM
-                  , Error (..)
-                  , runTypeM
-                  , tyProgram
-                  -- * For debugging
-                  , tyOf
-                  ) where
-
-import           Control.Exception          (Exception)
-import           Control.Monad              (forM)
-import           Control.Monad.Except       (throwError)
-import           Control.Monad.State.Strict (StateT, gets, modify, runState, runStateT)
-import           Data.Bifunctor             (first, second)
-import           Data.Foldable              (traverse_)
-import           Data.Functor               (void, ($>))
-import qualified Data.IntMap                as IM
-import           Data.Maybe                 (fromMaybe)
-import           Data.Semigroup             ((<>))
-import qualified Data.Set                   as S
-import qualified Data.Text                  as T
-import           Data.Typeable              (Typeable)
-import qualified Data.Vector                as V
-import qualified Data.Vector.Ext            as V
-import           Intern.Name
-import           Intern.Unique
-import           Jacinda.AST
-import           Jacinda.Ty.Const
-import           Prettyprinter              (Doc, Pretty (..), squotes, vsep, (<+>))
-
-data Error a = UnificationFailed a (T ()) (T ())
-             | Doesn'tSatisfy a (T ()) C
-             | IllScoped a (Name a)
-             | Ambiguous (T K) (E ())
-             | Expected K K
-             | IllScopedTyVar (TyName ())
-
-instance Pretty a => Pretty (Error a) where
-    pretty (UnificationFailed l ty ty') = pretty l <+> "could not unify type" <+> squotes (pretty ty) <+> "with" <+> squotes (pretty ty')
-    pretty (Doesn'tSatisfy l ty c)      = pretty l <+> squotes (pretty ty) <+> "is not a member of class" <+> pretty c
-    pretty (IllScoped l n)              = pretty l <+> squotes (pretty n) <+> "is not in scope."
-    pretty (Ambiguous ty e)             = "type" <+> squotes (pretty ty) <+> "of" <+> squotes (pretty e) <+> "is ambiguous"
-    pretty (Expected k0 k1)             = "Found kind" <+> pretty k0 <> ", expected kind" <+> pretty k1
-    pretty (IllScopedTyVar n)           = "Type variable" <+> squotes (pretty n) <+> "is not in scope."
-
-instance Pretty a => Show (Error a) where
-    show = show . pretty
-
-instance (Typeable a, Pretty a) => Exception (Error a) where
-
--- solve, unify etc. THEN check that all constraints are satisfied?
--- (after accumulating classVar membership...)
-data TyState a = TyState { maxU        :: !Int
-                         , kindEnv     :: IM.IntMap K
-                         , classVars   :: IM.IntMap (S.Set (C, a))
-                         , varEnv      :: IM.IntMap (T K)
-                         , constraints :: S.Set (a, T K, T K)
-                         }
-
-prettyConstraints :: S.Set (b, T a, T a) -> Doc ann
-prettyConstraints cs = vsep (prettyEq . go <$> S.toList cs) where
-    go (_, x, y) = (x, y)
-
-prettyEq :: (T a, T a) -> Doc ann
-prettyEq (ty, ty') = pretty ty <+> "≡" <+> pretty ty'
-
-mapMaxU :: (Int -> Int) -> TyState a -> TyState a
-mapMaxU f (TyState u k c v cs) = TyState (f u) k c v cs
-
-setMaxU :: Int -> TyState a -> TyState a
--- setMaxU i = mapMaxU (const i)
-setMaxU i (TyState _ k c v cs) = TyState i k c v cs
-
-mapClassVars :: (IM.IntMap (S.Set (C, a)) -> IM.IntMap (S.Set (C, a))) -> TyState a -> TyState a
-mapClassVars f (TyState u k cvs v cs) = TyState u k (f cvs) v cs
-
-addVarEnv :: Int -> T K -> TyState a -> TyState a
-addVarEnv i ty (TyState u k cvs v cs) = TyState u k cvs (IM.insert i ty v) cs
-
-addKindEnv :: Int -> K -> TyState a -> TyState a
-addKindEnv i k (TyState u ks cvs v cs) = TyState u (IM.insert i k ks) cvs v cs
-
-addConstraint :: Ord a => (a, T K, T K) -> TyState a -> TyState a
-addConstraint c (TyState u k cvs v cs) = TyState u k cvs v (S.insert c cs)
-
-type TypeM a = StateT (TyState a) (Either (Error a))
-
-runTypeM :: Int -> TypeM a b -> Either (Error a) (b, Int)
-runTypeM i = fmap (second maxU) . flip runStateT (TyState i IM.empty IM.empty IM.empty S.empty)
-
-type UnifyMap a = IM.IntMap (T a)
-
-inContext :: UnifyMap a -> T a -> T a
-inContext um ty'@(TyVar _ (Name _ (Unique i) _)) =
-    case IM.lookup i um of
-        Just ty@TyVar{} -> inContext (IM.delete i um) ty -- prevent cyclic lookups
-        -- TODO: does this need a case for TyApp -> inContext?
-        Just ty         -> ty
-        Nothing         -> ty'
-inContext _ ty'@TyB{} = ty'
-inContext _ ty'@TyNamed{} = ty'
-inContext um (TyApp l ty ty') = TyApp l (inContext um ty) (inContext um ty')
-inContext um (TyArr l ty ty') = TyArr l (inContext um ty) (inContext um ty')
-inContext um (TyTup l tys)    = TyTup l (inContext um <$> tys)
-
--- | Perform substitutions before handing off to 'unifyMatch'
-unifyPrep :: UnifyMap a
-          -> [(l, T a, T a)]
-          -> TypeM l (IM.IntMap (T a))
-unifyPrep _ [] = pure mempty
-unifyPrep um ((l, ty, ty'):tys) =
-    let ty'' = inContext um ty
-        ty''' = inContext um ty'
-    in unifyMatch um $ (l, ty'', ty'''):tys
-
-unifyMatch :: UnifyMap a -> [(l, T a, T a)] -> TypeM l (IM.IntMap (T a))
-unifyMatch _ [] = pure mempty
-unifyMatch um ((_, TyB _ b, TyB _ b'):tys) | b == b' = unifyPrep um tys
-unifyMatch um ((_, TyNamed _ n0, TyNamed _ n1):tys) | n0 == n1 = unifyPrep um tys
-unifyMatch um ((_, ty@TyB{}, TyVar  _ (Name _ (Unique k) _)):tys) = IM.insert k ty <$> unifyPrep (IM.insert k ty um) tys
-unifyMatch um ((_, TyVar _ (Name _ (Unique k) _), ty@(TyB{})):tys) = IM.insert k ty <$> unifyPrep (IM.insert k ty um) tys
-unifyMatch um ((_, ty@TyArr{}, TyVar  _ (Name _ (Unique k) _)):tys) = IM.insert k ty <$> unifyPrep (IM.insert k ty um) tys
-unifyMatch um ((_, TyVar _ (Name _ (Unique k) _), ty@(TyArr{})):tys) = IM.insert k ty <$> unifyPrep (IM.insert k ty um) tys
-unifyMatch um ((_, ty@TyApp{}, TyVar  _ (Name _ (Unique k) _)):tys) = IM.insert k ty <$> unifyPrep (IM.insert k ty um) tys
-unifyMatch um ((_, TyVar _ (Name _ (Unique k) _), ty@(TyTup{})):tys) = IM.insert k ty <$> unifyPrep (IM.insert k ty um) tys
-unifyMatch um ((_, ty@TyTup{}, TyVar  _ (Name _ (Unique k) _)):tys) = IM.insert k ty <$> unifyPrep (IM.insert k ty um) tys
-unifyMatch um ((_, TyVar _ (Name _ (Unique k) _), ty@(TyApp{})):tys) = IM.insert k ty <$> unifyPrep (IM.insert k ty um) tys
-unifyMatch um ((l, TyApp _ ty ty', TyApp _ ty'' ty'''):tys) = unifyPrep um ((l, ty, ty'') : (l, ty', ty''') : tys)
-unifyMatch um ((l, TyArr _ ty ty', TyArr _ ty'' ty'''):tys) = unifyPrep um ((l, ty, ty'') : (l, ty', ty''') : tys)
-unifyMatch um ((l, ty@(TyTup _ tys), ty'@(TyTup _ tys')):tyss)
-    | length tys == length tys' = unifyPrep um (zip3 (repeat l) tys tys' ++ tyss)
-    | otherwise = throwError (UnificationFailed l (void ty) (void ty'))
-unifyMatch um ((_, TyVar _ n@(Name _ (Unique k) _), ty@(TyVar _ n')):tys)
-    | n == n' = unifyPrep um tys
-    | otherwise = IM.insert k ty <$> unifyPrep (IM.insert k ty um) tys
-unifyMatch _ ((l, ty, ty'):_) = throwError (UnificationFailed l (void ty) (void ty'))
-
-unify :: [(l, T a, T a)] -> TypeM l (IM.IntMap (T a))
-unify = unifyPrep IM.empty
-
-unifyM :: S.Set (l, T a, T a) -> TypeM l (IM.IntMap (T a))
-unifyM s = {-# SCC "unifyM" #-} unify (S.toList s)
-
-substInt :: IM.IntMap (T a) -> Int -> Maybe (T a)
-substInt tys k =
-    case IM.lookup k tys of
-        Just ty'@TyVar{}       -> Just $ substConstraints (IM.delete k tys) ty' -- TODO: this is to prevent cyclic lookups: is it right?
-        Just (TyApp l ty0 ty1) -> Just $ let tys' = IM.delete k tys in TyApp l (substConstraints tys' ty0) (substConstraints tys' ty1)
-        Just (TyArr l ty0 ty1) -> Just $ let tys' = IM.delete k tys in TyArr l (substConstraints tys' ty0) (substConstraints tys' ty1)
-        Just (TyTup l tysϵ)    -> Just $ let tys' = IM.delete k tys in TyTup l (substConstraints tys' <$> tysϵ)
-        Just ty'               -> Just ty'
-        Nothing                -> Nothing
-
-substConstraints :: IM.IntMap (T a) -> T a -> T a
-substConstraints _ ty@TyB{}                             = ty
-substConstraints _ ty@TyNamed{}                         = ty
-substConstraints tys ty@(TyVar _ (Name _ (Unique k) _)) = fromMaybe ty (substInt tys k)
-substConstraints tys (TyTup l tysϵ)                     = TyTup l (substConstraints tys <$> tysϵ)
-substConstraints tys (TyApp l ty ty')                   =
-    TyApp l (substConstraints tys ty) (substConstraints tys ty')
-substConstraints tys (TyArr l ty ty')                   =
-    TyArr l (substConstraints tys ty) (substConstraints tys ty')
-
-freshName :: T.Text -> K -> TypeM a (Name K)
-freshName n k = do
-    st <- gets maxU
-    Name n (Unique $ st+1) k
-        <$ modify (mapMaxU (+1))
-
-namek :: Name K -> TypeM a (Name K)
-namek n =
-    modify (addKindEnv (unUnique$unique n) (loc n)) $> n
-
-higherOrder :: T.Text -> TypeM a (Name K)
-higherOrder t = freshName t (KArr Star Star) >>= namek
-
--- of kind 'Star'
-dummyName :: T.Text -> TypeM a (Name K)
-dummyName n = freshName n Star >>= namek
-
-addC :: Ord a => Name b -> (C, a) -> IM.IntMap (S.Set (C, a)) -> IM.IntMap (S.Set (C, a))
-addC (Name _ (Unique i) _) c = IM.alter (Just . go) i where
-    go Nothing   = S.singleton c
-    go (Just cs) = S.insert c cs
-
--- | arguments assumed to have kind 'Star'
-tyArr :: T K -> T K -> T K
-tyArr = TyArr Star
-
-var :: Name K -> T K
-var = TyVar Star
-
--- assumes they have been renamed...
-pushConstraint :: Ord a => a -> T K -> T K -> TypeM a ()
-pushConstraint l ty ty' =
-    modify (addConstraint (l, ty, ty'))
-
-isStar :: K -> TypeM a ()
-isStar Star = pure ()
-isStar k    = throwError $ Expected k Star
-
-liftCloneTy :: T a -> TypeM b (T a)
-liftCloneTy ty = do
-    i <- gets maxU
-    let (ty', (j, iMaps)) = cloneTy i ty
-    -- FIXME: clone/propagate constraints
-    ty' <$ modify (setMaxU j)
-
-cloneTy :: Int -> T a -> (T a, (Int, IM.IntMap Unique))
-cloneTy i ty = flip runState (i, IM.empty) $ cloneTyM ty
-    where cloneTyM (TyVar l (Name n (Unique j) l')) = do
-                st <- gets snd
-                case IM.lookup j st of
-                    Just k -> pure (TyVar l (Name n k l'))
-                    Nothing -> do
-                        k <- gets fst
-                        let j' = Unique $ k+1
-                        TyVar l (Name n j' l') <$ modify (\(u, s) -> (u+1, IM.insert j j' s))
-          cloneTyM (TyArr l tyϵ ty')               = TyArr l <$> cloneTyM tyϵ <*> cloneTyM ty'
-          cloneTyM (TyApp l tyϵ ty')               = TyApp l <$> cloneTyM tyϵ <*> cloneTyM ty'
-          cloneTyM (TyTup l tys)                   = TyTup l <$> traverse cloneTyM tys
-          cloneTyM tyϵ@TyNamed{}                   = pure tyϵ
-          cloneTyM tyϵ@TyB{}                       = pure tyϵ
-
-kind :: T K -> TypeM a ()
-kind (TyB Star TyStr)                  = pure ()
-kind (TyB Star TyInteger)              = pure ()
-kind (TyB Star TyFloat)                = pure ()
-kind (TyB (KArr Star Star) TyStream)   = pure ()
-kind (TyB (KArr Star Star) TyOption)   = pure ()
-kind (TyB Star TyBool)                 = pure ()
-kind (TyB (KArr Star Star) TyVec)      = pure ()
-kind (TyB Star TyUnit)                 = pure ()
-kind (TyB k TyStr)                     = throwError $ Expected Star k
-kind (TyB k TyInteger)                 = throwError $ Expected Star k
-kind (TyB k TyFloat)                   = throwError $ Expected Star k
-kind (TyB k TyUnit)                    = throwError $ Expected Star k
-kind (TyB k TyBool)                    = throwError $ Expected Star k
-kind (TyB k TyOption)                  = throwError $ Expected (KArr Star Star) k
-kind (TyB k TyStream)                  = throwError $ Expected (KArr Star Star) k
-kind (TyB k TyVec)                     = throwError $ Expected (KArr Star Star) k
-kind (TyVar _ n@(Name _ (Unique i) _)) = do
-    preK <- gets (IM.lookup i . kindEnv)
-    case preK of
-        Just{}  -> pure ()
-        Nothing -> throwError $ IllScopedTyVar (void n)
-kind (TyTup Star tys) =
-    traverse_  isStar (fmap tLoc tys)
-kind (TyTup k _) = throwError $ Expected Star k
-kind (TyArr Star ty0 ty1) =
-    isStar (tLoc ty0) *>
-    isStar (tLoc ty1)
-kind (TyArr k _ _) = throwError $ Expected Star k
-kind (TyApp k1 ty0 ty1) = do
-    case tLoc ty0 of
-        (KArr k0 k1') | k0 == tLoc ty1 && k1' == k1 -> pure ()
-                      | k0 == tLoc ty1 -> throwError $ Expected k1' k1
-                      | otherwise        -> throwError $ Expected (tLoc ty1) k0
-        k0                               -> throwError $ Expected (KArr Star Star) k0
-
-checkType :: Ord a => T K -> (C, a) -> TypeM a ()
-checkType TyVar{} _                            = pure () -- TODO: I think this is right
-checkType (TyB _ TyR) (IsSemigroup, _)         = pure ()
-checkType (TyB _ TyStr) (IsSemigroup, _)       = pure ()
-checkType (TyB _ TyInteger) (IsSemigroup, _)   = pure ()
-checkType (TyB _ TyInteger) (IsNum, _)         = pure ()
-checkType (TyB _ TyInteger) (IsOrd, _)         = pure ()
-checkType (TyB _ TyInteger) (IsEq, _)          = pure ()
-checkType (TyB _ TyInteger) (IsParseable, _)   = pure ()
-checkType (TyB _ TyFloat) (IsParseable, _)     = pure ()
-checkType ty (IsParseable, l)                  = throwError $ Doesn'tSatisfy l (void ty) IsParseable
-checkType (TyB _ TyFloat) (IsSemigroup, _)     = pure ()
-checkType (TyB _ TyFloat) (IsNum, _)           = pure ()
-checkType (TyB _ TyFloat) (IsOrd, _)           = pure ()
-checkType (TyB _ TyFloat) (IsEq, _)            = pure ()
-checkType (TyB _ TyBool) (IsEq, _)             = pure ()
-checkType (TyB _ TyStr) (IsEq, _)              = pure ()
-checkType ty@(TyB _ TyStr) (c@IsOrd, l)        = throwError $ Doesn'tSatisfy l (void ty) c
-checkType (TyTup _ tys) (IsEq, l)              = traverse_ (`checkType` (IsEq, l)) tys
-checkType (TyTup _ tys) (IsOrd, l)             = traverse_ (`checkType` (IsOrd, l)) tys
-checkType (TyApp _ (TyB _ TyVec) ty) (IsEq, l) = checkType ty (IsEq, l)
-checkType ty@TyTup{} (c@IsNum, l)              = throwError $ Doesn'tSatisfy l (void ty) c
-checkType ty@(TyB _ TyStr) (c@IsNum, l)        = throwError $ Doesn'tSatisfy l (void ty) c
-checkType ty@(TyB _ TyBool) (c@IsNum, l)       = throwError $ Doesn'tSatisfy l (void ty) c
-checkType ty@TyArr{} (c, l)                    = throwError $ Doesn'tSatisfy l (void ty) c
-checkType (TyB _ TyVec) (Functor, _)           = pure ()
-checkType (TyB _ TyStream) (Functor, _)        = pure ()
-checkType (TyB _ TyOption) (Functor, _)        = pure ()
-checkType (TyB _ TyStream) (Witherable, _)     = pure ()
-checkType ty (c@Witherable, l)                 = throwError $ Doesn'tSatisfy l (void ty) c
-checkType ty (c@Functor, l)                    = throwError $ Doesn'tSatisfy l (void ty) c
-checkType (TyB _ TyVec) (Foldable, _)          = pure ()
-checkType (TyB _ TyStream) (Foldable, _)       = pure ()
-checkType ty (c@Foldable, l)                   = throwError $ Doesn'tSatisfy l (void ty) c
-checkType (TyB _ TyStr) (IsPrintf, _)          = pure ()
-checkType (TyB _ TyFloat) (IsPrintf, _)        = pure ()
-checkType (TyB _ TyInteger) (IsPrintf, _)      = pure ()
-checkType (TyB _ TyBool) (IsPrintf, _)         = pure ()
-checkType (TyTup _ tys) (IsPrintf, l)          = traverse_ (`checkType` (IsPrintf, l)) tys
-checkType ty (c@IsPrintf, l)                   = throwError $ Doesn'tSatisfy l (void ty) c
-checkType ty@(TyTup _ tys) (c@(HasField i ty'), l) | length tys >= i = pushConstraint l ty' (tys !! (i-1))
-                                                   | otherwise = throwError $ Doesn'tSatisfy l (void ty) c
-checkType ty (c@HasField{}, l)                 = throwError $ Doesn'tSatisfy l (void ty) c
-
-substC :: IM.IntMap (T K) -- ^ Unification result
-       -> C
-       -> C
-substC um (HasField i ty) = HasField i (substConstraints um ty)
-substC _ c                = c
-
-checkClass :: Ord a
-           => IM.IntMap (T K) -- ^ Unification result
-           -> Int
-           -> S.Set (C, a)
-           -> TypeM a ()
-checkClass tys i cs = {-# SCC "checkClass" #-}
-    case substInt tys i of
-        Just ty -> traverse_ (checkType ty) (first (substC tys) <$> S.toList cs)
-        Nothing -> pure () -- FIXME: we need to check that the var is well-kinded for constraint
-
-lookupVar :: Name a -> TypeM a (T K)
-lookupVar n@(Name _ (Unique i) l) = do
-    st <- gets varEnv
-    case IM.lookup i st of
-        Just ty -> pure ty -- liftCloneTy ty
-        Nothing -> throwError $ IllScoped l n
-
-tyOf :: Ord a => E a -> TypeM a (T K)
-tyOf = fmap eLoc . tyE
-
-tyD0 :: Ord a => D a -> TypeM a (D (T K))
-tyD0 (SetFS bs) = pure $ SetFS bs
-tyD0 (FunDecl n@(Name _ (Unique i) _) [] e) = do
-    e' <- tyE0 e
-    let ty = eLoc e'
-    modify (addVarEnv i ty)
-    pure $ FunDecl (n $> ty) [] e'
-tyD0 FunDecl{} = error "Internal error. Should have been desugared by now."
-
-isAmbiguous :: T K -> Bool
-isAmbiguous TyVar{}          = True
-isAmbiguous (TyArr _ ty ty') = isAmbiguous ty || isAmbiguous ty'
-isAmbiguous (TyApp _ ty ty') = isAmbiguous ty || isAmbiguous ty'
-isAmbiguous (TyTup _ tys)    = any isAmbiguous tys
-isAmbiguous TyNamed{}        = False
-isAmbiguous TyB{}            = False
-
-checkAmb :: E (T K) -> TypeM a ()
-checkAmb e@(BBuiltin ty _) | isAmbiguous ty = throwError $ Ambiguous ty (void e)
-checkAmb TBuiltin{} = pure () -- don't fail on ternary builtins, we don't need it anyway... better error messages
-checkAmb e@(UBuiltin ty _) | isAmbiguous ty = throwError $ Ambiguous ty (void e)
-checkAmb (Implicit _ e') = checkAmb e'
-checkAmb (Guarded _ p e') = checkAmb p *> checkAmb e'
-checkAmb (EApp _ e' e'') = checkAmb e' *> checkAmb e'' -- more precise errors, don't fail yet!
-checkAmb (Tup _ es) = traverse_ checkAmb es
-checkAmb e@(Arr ty _) | isAmbiguous ty = throwError $ Ambiguous ty (void e)
-checkAmb e@(Var ty _) | isAmbiguous ty = throwError $ Ambiguous ty (void e)
-checkAmb (Let _ bs e) = traverse_ checkAmb [e, snd bs]
-checkAmb (Lam _ _ e) = checkAmb e -- I think
-checkAmb _ = pure ()
-
-tyProgram :: Ord a => Program a -> TypeM a (Program (T K))
-tyProgram (Program ds e) = do
-    ds' <- traverse tyD0 ds
-    e' <- tyE0 e
-    backNames <- unifyM =<< gets constraints
-    toCheck <- gets (IM.toList . classVars)
-    traverse_ (uncurry (checkClass backNames)) toCheck
-    backNames' <- unifyM =<< gets constraints
-    -- FIXME: not sure if termination/whatever is guaranteed, need 2 think..
-    let res = {-# SCC "substConstraints" #-} fmap (substConstraints backNames') (Program ds' e')
-    checkAmb (expr res) $> res
-
-tyE :: Ord a => E a -> TypeM a (E (T K))
-tyE e = do
-    e' <- tyE0 e
-    backNames <- unifyM =<< gets constraints
-    toCheck <- gets (IM.toList . classVars)
-    traverse_ (uncurry (checkClass backNames)) toCheck
-    pure (fmap (substConstraints backNames) e')
-
-tyNumOp :: Ord a => a -> TypeM a (T K)
-tyNumOp l = do
-    m <- dummyName "m"
-    modify (mapClassVars (addC m (IsNum, l)))
-    let m' = var m
-    pure $ tyArr m' (tyArr m' m')
-
-tySemiOp :: Ord a => a -> TypeM a (T K)
-tySemiOp l = do
-    m <- dummyName "m"
-    modify (mapClassVars (addC m (IsSemigroup, l)))
-    let m' = var m
-    pure $ tyArr m' (tyArr m' m')
-
-tyOrd :: Ord a => a -> TypeM a (T K)
-tyOrd l = do
-    a <- dummyName "a"
-    modify (mapClassVars (addC a (IsOrd, l)))
-    let a' = var a
-    pure $ tyArr a' (tyArr a' tyBool)
-
-tyEq :: Ord a => a -> TypeM a (T K)
-tyEq l = do
-    a <- dummyName "a"
-    modify (mapClassVars (addC a (IsEq, l)))
-    let a' = var a
-    pure $ tyArr a' (tyArr a' tyBool)
-
--- min/max
-tyM :: Ord a => a -> TypeM a (T K)
-tyM l = do
-    a <- dummyName "a"
-    modify (mapClassVars (addC a (IsOrd, l)))
-    let a' = var a
-    pure $ tyArr a' (tyArr a' a')
-
-desugar :: a
-desugar = error "Should have been de-sugared in an earlier stage!"
-
-tyE0 :: Ord a => E a -> TypeM a (E (T K))
-tyE0 (BoolLit _ b)           = pure $ BoolLit tyBool b
-tyE0 (IntLit _ i)            = pure $ IntLit tyI i
-tyE0 (FloatLit _ f)          = pure $ FloatLit tyF f
-tyE0 (StrLit _ str)          = pure $ StrLit tyStr str
-tyE0 (RegexLit _ rr)         = pure $ RegexLit tyR rr
-tyE0 (Column _ i)            = pure $ Column (tyStream tyStr) i
-tyE0 (IParseCol _ i)         = pure $ IParseCol (tyStream tyI) i
-tyE0 (FParseCol _ i)         = pure $ FParseCol (tyStream tyF) i
-tyE0 (Field _ i)             = pure $ Field tyStr i
-tyE0 (LastField _)           = pure $ LastField tyStr
-tyE0 AllField{}              = pure $ AllField tyStr
-tyE0 AllColumn{}             = pure $ AllColumn (tyStream tyStr)
-tyE0 (NBuiltin _ Ix)         = pure $ NBuiltin tyI Ix
-tyE0 (NBuiltin _ Fp)         = pure $ NBuiltin tyStr Fp
-tyE0 (NBuiltin _ Nf)         = pure $ NBuiltin tyI Nf
-tyE0 (BBuiltin l Plus)       = BBuiltin <$> tySemiOp l <*> pure Plus
-tyE0 (BBuiltin l Minus)      = BBuiltin <$> tyNumOp l <*> pure Minus
-tyE0 (BBuiltin l Times)      = BBuiltin <$> tyNumOp l <*> pure Times
-tyE0 (BBuiltin l Gt)         = BBuiltin <$> tyOrd l <*> pure Gt
-tyE0 (BBuiltin l Lt)         = BBuiltin <$> tyOrd l <*> pure Lt
-tyE0 (BBuiltin l Geq)        = BBuiltin <$> tyOrd l <*> pure Geq
-tyE0 (BBuiltin l Leq)        = BBuiltin <$> tyOrd l <*> pure Leq
-tyE0 (BBuiltin l Eq)         = BBuiltin <$> tyEq l <*> pure Eq
-tyE0 (BBuiltin l Neq)        = BBuiltin <$> tyEq l <*> pure Neq
-tyE0 (BBuiltin l Min)        = BBuiltin <$> tyM l <*> pure Min
-tyE0 (BBuiltin l Max)        = BBuiltin <$> tyM l <*> pure Max
-tyE0 (BBuiltin _ Split)      = pure $ BBuiltin (tyArr tyStr (tyArr tyR (mkVec tyStr))) Split
-tyE0 (BBuiltin _ Splitc)     = pure $ BBuiltin (tyArr tyStr (tyArr tyStr (mkVec tyStr))) Splitc
-tyE0 (BBuiltin _ Matches)    = pure $ BBuiltin (tyArr tyStr (tyArr tyR tyBool)) Matches
-tyE0 (BBuiltin _ NotMatches) = pure $ BBuiltin (tyArr tyStr (tyArr tyR tyBool)) NotMatches
-tyE0 (UBuiltin _ Tally)      = pure $ UBuiltin (tyArr tyStr tyI) Tally
-tyE0 (BBuiltin _ Div)        = pure $ BBuiltin (tyArr tyF (tyArr tyF tyF)) Div
-tyE0 (UBuiltin _ Not)        = pure $ UBuiltin (tyArr tyBool tyBool) Not
-tyE0 (BBuiltin _ And)        = pure $ BBuiltin (tyArr tyBool (tyArr tyBool tyBool)) And
-tyE0 (BBuiltin _ Or)         = pure $ BBuiltin (tyArr tyBool (tyArr tyBool tyBool)) Or
-tyE0 (BBuiltin _ Match)      = pure $ BBuiltin (tyArr tyStr (tyArr tyR (tyOpt $ TyTup Star [tyI, tyI]))) Match
-tyE0 (TBuiltin _ Substr)     = pure $ TBuiltin (tyArr tyStr (tyArr tyI (tyArr tyI tyStr))) Substr
-tyE0 (UBuiltin _ IParse)     = pure $ UBuiltin (tyArr tyStr tyI) IParse
-tyE0 (UBuiltin _ FParse)     = pure $ UBuiltin (tyArr tyStr tyF) FParse
-tyE0 (UBuiltin _ Floor)      = pure $ UBuiltin (tyArr tyF tyI) Floor
-tyE0 (UBuiltin _ Ceiling)    = pure $ UBuiltin (tyArr tyF tyI) Ceiling
-tyE0 (UBuiltin _ TallyList) = do
-    a <- dummyName "a"
-    let a' = var a
-    pure $ UBuiltin (tyArr a' tyI) TallyList
-tyE0 (UBuiltin l Negate) = do
-    a <- dummyName "a"
-    modify (mapClassVars (addC a (IsNum, l)))
-    let a' = var a
-    pure $ UBuiltin (tyArr a' a') Negate
-tyE0 (UBuiltin _ Some) = do
-    a <- dummyName "a"
-    let a' = var a
-    pure $ UBuiltin (tyArr a' (tyOpt a')) Some
-tyE0 (NBuiltin _ None) = do
-    a <- dummyName "a"
-    pure $ NBuiltin (tyOpt $ var a) None
-tyE0 (ParseCol l i) = do
-    a <- dummyName "a"
-    let a' = var a
-    modify (mapClassVars (addC a (IsParseable, l)))
-    pure $ ParseCol (tyStream a') i
-tyE0 (UBuiltin l Parse) = do
-    a <- dummyName "a"
-    let a' = var a
-    modify (mapClassVars (addC a (IsParseable, l)))
-    pure $ UBuiltin (tyArr tyStr a') Parse
-tyE0 (BBuiltin l Sprintf) = do
-    a <- dummyName "a"
-    let a' = var a
-    modify (mapClassVars (addC a (IsPrintf, l)))
-    pure $ BBuiltin (tyArr tyStr (tyArr a' tyStr)) Sprintf
-tyE0 (UBuiltin _ (At i)) = do
-    a <- dummyName "a"
-    let a' = var a
-        tyV = mkVec a'
-    pure $ UBuiltin (tyArr tyV a') (At i)
-tyE0 (UBuiltin l (Select i)) = do
-    a <- dummyName "a"
-    b <- dummyName "b"
-    let a' = var a
-        b' = var b
-    modify (mapClassVars (addC a (HasField i b', l)))
-    pure $ UBuiltin (tyArr a' b') (Select i)
-tyE0 (UBuiltin l Dedup) = do
-    a <- dummyName "a"
-    let a' = var a
-        fTy = tyArr (tyStream a') (tyStream a')
-    modify (mapClassVars (addC a (IsEq, l)))
-    pure $ UBuiltin fTy Dedup
-tyE0 (UBuiltin _ Const) = do
-    a <- dummyName "a"
-    b <- dummyName "b"
-    let a' = var a
-        b' = var b
-        fTy = tyArr a' (tyArr b' a')
-    pure $ UBuiltin fTy Const
-tyE0 (UBuiltin l CatMaybes) = do
-    a <- dummyName "a"
-    f <- higherOrder "f"
-    let a' = var a
-        f' = var f
-        fTy = tyArr (hkt f' $ tyOpt a') (hkt f' a')
-    modify (mapClassVars (addC f (Witherable, l)))
-    pure $ UBuiltin fTy CatMaybes
-tyE0 (BBuiltin l Filter) = do
-    a <- dummyName "a"
-    f <- higherOrder "f"
-    let a' = var a
-        f' = var f
-        fTy = tyArr (tyArr a' tyBool) (tyArr (hkt f' a') (hkt f' a'))
-    modify (mapClassVars (addC f (Witherable , l)))
-    pure $ BBuiltin fTy Filter
-tyE0 (BBuiltin l MapMaybe) = do
-    a <- dummyName "a"
-    b <- dummyName "b"
-    f <- higherOrder "f"
-    let a' = var a
-        b' = var b
-        f' = var f
-        fTy = tyArr (tyArr a' (tyOpt b')) (tyArr (hkt f' a') (hkt f' b'))
-    modify (mapClassVars (addC f (Witherable, l)))
-    pure $ BBuiltin fTy MapMaybe
-tyE0 (BBuiltin l Map) = do
-    a <- dummyName "a"
-    b <- dummyName "b"
-    f <- higherOrder "f"
-    let a' = var a
-        b' = var b
-        f' = var f
-        fTy = tyArr (tyArr a' b') (tyArr (hkt f' a') (hkt f' b'))
-    modify (mapClassVars (addC f (Functor, l)))
-    pure $ BBuiltin fTy Map
-tyE0 (TBuiltin l Fold) = do
-    b <- dummyName "b"
-    a <- dummyName "a"
-    f <- higherOrder "f"
-    let b' = var b
-        a' = var a
-        f' = var f
-        fTy = tyArr (tyArr b' (tyArr a' b')) (tyArr b' (tyArr (hkt f' a') b'))
-    modify (mapClassVars (addC f (Foldable, l)))
-    pure $ TBuiltin fTy Fold
-tyE0 (BBuiltin l Fold1) = do
-    a <- dummyName "a"
-    f <- higherOrder "f"
-    let a' = var a
-        f' = var f
-        fTy = tyArr (tyArr a' (tyArr a' a')) (tyArr (hkt f' a') a')
-    modify (mapClassVars (addC f (Foldable, l)))
-    pure $ BBuiltin fTy Fold1
-tyE0 (TBuiltin _ Captures) =
-    pure $ TBuiltin (tyArr tyStr (tyArr tyI (tyArr tyR (tyOpt tyStr)))) Captures
--- (a -> a -> a) -> Stream a -> Stream a
-tyE0 (BBuiltin _ Prior) = do
-    a <- dummyName "a"
-    b <- dummyName "b"
-    let a' = var a
-        b' = var b
-        fTy = tyArr (tyArr a' (tyArr a' b')) (tyArr (tyStream a') (tyStream b'))
-    pure $ BBuiltin fTy Prior
--- (a -> b -> c) -> Stream a -> Stream b -> Stream c
-tyE0 (TBuiltin _ ZipW) = do
-    a <- dummyName "a"
-    b <- dummyName "b"
-    c <- dummyName "c"
-    let a' = var a
-        b' = var b
-        c' = var c
-        fTy = tyArr (tyArr a' (tyArr b' c')) (tyArr (tyStream a') (tyArr (tyStream b') (tyStream c')))
-    pure $ TBuiltin fTy ZipW
--- (b -> a -> b) -> b -> Stream a -> Stream b
-tyE0 (TBuiltin _ Scan) = do
-    b <- dummyName "b"
-    a <- dummyName "a"
-    let b' = var b
-        a' = var a
-        fTy = tyArr (tyArr b' (tyArr a' b')) (tyArr b' (tyArr (tyStream a') (tyStream b')))
-    pure $ TBuiltin fTy Scan
-tyE0 (TBuiltin _ Option) = do
-    b <- dummyName "b"
-    a <- dummyName "a"
-    let b' = var b
-        a' = var a
-        fTy = tyArr b' (tyArr (tyArr a' b') (tyArr (tyOpt a') b'))
-    pure $ TBuiltin fTy Option
-tyE0 (TBuiltin _ AllCaptures) =
-    pure $ TBuiltin (tyArr tyStr (tyArr tyI (tyArr tyR (mkVec tyStr)))) AllCaptures
-tyE0 (Implicit _ e) = do
-    e' <- tyE0 e
-    pure $ Implicit (tyStream (eLoc e')) e'
-tyE0 (Guarded l e streamE) = do
-    streamE' <- tyE0 streamE
-    e' <- tyE0 e
-    pushConstraint l tyBool (eLoc e')
-    pure $ Guarded (tyStream (eLoc streamE')) e' streamE'
-tyE0 (EApp _ e0 e1) = do
-    e0' <- tyE0 e0
-    e1' <- tyE0 e1
-    a <- dummyName "a"
-    b <- dummyName "b"
-    let a' = var a
-        b' = var b
-        fTy = tyArr a' b'
-    pushConstraint (eLoc e0) fTy (eLoc e0')
-    pushConstraint (eLoc e1) a' (eLoc e1')
-    pure $ EApp b' e0' e1'
-tyE0 (Lam _ n@(Name _ (Unique i) _) e) = do
-    a <- dummyName "a"
-    let a' = var a
-    modify (addVarEnv i a')
-    e' <- tyE0 e
-    pure $ Lam (tyArr a' (eLoc e')) (n $> a') e'
-tyE0 (Let _ (n@(Name _ (Unique i) _), eϵ) e) = do
-    eϵ' <- tyE0 eϵ
-    let bTy = eLoc eϵ'
-    modify (addVarEnv i bTy)
-    e' <- tyE0 e
-    pure $ Let (eLoc e') (n $> bTy, eϵ') e'
-tyE0 (Tup _ es) = do
-    es' <- traverse tyE0 es
-    pure $ Tup (TyTup Star (eLoc <$> es')) es'
-tyE0 (Var _ n) = do
-    ty <- lookupVar n
-    pure (Var ty (n $> ty))
-tyE0 Dfn{} = desugar
-tyE0 (ResVar _ X) = desugar
-tyE0 (ResVar _ Y) = desugar
-tyE0 RegexCompiled{} = error "Regex should not be compiled at this stage."
-tyE0 Paren{} = desugar
-tyE0 (OptionVal _ (Just e)) = do
-    e' <- tyE0 e
-    pure $ OptionVal (tyOpt $ eLoc e') (Just e')
-tyE0 (OptionVal _ Nothing) = do
-    a <- dummyName "a"
-    let a' = var a
-    pure $ OptionVal (tyOpt a') Nothing
-tyE0 (Arr l v) | V.null v = do
-    a <- dummyName "a"
-    let a' = var a
-    pure $ Arr (mkVec a') V.empty
-               | otherwise = do
-    v' <- traverse tyE0 v
-    let x = V.head v'
-    V.priorM_ (\y y' -> pushConstraint l (eLoc y) (eLoc y')) v'
-    pure $ Arr (eLoc x) v'
-tyE0 (Anchor l es) = do
-    es' <- forM es $ \e -> do
-        e' <- tyE0 e
-        a <- dummyName "a"
-        let a' = var a
-        pushConstraint l (tyStream a') (eLoc e') $> e'
-    pure $ Anchor (TyB Star TyUnit) es'
-tyE0 (Cond l p e0 e1) = do
-    p' <- tyE0 p
-    e0' <- tyE0 e0
-    e1' <- tyE0 e1
-    let ty0 = eLoc e0'
-    pushConstraint l tyBool (eLoc p')
-    pushConstraint (eLoc e0) ty0 (eLoc e1')
-    pure $ Cond ty0 p' e0' e1'
diff --git a/src/Jacinda/Ty/Const.hs b/src/Jacinda/Ty/Const.hs
deleted file mode 100644
--- a/src/Jacinda/Ty/Const.hs
+++ /dev/null
@@ -1,42 +0,0 @@
-module Jacinda.Ty.Const ( tyStream
-                        , tyStr, tyR
-                        , tyI
-                        , tyF
-                        , tyBool
-                        , hkt
-                        , tyOpt
-                        , mkVec
-                        ) where
-
-import           Jacinda.AST
-
--- | argument assumed to have kind 'Star'
-tyStream :: T K -> T K
-tyStream = TyApp Star (TyB (KArr Star Star) TyStream)
-
-tyBool :: T K
-tyBool = TyB Star TyBool
-
-tyI :: T K
-tyI = TyB Star TyInteger
-
-tyF :: T K
-tyF = TyB Star TyFloat
-
-tyStr :: T K
-tyStr = TyB Star TyStr
-
-tyR :: T K
-tyR = TyB Star TyR
-
-hkt :: T K -> T K -> T K
-hkt = TyApp Star
-
-tyOpt :: T K -> T K
-tyOpt = hkt (TyB (KArr Star Star) TyOption)
-
-tyVec :: T K
-tyVec = TyB (KArr Star Star) TyVec
-
-mkVec :: T K -> T K
-mkVec = hkt tyVec
diff --git a/src/L.x b/src/L.x
new file mode 100644
--- /dev/null
+++ b/src/L.x
@@ -0,0 +1,482 @@
+{
+    {-# LANGUAGE OverloadedStrings #-}
+    module L ( alexMonadScan
+             , alexInitUserState
+             , runAlex
+             , runAlexSt
+             , withAlexSt
+             , freshName
+             , AlexPosn (..)
+             , Alex (..)
+             , Token (..)
+             , Keyword (..)
+             , Sym (..)
+             , Builtin (..)
+             , Var (..)
+             , AlexUserState
+             ) where
+
+import Control.Arrow ((&&&))
+import Data.Bifunctor (first)
+import qualified Data.ByteString.Lazy as BSL
+import Data.Functor (($>))
+import qualified Data.IntMap as IM
+import qualified Data.Map as M
+import Data.Semigroup ((<>))
+import qualified Data.Text as T
+import Nm
+import Prettyprinter (Pretty (pretty), (<+>), colon, squotes)
+import U
+
+}
+
+%wrapper "monadUserState-strict-text"
+
+$digit = [0-9]
+
+$latin = [a-zA-Z]
+
+@follow_char = [$latin $digit \_]
+
+$str_special = [\\\']
+
+@escape_str = \\ [$str_special nt]
+
+@string = \' ([^ $str_special] | @escape_str)* \'
+
+@name = [a-z] @follow_char*
+@tyname = [A-Z] @follow_char*
+
+@float = $digit+\.$digit+
+
+tokens :-
+
+    <dfn> {
+        x                        { mkRes VarX }
+        y                        { mkRes VarY }
+    }
+
+    <0> "["                      { mkSym LSqBracket `andBegin` dfn } -- FIXME: this doesn't allow nested
+
+    <0,dfn> {
+
+        $white+                  ;
+
+        "{.".*                   ;
+        "#!".*                   ; -- shebang
+
+        ":="                     { mkSym DefEq }
+        "≔"                      { mkSym DefEq }
+        "{"                      { mkSym LBrace }
+        "}"                      { mkSym RBrace }
+
+        "#."                     { mkSym FilterTok }
+
+        -- symbols/operators
+        "%"                      { mkSym PercentTok }
+        "*"                      { mkSym TimesTok }
+        "**"                     { mkSym ExpTok }
+        "+"                      { mkSym PlusTok }
+        "-"                      { mkSym MinusTok }
+
+        "|"                      { mkSym FoldTok }
+        \"                       { mkSym Quot }
+        "^"                      { mkSym Caret }
+        "|>"                     { mkSym Fold1Tok }
+
+        "="                      { mkSym EqTok }
+        "!="                     { mkSym NeqTok }
+        "<="                     { mkSym LeqTok }
+        "<"                      { mkSym LtTok }
+        ">="                     { mkSym GeqTok }
+        ">"                      { mkSym GtTok }
+        "&"                      { mkSym AndTok }
+        "||"                     { mkSym OrTok }
+        "("                      { mkSym LParen }
+        ")"                      { mkSym RParen }
+        "&("                     { mkSym LAnchor }
+        "{%"                     { mkSym LBracePercent }
+        "{|"                     { mkSym LBraceBar }
+        "]"                      { mkSym RSqBracket `andBegin` 0 }
+        "~"                      { mkSym Tilde }
+        "!~"                     { mkSym NotMatchTok }
+        ","                      { mkSym Comma }
+        "."                      { mkSym Dot }
+        "#"                      { mkSym TallyTok }
+        "#*"                     { mkSym LengthTok }
+        "[:"                     { mkSym ConstTok }
+        "!"                      { mkSym Exclamation }
+        ":"                      { mkSym Colon }
+        ";"                      { mkSym Semicolon }
+        "\."                     { mkSym BackslashDot }
+        \\                       { mkSym Backslash }
+        λ                        { mkSym Backslash }
+        "|`"                     { mkSym CeilSym }
+        ⌈                        { mkSym CeilSym }
+        "|."                     { mkSym FloorSym }
+        ⌊                        { mkSym FloorSym }
+        "~."                     { mkSym DedupTok }
+        "~.*"                    { mkSym DedupOnTok }
+        ".?"                     { mkSym CatMaybesTok }
+        ":?"                     { mkSym MapMaybeTok }
+        "~*"                     { mkSym CapTok }
+        "-."                     { mkSym NegTok }
+        "`*"                     { mkSym LastFieldTok }
+
+        in                       { mkKw KwIn }
+        let                      { mkKw KwLet }
+        val                      { mkKw KwVal }
+        end                      { mkKw KwEnd }
+        :set                     { mkKw KwSet }
+        :flush                   { mkKw KwFlush }
+        fn                       { mkKw KwFn }
+        "@include"               { mkKw KwInclude }
+        if                       { mkKw KwIf }
+        then                     { mkKw KwThen }
+        else                     { mkKw KwElse }
+
+        fs                       { mkRes VarFs }
+        ix                       { mkRes VarIx }
+        ⍳                        { mkRes VarIx }
+        nf                       { mkRes VarNf }
+        ¨                        { mkSym Quot }
+        min                      { mkRes VarMin }
+        max                      { mkRes VarMax }
+
+        substr                   { mkBuiltin BuiltinSubstr }
+        split                    { mkBuiltin BuiltinSplit }
+        splitc                   { mkBuiltin BuiltinSplitc }
+        sprintf                  { mkBuiltin BuiltinSprintf }
+        option                   { mkBuiltin BuiltinOption }
+        floor                    { mkBuiltin BuiltinFloor }
+        ceil                     { mkBuiltin BuiltinCeil }
+        match                    { mkBuiltin BuiltinMatch }
+        captures                 { mkBuiltin BuiltinCaptures }
+        Some                     { mkBuiltin BuiltinSome }
+        None                     { mkBuiltin BuiltinNone }
+        fp                       { mkBuiltin BuiltinFp }
+
+        ":i"                     { mkBuiltin BuiltinIParse }
+        ":f"                     { mkBuiltin BuiltinFParse }
+
+        "#t"                     { tok (\p _ -> alex $ TokBool p True) }
+        "#f"                     { tok (\p _ -> alex $ TokBool p False) }
+
+        \$$digit+                { tok (\p s -> alex $ TokStreamLit p (read $ T.unpack $ T.tail s)) }
+        `$digit+                 { tok (\p s -> alex $ TokFieldLit p (read $ T.unpack $ T.tail s)) }
+
+        "."$digit+               { tok (\p s -> alex $ TokAccess p (read $ T.unpack $ T.tail s)) }
+        "->"$digit+              { tok (\p s -> alex $ TokSelect p (read $ T.unpack $ T.drop 2 s)) }
+        $digit+                  { tok (\p s -> alex $ TokInt p (read $ T.unpack s)) }
+        _$digit+                 { tok (\p s -> alex $ TokInt p (negate $ read $ T.unpack $ T.tail s)) }
+
+        $digit+\.$digit+         { tok (\p s -> alex $ TokFloat p (read $ T.unpack s)) }
+        _$digit+\.$digit+        { tok (\p s -> alex $ TokFloat p (negate $ read $ T.unpack $ T.tail s)) }
+
+        @string                  { tok (\p s -> alex $ TokStr p (escReplace $ T.init $ T.tail s)) }
+
+        -- TODO: allow chars to be escaped
+        "/"[^\/]*"/"             { tok (\p s -> alex $ TokRR p (T.init $ T.tail s)) }
+
+        @name                    { tok (\p s -> TokName p <$> newIdentAlex p s) }
+        @tyname                  { tok (\p s -> TokTyName p <$> newIdentAlex p s) }
+
+    }
+
+{
+
+dropQuotes :: BSL.ByteString -> BSL.ByteString
+dropQuotes = BSL.init . BSL.tail
+
+alex :: a -> Alex a
+alex = pure
+
+tok f (p,_,_,s) len = f p (T.take len s)
+
+constructor c t = tok (\p _ -> alex $ c p t)
+
+mkRes = constructor TokResVar
+
+mkKw = constructor TokKeyword
+
+mkSym = constructor TokSym
+
+mkBuiltin = constructor TokBuiltin
+
+-- this is inefficient but w/e
+escReplace :: T.Text -> T.Text
+escReplace =
+      T.replace "\\\"" "\""
+    . T.replace "\\n" "\n"
+    . T.replace "\\t" "\t"
+
+instance Pretty AlexPosn where
+    pretty (AlexPn _ line col) = pretty line <> colon <> pretty col
+
+-- functional bimap?
+type AlexUserState = (Int, M.Map T.Text Int, IM.IntMap (Nm AlexPosn))
+
+alexInitUserState :: AlexUserState
+alexInitUserState = (0, mempty, mempty)
+
+gets_alex :: (AlexState -> a) -> Alex a
+gets_alex f = Alex (Right . (id &&& f))
+
+get_ust :: Alex AlexUserState
+get_ust = gets_alex alex_ust
+
+get_pos :: Alex AlexPosn
+get_pos = gets_alex alex_pos
+
+set_ust :: AlexUserState -> Alex ()
+set_ust st = Alex (Right . (go &&& (const ())))
+    where go s = s { alex_ust = st }
+
+alexEOF = EOF <$> get_pos
+
+data Sym = PlusTok
+         | MinusTok
+         | PercentTok
+         | ExpTok
+         | FoldTok
+         | Fold1Tok
+         | Quot
+         | TimesTok
+         | DefEq
+         | Colon
+         | LBrace
+         | RBrace
+         | LParen
+         | LAnchor
+         | RParen
+         | LSqBracket
+         | RSqBracket
+         | Semicolon
+         | Underscore
+         | EqTok
+         | LeqTok
+         | LtTok
+         | NeqTok
+         | GeqTok
+         | GtTok
+         | AndTok
+         | OrTok
+         | Tilde
+         | NotMatchTok
+         | Comma
+         | Dot
+         | TallyTok
+         | LengthTok
+         | ConstTok
+         | LBracePercent
+         | LBraceBar
+         | Exclamation
+         | Caret
+         | Backslash
+         | BackslashDot
+         | FilterTok
+         | FloorSym
+         | CeilSym
+         | DedupTok
+         | DedupOnTok
+         | CatMaybesTok
+         | MapMaybeTok
+         | CapTok
+         | NegTok
+         | LastFieldTok
+
+instance Pretty Sym where
+    pretty PlusTok       = "+"
+    pretty MinusTok      = "-"
+    pretty PercentTok    = "%"
+    pretty ExpTok        = "**"
+    pretty FoldTok       = "|"
+    pretty Fold1Tok      = "|>"
+    pretty TimesTok      = "*"
+    pretty DefEq         = ":="
+    pretty Colon         = ":"
+    pretty LBrace        = "{"
+    pretty RBrace        = "}"
+    pretty Semicolon     = ";"
+    pretty Underscore    = "_"
+    pretty EqTok         = "="
+    pretty LeqTok        = "<="
+    pretty LtTok         = "<"
+    pretty NeqTok        = "!="
+    pretty GeqTok        = ">="
+    pretty GtTok         = ">"
+    pretty AndTok        = "&"
+    pretty OrTok         = "||"
+    pretty LParen        = "("
+    pretty RParen        = ")"
+    pretty LAnchor       = "&("
+    pretty LSqBracket    = "["
+    pretty RSqBracket    = "]"
+    pretty Tilde         = "~"
+    pretty NotMatchTok   = "!~"
+    pretty Comma         = ","
+    pretty Dot           = "."
+    pretty TallyTok      = "#"
+    pretty LengthTok     = "#*"
+    pretty Quot          = "¨"
+    pretty Caret         = "^"
+    pretty ConstTok      = "[:"
+    pretty LBracePercent = "{%"
+    pretty LBraceBar     = "{|"
+    pretty Exclamation   = "!"
+    pretty Backslash     = "\\"
+    pretty BackslashDot  = "\\."
+    pretty FilterTok     = "#."
+    pretty FloorSym      = "⌊"
+    pretty CeilSym       = "⌈"
+    pretty DedupTok      = "~."
+    pretty DedupOnTok    = "~.*"
+    pretty CatMaybesTok  = ".?"
+    pretty MapMaybeTok   = ":?"
+    pretty CapTok        = "~*"
+    pretty NegTok        = "-."
+    pretty LastFieldTok  = "`*"
+
+data Keyword = KwLet
+             | KwIn
+             | KwVal
+             | KwEnd
+             | KwSet
+             | KwFlush
+             | KwFn
+             | KwInclude
+             | KwIf
+             | KwThen
+             | KwElse
+
+-- | Reserved/special variables
+data Var = VarX
+         | VarY
+         | VarFs
+         | VarIx
+         | VarMin
+         | VarMax
+         | VarNf
+
+instance Pretty Var where
+    pretty VarX     = "x"
+    pretty VarY     = "y"
+    pretty VarFs    = "fs"
+    pretty VarIx    = "⍳"
+    pretty VarNf    = "nf"
+    pretty VarMin   = "min"
+    pretty VarMax   = "max"
+
+instance Pretty Keyword where
+    pretty KwLet     = "let"
+    pretty KwIn      = "in"
+    pretty KwVal     = "val"
+    pretty KwEnd     = "end"
+    pretty KwSet     = ":set"
+    pretty KwFlush   = ":flush"
+    pretty KwFn      = "fn"
+    pretty KwInclude = "@include"
+    pretty KwIf      = "if"
+    pretty KwThen    = "then"
+    pretty KwElse    = "else"
+
+data Builtin = BuiltinIParse
+             | BuiltinFParse
+             | BuiltinSubstr
+             | BuiltinSplit
+             | BuiltinSplitc
+             | BuiltinOption
+             | BuiltinSprintf
+             | BuiltinFloor
+             | BuiltinCeil
+             | BuiltinMatch
+             | BuiltinCaptures
+             | BuiltinSome
+             | BuiltinNone
+             | BuiltinFp
+
+instance Pretty Builtin where
+    pretty BuiltinIParse   = ":i"
+    pretty BuiltinFParse   = ":f"
+    pretty BuiltinSubstr   = "substr"
+    pretty BuiltinSplit    = "split"
+    pretty BuiltinOption   = "option"
+    pretty BuiltinSplitc   = "splitc"
+    pretty BuiltinSprintf  = "sprintf"
+    pretty BuiltinFloor    = "floor"
+    pretty BuiltinCeil     = "ceil"
+    pretty BuiltinMatch    = "match"
+    pretty BuiltinSome     = "Some"
+    pretty BuiltinNone     = "None"
+    pretty BuiltinFp       = "fp"
+    pretty BuiltinCaptures = "captures"
+
+data Token a = EOF { loc :: a }
+             | TokSym { loc :: a, _sym :: Sym }
+             | TokName { loc :: a, _name :: Nm a }
+             | TokTyName { loc :: a, _tyName :: TyName a }
+             | TokBuiltin { loc :: a, _builtin :: Builtin }
+             | TokKeyword { loc :: a, _kw :: Keyword }
+             | TokResVar { loc :: a, _var :: Var }
+             | TokInt { loc :: a, int :: Integer }
+             | TokFloat { loc :: a, float :: Double }
+             | TokBool { loc :: a, boolTok :: Bool }
+             | TokStr { loc :: a, strTok :: T.Text }
+             | TokStreamLit { loc :: a, ix :: Int }
+             | TokFieldLit { loc :: a, ix :: Int }
+             | TokRR { loc :: a, rr :: T.Text }
+             | TokAccess { loc :: a, ix :: Int }
+             | TokSelect { loc :: a, field :: Int }
+
+instance Pretty (Token a) where
+    pretty EOF{}              = "(eof)"
+    pretty (TokSym _ s)       = "symbol" <+> squotes (pretty s)
+    pretty (TokName _ n)      = "identifier" <+> squotes (pretty n)
+    pretty (TokTyName _ tn)   = "identifier" <+> squotes (pretty tn)
+    pretty (TokBuiltin _ b)   = "builtin" <+> squotes (pretty b)
+    pretty (TokKeyword _ kw)  = "keyword" <+> squotes (pretty kw)
+    pretty (TokInt _ i)       = pretty i
+    pretty (TokStr _ str)     = squotes (pretty str)
+    pretty (TokStreamLit _ i) = "$" <> pretty i
+    pretty (TokFieldLit _ i)  = "`" <> pretty i
+    pretty (TokRR _ rr')      = "/" <> pretty rr' <> "/"
+    pretty (TokResVar _ v)    = "reserved variable" <+> squotes (pretty v)
+    pretty (TokBool _ True)   = "#t"
+    pretty (TokBool _ False)  = "#f"
+    pretty (TokAccess _ i)    = "." <> pretty i
+    pretty (TokFloat _ f)     = pretty f
+    pretty (TokSelect _ i)    = "->" <> pretty i
+
+freshName :: T.Text -> Alex (Nm AlexPosn)
+freshName t = do
+    pos <- get_pos
+    newIdentAlex pos t
+
+newIdentAlex :: AlexPosn -> T.Text -> Alex (Nm AlexPosn)
+newIdentAlex pos t = do
+    st <- get_ust
+    let (st', n) = newIdent pos t st
+    set_ust st' $> (n $> pos)
+
+newIdent :: AlexPosn -> T.Text -> AlexUserState -> (AlexUserState, Nm AlexPosn)
+newIdent pos t pre@(max', names, uniqs) =
+    case M.lookup t names of
+        Just i -> (pre, Nm t (U i) pos)
+        Nothing -> let i = max' + 1
+            in let newName = Nm t (U i) pos
+                in ((i, M.insert t i names, IM.insert i newName uniqs), newName)
+
+runAlexSt :: T.Text -> Alex a -> Either String (AlexUserState, a)
+runAlexSt inp = withAlexSt inp alexInitUserState
+
+withAlexSt :: T.Text -> AlexUserState -> Alex a -> Either String (AlexUserState, a)
+withAlexSt inp ust (Alex f) = first alex_ust <$> f
+    (AlexState { alex_bytes = []
+               , alex_pos = alexStartPos
+               , alex_inp = inp
+               , alex_chr = '\n'
+               , alex_ust = ust
+               , alex_scd = 0
+               })
+
+}
diff --git a/src/Nm.hs b/src/Nm.hs
new file mode 100644
--- /dev/null
+++ b/src/Nm.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE DeriveFunctor #-}
+
+module Nm ( Nm (..)
+          , TyName
+          , eqName
+          ) where
+
+import qualified Data.Text     as T
+import           Prettyprinter (Pretty (pretty))
+import           U
+
+data Nm a = Nm { name   :: T.Text
+               , unique :: !U
+               , loc    :: a
+               } deriving (Functor)
+
+-- for testing
+eqName :: Nm a -> Nm a -> Bool
+eqName (Nm n _ _) (Nm n' _ _) = n == n'
+
+instance Eq (Nm a) where
+    (==) (Nm _ u _) (Nm _ u' _) = u == u'
+
+instance Pretty (Nm a) where
+    pretty (Nm t _ _) = pretty t
+
+instance Show (Nm a) where show=show.pretty
+
+instance Ord (Nm a) where
+    compare (Nm _ u _) (Nm _ u' _) = compare u u'
+
+type TyName = Nm
diff --git a/src/Parser.y b/src/Parser.y
new file mode 100644
--- /dev/null
+++ b/src/Parser.y
@@ -0,0 +1,360 @@
+{
+    {-# LANGUAGE OverloadedStrings #-}
+    module Parser ( parse
+                  , parseWithMax
+                  , parseWithInitCtx
+                  , parseWithCtx
+                  , parseLibWithCtx
+                  , ParseError (..)
+                  -- * Type synonyms
+                  , File
+                  , Library
+                  ) where
+
+import Control.Exception (Exception)
+import Control.Monad.Except (ExceptT, runExceptT, throwError)
+import Control.Monad.Trans.Class (lift)
+import Data.Bifunctor (first)
+import qualified Data.ByteString.Lazy as BSL
+import qualified Data.Text as T
+import Data.Text.Encoding (encodeUtf8)
+import Data.Typeable (Typeable)
+import A
+import L
+import Nm hiding (loc)
+import qualified Nm
+import Prettyprinter (Pretty (pretty), (<+>))
+
+}
+
+%name parseF File
+%name parseLib Library
+%tokentype { Token AlexPosn }
+%error { parseError }
+%monad { Parse } { (>>=) } { pure }
+%lexer { lift alexMonadScan >>= } { EOF _ }
+
+%token
+
+    defEq { TokSym $$ DefEq }
+    colon { TokSym $$ Colon }
+    lbrace { TokSym $$ LBrace }
+    rbrace { TokSym $$ RBrace }
+    lsqbracket { TokSym $$ LSqBracket }
+    rsqbracket { TokSym $$ RSqBracket }
+    lparen { TokSym $$ LParen }
+    lanchor { TokSym $$ LAnchor }
+    rparen { TokSym $$ RParen }
+    semicolon { TokSym $$ Semicolon }
+    backslash { TokSym $$ Backslash }
+    tilde { TokSym $$ Tilde }
+    notMatch { TokSym $$ NotMatchTok }
+    dot { TokSym $$ Dot }
+    lbracePercent { TokSym $$ LBracePercent }
+    lbraceBar { TokSym $$ LBraceBar }
+    tally { TokSym $$ TallyTok }
+    tallyL { TokSym $$ LengthTok }
+    const { TokSym $$ ConstTok }
+    filter { TokSym $$ FilterTok }
+    exclamation { TokSym $$ Exclamation }
+    backslashdot { TokSym $$ BackslashDot }
+    at { $$@(TokAccess _ _) }
+    select { $$@(TokSelect _ _) }
+    floorSym { TokSym $$ FloorSym }
+    ceilSym { TokSym $$ CeilSym }
+    dedup { TokSym $$ DedupTok }
+    dedupon { TokSym $$ DedupOnTok }
+
+    plus { TokSym $$ PlusTok }
+    minus { TokSym $$ MinusTok }
+    times { TokSym $$ TimesTok }
+    percent { TokSym $$ PercentTok }
+    exp { TokSym $$ ExpTok }
+
+    comma { TokSym $$ Comma }
+    fold { TokSym $$ FoldTok }
+    fold1 { TokSym $$ Fold1Tok }
+    caret { TokSym $$ Caret }
+    quot { TokSym $$ Quot }
+    mapMaybe { TokSym $$ MapMaybeTok }
+    catMaybes { TokSym $$ CatMaybesTok }
+    capture { TokSym $$ CapTok }
+    neg { TokSym $$ NegTok }
+
+    eq { TokSym $$ EqTok }
+    neq { TokSym $$ NeqTok }
+    leq { TokSym $$ LeqTok }
+    lt { TokSym $$ LtTok }
+    geq { TokSym $$ GeqTok }
+    gt { TokSym $$ GtTok }
+
+    and { TokSym $$ AndTok }
+    or { TokSym $$ OrTok }
+
+    name { TokName _ $$ }
+    tyName { TokTyName  _ $$ }
+
+    intLit { $$@(TokInt _ _) }
+    floatLit { $$@(TokFloat _ _) }
+    boolLit { $$@(TokBool _ _) }
+    strLit { $$@(TokStr _ _) }
+    allColumn { TokStreamLit $$ 0 }
+    allField { TokFieldLit $$ 0 }
+    column { $$@(TokStreamLit _ _) }
+    field { $$@(TokFieldLit _ _) }
+    lastField { TokSym $$ LastFieldTok } -- TokSym is maybe insensible but whatever
+
+    let { TokKeyword $$ KwLet }
+    in { TokKeyword $$ KwIn }
+    val { TokKeyword $$ KwVal }
+    end { TokKeyword $$ KwEnd }
+    set { TokKeyword $$ KwSet }
+    flush { TokKeyword $$ KwFlush }
+    fn { TokKeyword $$ KwFn }
+    include { TokKeyword $$ KwInclude }
+    if { TokKeyword $$ KwIf }
+    then { TokKeyword $$ KwThen }
+    else { TokKeyword $$ KwElse }
+
+    x { TokResVar $$ VarX }
+    y { TokResVar $$ VarY }
+
+    min { TokResVar $$ VarMin }
+    max { TokResVar $$ VarMax }
+    ix { TokResVar $$ VarIx }
+    nf { TokResVar $$ VarNf }
+    fs { TokResVar $$ VarFs }
+
+    split { TokBuiltin $$ BuiltinSplit }
+    splitc { TokBuiltin $$ BuiltinSplitc }
+    substr { TokBuiltin $$ BuiltinSubstr }
+    sprintf { TokBuiltin $$ BuiltinSprintf }
+    floor { TokBuiltin $$ BuiltinFloor }
+    ceil { TokBuiltin $$ BuiltinCeil }
+    option { TokBuiltin $$ BuiltinOption }
+    match { TokBuiltin $$ BuiltinMatch }
+    some { TokBuiltin $$ BuiltinSome }
+    none { TokBuiltin $$ BuiltinNone }
+    fp { TokBuiltin $$ BuiltinFp }
+    captures { TokBuiltin $$ BuiltinCaptures }
+
+    iParse { TokBuiltin $$ BuiltinIParse }
+    fParse { TokBuiltin $$ BuiltinFParse }
+
+    rr { $$@(TokRR _ _) }
+
+%right const
+%left paren iParse fParse
+%nonassoc leq geq gt lt neq eq
+
+%%
+
+many(p)
+    : many(p) p { $2 : $1 }
+    | { [] }
+
+sepBy(p,q)
+    : sepBy(p,q) q p { $3 : $1 }
+    | p q p { $3 : [$1] }
+
+braces(p)
+    : lbrace p rbrace { $2 }
+
+brackets(p)
+    : lsqbracket p rsqbracket { $2 }
+
+parens(p)
+    : lparen p rparen { $2 }
+
+-- binary operator
+BBin :: { BBin }
+     : plus { Plus }
+     | times { Times }
+     | minus { Minus }
+     | percent { Div }
+     | gt { Gt }
+     | lt { Lt }
+     | geq { Geq }
+     | leq { Leq }
+     | eq { Eq }
+     | neq { Neq }
+     | quot { Map }
+     | mapMaybe { MapMaybe }
+     | tilde { Matches }
+     | notMatch { NotMatches }
+     | and { And }
+     | or { Or }
+     | backslashdot { Prior }
+     | filter { Filter }
+     | fold1 { Fold1 }
+     | exp { Exp }
+     | dedupon { DedupOn }
+
+Bind :: { (Nm AlexPosn, E AlexPosn) }
+     : val name defEq E { ($2, $4) }
+
+Args :: { [(Nm AlexPosn)] }
+     : lparen rparen { [] }
+     | parens(name) { [$1] }
+     | parens(sepBy(name, comma)) { reverse $1 }
+
+D :: { D AlexPosn }
+  : set fs defEq rr semicolon { SetFS (rr $4) }
+  | flush semicolon { FlushDecl }
+  | fn name Args defEq E semicolon { FunDecl $2 $3 $5 }
+  | fn name defEq E semicolon { FunDecl $2 [] $4 }
+
+Include :: { FilePath }
+        : include strLit { T.unpack (strTok $2) }
+
+File :: { ([FilePath], Program AlexPosn) }
+     : many(Include) Program { (reverse $1, $2) }
+
+Library :: { Library }
+        : many(Include) many(D) { (reverse $1, reverse $2) }
+
+Program :: { Program AlexPosn }
+        : many(D) E { Program (reverse $1) $2 }
+
+E :: { E AlexPosn }
+  : name { Var (Nm.loc $1) $1 }
+  | intLit { ILit (loc $1) (int $1) }
+  | floatLit { FLit (loc $1) (float $1) }
+  | boolLit { BLit (loc $1) (boolTok $1) }
+  | strLit { StrLit (loc $1) (encodeUtf8 $ strTok $1) }
+  | column { Column (loc $1) (ix $1) }
+  | field { Field (loc $1) (ix $1) }
+  | allColumn { AllColumn $1 }
+  | allField { AllField $1 }
+  | lastField { LastField $1 }
+  | field iParse { EApp (loc $1) (UB $2 IParse) (Field (loc $1) (ix $1)) }
+  | field fParse { EApp (loc $1) (UB $2 FParse) (Field (loc $1) (ix $1)) }
+  | name iParse { EApp (Nm.loc $1) (UB $2 IParse) (Var (Nm.loc $1) $1) }
+  | name fParse { EApp (Nm.loc $1) (UB $2 FParse) (Var (Nm.loc $1) $1) }
+  | field colon { EApp (loc $1) (UB $2 Parse) (Field (loc $1) (ix $1)) }
+  | name colon { EApp (Nm.loc $1) (UB $2 Parse) (Var (Nm.loc $1) $1) }
+  | lastField iParse { EApp $1 (UB $2 IParse) (LastField $1) }
+  | lastField fParse { EApp $1 (UB $2 FParse) (LastField $1) }
+  | lastField colon { EApp $1 (UB $2 Parse) (LastField $1) }
+  | x colon { EApp $1 (UB $2 Parse) (ResVar $1 X) }
+  | y colon { EApp $1 (UB $2 Parse) (ResVar $1 Y) }
+  | x iParse { EApp $1 (UB $2 IParse) (ResVar $1 X) }
+  | x fParse { EApp $1 (UB $2 FParse) (ResVar $1 X) }
+  | y iParse { EApp $1 (UB $2 IParse) (ResVar $1 Y) }
+  | y fParse { EApp $1 (UB $2 FParse) (ResVar $1 Y) }
+  | column iParse { IParseCol (loc $1) (ix $1) }
+  | column fParse { FParseCol (loc $1) (ix $1) }
+  | column colon { ParseCol (loc $1) (ix $1) }
+  | parens(iParse) { UB $1 IParse }
+  | parens(fParse) { UB $1 FParse }
+  | parens(colon) { UB $1 Parse }
+  | lparen BBin rparen { BB $1 $2 }
+  | lparen E BBin rparen { EApp $1 (BB $1 $3) $2 }
+  | lparen BBin E rparen {% do { n <- lift $ freshName "x" ; pure (Lam $1 n (EApp $1 (EApp $1 (BB $1 $2) (Var (Nm.loc n) n)) $3)) } }
+  | E BBin E { EApp (eLoc $1) (EApp (eLoc $3) (BB (eLoc $1) $2) $1) $3 }
+  | E fold E E { EApp (eLoc $1) (EApp (eLoc $1) (EApp $2 (TB $2 Fold) $1) $3) $4 }
+  | E capture E E { EApp (eLoc $1) (EApp (eLoc $1) (EApp $2 (TB $2 Captures) $1) $3) $4 }
+  | E caret E E { EApp (eLoc $1) (EApp (eLoc $1) (EApp $2 (TB $2 Scan) $1) $3) $4 }
+  | comma E E E { EApp $1 (EApp $1 (EApp $1 (TB $1 ZipW) $2) $3) $4 }
+  | lbrace E rbrace braces(E) { Guarded $1 $2 $4 }
+  | lbracePercent E rbrace braces(E) { let tl = eLoc $2 in Guarded $1 (EApp tl (EApp tl (BB tl Matches) (AllField tl)) $2) $4 }
+  | lbraceBar E rbrace { Implicit $1 $2 }
+  | let many(Bind) in E end { mkLet $1 (reverse $2) $4 }
+  | lparen sepBy(E, dot) rparen { Tup $1 (reverse $2) }
+  | lanchor sepBy(E, dot) rparen { Anchor $1 (reverse $2) }
+  | E E { EApp (eLoc $1) $1 $2 }
+  | tally { UB $1 Tally }
+  | tallyL { UB $1 TallyList }
+  | const { UB $1 Const }
+  | exclamation { UB $1 Not }
+  | lsqbracket E rsqbracket { Dfn $1 $2 }
+  | x { ResVar $1 X }
+  | y { ResVar $1 Y }
+  | rr { RegexLit (loc $1) (encodeUtf8 $ rr $1) }
+  | min { BB $1 Min }
+  | max { BB $1 Max }
+  | split { BB $1 Split }
+  | match { BB $1 Match }
+  | splitc { BB $1 Splitc }
+  | substr { TB $1 Substr }
+  | sprintf { BB $1 Sprintf }
+  | option { TB $1 Option }
+  | captures { TB $1 AllCaptures }
+  | floor { UB $1 Floor }
+  | ceil { UB $1 Ceiling }
+  | floorSym { UB $1 Floor }
+  | ceilSym { UB $1 Ceiling }
+  | dedup { UB $1 Dedup }
+  | some { UB $1 Some }
+  | catMaybes { UB $1 CatMaybes }
+  | neg { UB $1 Negate }
+  | ix { NB $1 Ix }
+  | nf { NB $1 Nf }
+  | none { NB $1 None }
+  | fp { NB $1 Fp }
+  | parens(at) { UB (loc $1) (At $ ix $1) }
+  | parens(select) { UB (loc $1) (Select $ field $1) }
+  | E at { EApp (eLoc $1) (UB (loc $2) (At $ ix $2)) $1 }
+  | E select { EApp (eLoc $1) (UB (loc $2) (Select $ field $2)) $1 }
+  | backslash name dot E { Lam $1 $2 $4 }
+  | parens(E) { Paren (eLoc $1) $1 }
+  | if E then E else E { Cond $1 $2 $4 $6 }
+
+{
+
+type File = ([FilePath], Program AlexPosn)
+
+type Library = ([FilePath], [D AlexPosn])
+
+parseError :: Token AlexPosn -> Parse a
+parseError = throwError . Unexpected
+
+mkLet :: a -> [(Nm a, E a)] -> E a -> E a
+mkLet _ [] e     = e
+mkLet l (b:bs) e = Let l b (mkLet l bs e)
+
+data ParseError a = Unexpected (Token a)
+                  | LexErr String
+
+instance Pretty a => Pretty (ParseError a) where
+    pretty (Unexpected tok)  = pretty (loc tok) <+> "Unexpected" <+> pretty tok
+    pretty (LexErr str)      = pretty (T.pack str)
+
+instance Pretty a => Show (ParseError a) where
+    show = show . pretty
+
+instance (Pretty a, Typeable a) => Exception (ParseError a)
+
+type Parse = ExceptT (ParseError AlexPosn) Alex
+
+parse :: T.Text -> Either (ParseError AlexPosn) File
+parse = fmap snd . runParse parseF
+
+parseWithMax :: T.Text -> Either (ParseError AlexPosn) (Int, File)
+parseWithMax = fmap (first fst3) . runParse parseF
+    where fst3 (x, _, _) = x
+
+parseWithInitCtx :: T.Text -> Either (ParseError AlexPosn) (AlexUserState, File)
+parseWithInitCtx bsl = parseWithCtx bsl alexInitUserState
+
+parseWithCtx :: T.Text -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, File)
+parseWithCtx = parseWithInitSt parseF
+
+parseLibWithCtx :: T.Text -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, Library)
+parseLibWithCtx = parseWithInitSt parseLib
+
+runParse :: Parse a -> T.Text -> Either (ParseError AlexPosn) (AlexUserState, a)
+runParse parser str = liftErr $ runAlexSt str (runExceptT parser)
+
+parseWithInitSt :: Parse a -> T.Text -> AlexUserState -> Either (ParseError AlexPosn) (AlexUserState, a)
+parseWithInitSt parser str st = liftErr $ withAlexSt str st (runExceptT parser)
+    where liftErr (Left err)            = Left (LexErr err)
+          liftErr (Right (_, Left err)) = Left err
+          liftErr (Right (i, Right x))  = Right (i, x)
+
+liftErr :: Either String (b, Either (ParseError a) c) -> Either (ParseError a) (b, c)
+liftErr (Left err)            = Left (LexErr err)
+liftErr (Right (_, Left err)) = Left err
+liftErr (Right (i, Right x))  = Right (i, x)
+
+}
diff --git a/src/Parser/Rw.hs b/src/Parser/Rw.hs
new file mode 100644
--- /dev/null
+++ b/src/Parser/Rw.hs
@@ -0,0 +1,59 @@
+module Parser.Rw ( rwP
+                 , rwD
+                 , rwE
+                 ) where
+
+
+import           A
+import           Control.Recursion (cata, embed)
+
+rwP :: Program a -> Program a
+rwP (Program ds e) = Program (rwD <$> ds) (rwE e)
+
+rwD :: D a -> D a
+rwD (FunDecl n bs e) = FunDecl n bs (rwE e); rwD d = d
+
+rwE :: E a -> E a
+rwE = cata a where
+    a (EAppF l e0@(UB _ Tally) (EApp lϵ (EApp lϵϵ e1@BB{} e2) e3))                      = EApp l (EApp lϵ e1 (EApp lϵϵ e0 e2)) e3
+    a (EAppF l e0@(UB _ Const) (EApp lϵ (EApp lϵϵ e1@(BB _ Map) e2) e3))                = EApp l (EApp lϵ e1 (EApp lϵϵ e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Eq) _) (EApp l1 (EApp l2 e1@(BB _ And) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Eq) _) (EApp l1 (EApp l2 e1@(BB _ Or) e2) e3))          = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Neq) _) (EApp l1 (EApp l2 e1@(BB _ And) e2) e3))        = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Neq) _) (EApp l1 (EApp l2 e1@(BB _ Or) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Gt) _) (EApp l1 (EApp l2 e1@(BB _ And) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Gt) _) (EApp l1 (EApp l2 e1@(BB _ Or) e2) e3))          = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Lt) _) (EApp l1 (EApp l2 e1@(BB _ And) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Lt) _) (EApp l1 (EApp l2 e1@(BB _ Or) e2) e3))          = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Leq) _) (EApp l1 (EApp l2 e1@(BB _ And) e2) e3))        = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Leq) _) (EApp l1 (EApp l2 e1@(BB _ Or) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Geq) _) (EApp l1 (EApp l2 e1@(BB _ And) e2) e3))        = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Geq) _) (EApp l1 (EApp l2 e1@(BB _ Or) e2) e3))         = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Matches) _) (EApp l1 (EApp l2 e1@(BB _ And) e2) e3))    = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Matches) _) (EApp l1 (EApp l2 e1@(BB _ Or) e2) e3))     = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ NotMatches) _) (EApp l1 (EApp l2 e1@(BB _ And) e2) e3)) = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ NotMatches) _) (EApp l1 (EApp l2 e1@(BB _ Or) e2) e3))  = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Fold1) _) (EApp l1 (EApp l2 e1@(BB _ Eq) e2) e3))       = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Fold1) _) (EApp l1 (EApp l2 e1@(BB _ Neq) e2) e3))      = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Fold1) _) (EApp l1 (EApp l2 e1@(BB _ Gt) e2) e3))       = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Fold1) _) (EApp l1 (EApp l2 e1@(BB _ Geq) e2) e3))      = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Fold1) _) (EApp l1 (EApp l2 e1@(BB _ Leq) e2) e3))      = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@(EApp _ (BB _ Fold1) _) (EApp l1 (EApp l2 e1@(BB _ Lt) e2) e3))       = EApp l1 (EApp l2 e1 (EApp l e0 e2)) e3
+    a (EAppF l e0@Var{} (EApp lϵ (EApp lϵϵ e1 e2) e3))                                  = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
+    -- TODO rewrite dfn
+    a (EAppF l e0@Var{} (EApp l0 e1 (EApp l1 (EApp l2 op@BB{} e2) e3)))                 = EApp l1 (EApp l2 op (EApp l (EApp l0 e0 e1) e2)) e3
+    a (EAppF l e0@Var{} (EApp lϵ e1 e2))                                                = EApp l (EApp lϵ e0 e1) e2
+    a (EAppF l e0@(BB _ Max) (EApp lϵ e1 e2))                                           = EApp l (EApp lϵ e0 e1) e2
+    a (EAppF l e0@(BB _ Min) (EApp lϵ e1 e2))                                           = EApp l (EApp lϵ e0 e1) e2
+    a (EAppF l e0@(BB _ Split) (EApp lϵ e1 e2))                                         = EApp l (EApp lϵ e0 e1) e2
+    a (EAppF l e0@(BB _ Match) (EApp lϵ e1 e2))                                         = EApp l (EApp lϵ e0 e1) e2
+    a (EAppF l e0@(BB _ Splitc) (EApp lϵ e1 e2))                                        = EApp l (EApp lϵ e0 e1) e2
+    a (EAppF l e0@(BB _ Sprintf) (EApp lϵ e1 e2))                                       = EApp l (EApp lϵ e0 e1) e2
+    a (EAppF l e0@(TB _ Substr) (EApp lϵ (EApp lϵϵ e1 e2) e3))                          = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
+    a (EAppF l e0@(TB _ Substr) (EApp lϵ e1 (EApp lϵϵ e2 e3)))                          = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
+    a (EAppF l e0@(TB _ Option) (EApp lϵ (EApp lϵϵ e1 e2) e3))                          = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
+    a (EAppF l e0@(TB _ Option) (EApp lϵ e1 (EApp lϵϵ e2 e3)))                          = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
+    a (EAppF l e0@(TB _ Option) (EApp lϵ e1 e2))                                        = EApp l (EApp lϵ e0 e1) e2
+    a (EAppF l e0@(TB _ AllCaptures) (EApp lϵ (EApp lϵϵ e1 e2) e3))                     = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
+    a (EAppF l e0@(TB _ AllCaptures) (EApp lϵ e1 (EApp lϵϵ e2 e3)))                     = EApp l (EApp lϵ (EApp lϵϵ e0 e1) e2) e3
+    a x                                                                                 = embed x
diff --git a/src/R.hs b/src/R.hs
new file mode 100644
--- /dev/null
+++ b/src/R.hs
@@ -0,0 +1,152 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module R ( rE
+         , rP
+         , RenameM
+         , Renames (..)
+         , HasRenames (..)
+         ) where
+
+import           A
+import           Control.Monad.State.Strict (MonadState, State, runState)
+import           Control.Recursion          (cata, embed)
+import           Data.Bifunctor             (second)
+import qualified Data.IntMap                as IM
+import qualified Data.Text                  as T
+import           Lens.Micro                 (Lens', over)
+import           Lens.Micro.Mtl             (modifying, use, (%=), (.=))
+import           Nm
+import           U
+
+data Renames = Renames { max_ :: Int, bound :: IM.IntMap Int }
+
+class HasRenames a where
+    rename :: Lens' a Renames
+
+instance HasRenames Renames where
+    rename = id
+
+boundLens :: Lens' Renames (IM.IntMap Int)
+boundLens f s = fmap (\x -> s { bound = x }) (f (bound s))
+
+maxLens :: Lens' Renames Int
+maxLens f s = fmap (\x -> s { max_ = x }) (f (max_ s))
+
+type RenameM = State Renames
+
+rP :: Int -> Program a -> (Program a, Int)
+rP i = runRM i . renameProgram
+
+runRM :: Int -> RenameM x -> (x, Int)
+runRM i act = second max_ (runState act (Renames i IM.empty))
+
+replaceUnique :: (MonadState s m, HasRenames s) => U -> m U
+replaceUnique u@(U i) = do
+    rSt <- use (rename.boundLens)
+    case IM.lookup i rSt of
+        Nothing -> pure u
+        Just j  -> withRenames (over boundLens (IM.delete i)) $ replaceUnique (U j)
+
+replaceVar :: (MonadState s m, HasRenames s) => Nm a -> m (Nm a)
+replaceVar (Nm n u l) = do
+    u' <- replaceUnique u
+    pure $ Nm n u' l
+
+dummyName :: (MonadState s m, HasRenames s) => a -> T.Text -> m (Nm a)
+dummyName l n = do
+    st <- use (rename.maxLens)
+    Nm n (U$st+1) l
+        <$ modifying (rename.maxLens) (+1)
+
+withRenames :: (HasRenames s, MonadState s m) => (Renames -> Renames) -> m a -> m a
+withRenames modSt act = do
+    preSt <- use rename
+    rename %= modSt
+    res <- act
+    postMax <- use (rename.maxLens)
+    rename .= setMax postMax preSt
+    pure res
+
+withName :: (HasRenames s, MonadState s m) => Nm a -> m (Nm a, Renames -> Renames)
+withName (Nm t (U i) l) = do
+    m <- use (rename.maxLens)
+    let newUniq = m+1
+    rename.maxLens .= newUniq
+    pure (Nm t (U newUniq) l, mapBound (IM.insert i (m+1)))
+
+mapBound :: (IM.IntMap Int -> IM.IntMap Int) -> Renames -> Renames
+mapBound f (Renames m b) = Renames m (f b)
+
+setMax :: Int -> Renames -> Renames
+setMax i (Renames _ b) = Renames i b
+
+-- | Desguar top-level functions as lambdas
+mkLam :: [Nm a] -> E a -> E a
+mkLam ns e = foldr (\n -> Lam (loc n) n) e ns
+
+hasY :: E a -> Bool
+hasY = cata a where
+    a (ResVarF _ Y)           = True
+    a (TupF _ es)             = or es
+    a (EAppF _ e e')          = e || e'
+    a (LamF _ _ e)            = e
+    a DfnF{}                  = error "Not supported yet."
+    a (LetF _ b e)            = e || snd b
+    a (GuardedF _ p b)        = b || p
+    a (ImplicitF _ e)         = e
+    a (ParenF _ e)            = e
+    a (ArrF _ es)             = or es
+    a (AnchorF _ es)          = or es
+    a (OptionValF _ (Just e)) = e
+    a (CondF _ p e e')        = p || e || e'
+    a _                       = False
+
+replaceXY :: (a -> Nm a) -- ^ @x@
+          -> (a -> Nm a) -- ^ @y@
+          -> E a
+          -> E a
+replaceXY nX nY = cata a where
+    a (ResVarF l X) = Var l (nX l)
+    a (ResVarF l Y) = Var l (nY l)
+    a x             = embed x
+
+replaceX :: (a -> Nm a) -> E a -> E a
+replaceX n = cata a where
+    a (ResVarF l X) = Var l (n l)
+    a x             = embed x
+
+renameD :: D a -> RenameM (D a)
+renameD (FunDecl n ns e) = FunDecl n [] <$> rE (mkLam ns e)
+renameD d                = pure d
+
+renameProgram :: Program a -> RenameM (Program a)
+renameProgram (Program ds e) = Program <$> traverse renameD ds <*> rE e
+
+{-# INLINABLE rE #-}
+rE :: (HasRenames s, MonadState s m) => E a -> m (E a)
+rE (EApp l e e')   = EApp l <$> rE e <*> rE e'
+rE (Tup l es)      = Tup l <$> traverse rE es
+rE (Var l n)       = Var l <$> replaceVar n
+rE (Lam l n e)     = do
+    (n', modR) <- withName n
+    Lam l n' <$> withRenames modR (rE e)
+rE (Dfn l e) | {-# SCC "hasY" #-} hasY e = do
+    x@(Nm nX uX _) <- dummyName l "x"
+    y@(Nm nY uY _) <- dummyName l "y"
+    Lam l x . Lam l y <$> rE ({-# SCC "replaceXY" #-} replaceXY (Nm nX uX) (Nm nY uY) e)
+                  | otherwise = do
+    x@(Nm n u _) <- dummyName l "x"
+    Lam l x <$> rE ({-# SCC "replaceX" #-} replaceX (Nm n u) e)
+rE (Guarded l p e) = Guarded l <$> rE p <*> rE e
+rE (Implicit l e) = Implicit l <$> rE e
+rE ResVar{} = error "Bare reserved variable."
+rE (Let l (n, eϵ) e') = do
+    eϵ' <- rE eϵ
+    (n', modR) <- withName n
+    Let l (n', eϵ') <$> withRenames modR (rE e')
+rE (Paren _ e) = rE e
+rE (Arr l es) = Arr l <$> traverse rE es
+rE (Anchor l es) = Anchor l <$> traverse rE es
+rE (OptionVal l e) = OptionVal l <$> traverse rE e
+rE (Cond l p e e') = Cond l <$> rE p <*> rE e <*> rE e'
+rE e = pure e
diff --git a/src/Ty.hs b/src/Ty.hs
new file mode 100644
--- /dev/null
+++ b/src/Ty.hs
@@ -0,0 +1,485 @@
+{-# LANGUAGE FlexibleContexts  #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Ty ( Subst
+          , runTyM
+          , tyP
+          , match
+          , aT
+          -- * For debugging
+          , tyOf
+          ) where
+
+import           A
+import           Control.Exception          (Exception, throw)
+import           Control.Monad              (zipWithM)
+import           Control.Monad.Except       (liftEither, throwError)
+import           Control.Monad.State.Strict (StateT, gets, modify, runState, runStateT)
+import           Data.Bifunctor             (first, second)
+import           Data.Foldable              (traverse_)
+import           Data.Functor               (void, ($>))
+import qualified Data.IntMap                as IM
+import qualified Data.IntSet                as IS
+import           Data.Semigroup             ((<>))
+import qualified Data.Set                   as S
+import qualified Data.Text                  as T
+import           Data.Typeable              (Typeable)
+import qualified Data.Vector                as V
+import           Nm
+import           Prettyprinter              (Pretty (..), squotes, (<+>))
+import           Ty.Const
+import           U
+
+data Err a = UF a T T
+           | Doesn'tSatisfy a T C
+           | IllScoped a (Nm a)
+           | Ambiguous T (E ())
+           | IllScopedTyVar (TyName ())
+           | MF T T
+           | Occ a T T
+
+instance Pretty a => Pretty (Err a) where
+    pretty (UF l ty ty')           = pretty l <+> "could not unify type" <+> squotes (pretty ty) <+> "with" <+> squotes (pretty ty')
+    pretty (Doesn'tSatisfy l ty c) = pretty l <+> squotes (pretty ty) <+> "is not a member of class" <+> pretty c
+    pretty (IllScoped l n)         = pretty l <+> squotes (pretty n) <+> "is not in scope."
+    pretty (Ambiguous ty e)        = "type" <+> squotes (pretty ty) <+> "of" <+> squotes (pretty e) <+> "is ambiguous"
+    pretty (IllScopedTyVar n)      = "Type variable" <+> squotes (pretty n) <+> "is not in scope."
+    pretty (MF t t')               = "Failed to match" <+> squotes (pretty t) <+> "against type" <+> squotes (pretty t')
+    pretty (Occ l t t')            = pretty l <+> "occurs check failed when unifying type" <+> squotes (pretty t) <+> "with type" <+> squotes (pretty t')
+
+instance Pretty a => Show (Err a) where show=show.pretty
+
+instance (Typeable a, Pretty a) => Exception (Err a) where
+
+data TyState a = TyState { maxU      :: !Int
+                         , classVars :: IM.IntMap (S.Set (C, a))
+                         , varEnv    :: IM.IntMap T
+                         }
+
+mapMaxU :: (Int -> Int) -> TyState a -> TyState a
+mapMaxU f (TyState u c v) = TyState (f u) c v
+
+setMaxU :: Int -> TyState a -> TyState a
+setMaxU i (TyState _ c v) = TyState i c v
+
+mapCV :: (IM.IntMap (S.Set (C, a)) -> IM.IntMap (S.Set (C, a))) -> TyState a -> TyState a
+mapCV f (TyState u cvs v) = TyState u (f cvs) v
+
+addVarEnv :: Int -> T -> TyState a -> TyState a
+addVarEnv i ty (TyState u cvs v) = TyState u cvs (IM.insert i ty v)
+
+type TyM a = StateT (TyState a) (Either (Err a))
+
+runTyM :: Int -> TyM a b -> Either (Err a) (b, Int)
+runTyM i = fmap (second maxU) . flip runStateT (TyState i IM.empty IM.empty)
+
+type Subst = IM.IntMap T
+
+aT :: Subst -> T -> T
+aT um ty'@(TyVar (Nm _ (U i) _)) =
+    case IM.lookup i um of
+        Just ty@TyVar{} -> aT (IM.delete i um) ty -- prevent cyclic lookups
+        Just ty@Rho{}   -> aT (IM.delete i um) ty
+        Just ty         -> aT um ty
+        Nothing         -> ty'
+aT um (Rho n@(Nm _ (U i) _) rs) =
+    case IM.lookup i um of
+        Just ty@Rho{}   -> aT (IM.delete i um) ty
+        Just ty@TyVar{} -> aT (IM.delete i um) ty
+        Just ty         -> aT um ty
+        Nothing         -> Rho n (fmap (aT um) rs)
+aT _ ty'@TyB{} = ty'
+aT um (TyApp ty ty') = TyApp (aT um ty) (aT um ty')
+aT um (TyArr ty ty') = TyArr (aT um ty) (aT um ty')
+aT um (TyTup tys)    = TyTup (aT um <$> tys)
+
+mguPrep :: l -> Subst -> T -> T -> Either (Err l) Subst
+mguPrep l s t0 t1 =
+    let t0' = aT s t0; t1' = aT s t1 in mgu l s t0' t1'
+
+match :: T -> T -> Subst
+match t t' = either (throw :: Err () -> Subst) id (maM t t')
+
+maM :: T -> T -> Either (Err l) Subst
+maM (TyB b) (TyB b') | b == b' = Right mempty
+maM (TyVar n) (TyVar n') | n == n' = Right mempty
+maM (TyVar (Nm _ (U i) _)) t = Right (IM.singleton i t)
+maM (TyArr t0 t1) (TyArr t0' t1') = (<>) <$> maM t0 t0' <*> maM t1' t1 -- TODO: I think <> is right
+maM (TyTup ts) (TyTup ts')        = fmap mconcat (zipWithM maM ts ts')
+maM (Rho n _) (Rho n' _) | n == n' = Right mempty
+maM (Rho n rs) t@(Rho _ rs') | IM.keysSet rs' `IS.isSubsetOf` IM.keysSet rs = IM.insert (unU$unique n) t . mconcat <$> traverse (uncurry maM) (IM.elems (IM.intersectionWith (,) rs rs'))
+maM (Rho n rs) t@(TyTup ts) | length ts >= fst (IM.findMax rs) = IM.insert (unU$unique n) t . mconcat <$> traverse (uncurry maM) [ (ts!!(i-1),tϵ) | (i,tϵ) <- IM.toList rs ]
+maM t t'                              = Left $ MF t t'
+
+occ :: T -> IS.IntSet
+occ (TyVar (Nm _ (U i) _))  = IS.singleton i
+occ TyB{}                   = IS.empty
+occ (TyTup ts)              = foldMap occ ts
+occ (TyApp t t')            = occ t <> occ t'
+occ (TyArr t t')            = occ t <> occ t'
+occ (Rho (Nm _ (U i) _) rs) = IS.insert i (foldMap occ (IM.elems rs))
+
+mgu :: l -> Subst -> T -> T -> Either (Err l) Subst
+mgu _ s (TyB b) (TyB b') | b == b' = Right s
+mgu _ s (TyVar n) (TyVar n') | n == n' = Right s
+mgu l s t t'@(TyVar (Nm _ (U k) _)) | k `IS.notMember` occ t = Right $ IM.insert k t s
+                                      | otherwise = Left $ Occ l t' t
+mgu l s t@(TyVar (Nm _ (U k) _)) t' | k `IS.notMember` occ t' = Right $ IM.insert k t' s
+                                      | otherwise = Left $ Occ l t t'
+mgu l s (TyArr t0 t1) (TyArr t0' t1')  = do {s0 <- mguPrep l s t0 t0'; mguPrep l s0 t1 t1'}
+mgu l s (TyApp t0 t1) (TyApp t0' t1')  = do {s0 <- mguPrep l s t0 t0'; mguPrep l s0 t1 t1'}
+mgu l s (TyTup ts) (TyTup ts') | length ts == length ts' = zS (mguPrep l) s ts ts'
+mgu l s (Rho n rs) t'@(TyTup ts) | length ts >= fst (IM.findMax rs) = tS_ (\sϵ (i, tϵ) -> IM.insert (unU$unique n) t' <$> mguPrep l sϵ (ts!!(i-1)) tϵ) s (IM.toList rs)
+mgu l s t@TyTup{} t'@Rho{} = mgu l s t' t
+mgu l s (Rho n rs) (Rho n' rs') = do
+    rss <- tS_ (\sϵ (t0,t1) -> mguPrep l sϵ t0 t1) s $ IM.elems $ IM.intersectionWith (,) rs rs'
+    pure (IM.insert (unU$unique n) (Rho n' (rs <> rs')) rss)
+mgu l _ t t' = Left $ UF l t t'
+
+tS_ :: Monad m => (Subst -> b -> m Subst) -> Subst -> [b] -> m Subst
+tS_ _ s []     = pure s
+tS_ f s (t:ts) = do {next <- f s t; tS_ f next ts}
+
+zS _ s [] _           = pure s
+zS _ s _ []           = pure s
+zS op s (x:xs) (y:ys) = do {next <- op s x y; zS op next xs ys}
+
+substInt :: IM.IntMap T -> Int -> Maybe T
+substInt tys k =
+    case IM.lookup k tys of
+        Just ty'@TyVar{}     -> Just $ aT (IM.delete k tys) ty'
+        Just (TyApp ty0 ty1) -> Just $ let tys'=IM.delete k tys in TyApp (aT tys' ty0) (aT tys' ty1)
+        Just (TyArr ty0 ty1) -> Just $ let tys'=IM.delete k tys in TyArr (aT tys' ty0) (aT tys' ty1)
+        Just (TyTup tysϵ)    -> Just $ let tys'=IM.delete k tys in TyTup (aT tys' <$> tysϵ)
+        Just ty'             -> Just ty'
+        Nothing              -> Nothing
+
+freshName :: T.Text -> TyM a (Nm ())
+freshName n = do
+    st <- gets maxU
+    Nm n (U $ st+1) ()
+        <$ modify (mapMaxU (+1))
+
+addC :: Ord a => Nm b -> (C, a) -> IM.IntMap (S.Set (C, a)) -> IM.IntMap (S.Set (C, a))
+addC (Nm _ (U i) _) c = IM.alter (Just . go) i where
+    go Nothing   = S.singleton c
+    go (Just cs) = S.insert c cs
+
+tyArr :: T -> T -> T
+tyArr = TyArr
+
+var :: Nm () -> T
+var = TyVar
+
+liftCloneTy :: T -> TyM b T
+liftCloneTy ty = do
+    i <- gets maxU
+    let (ty', (j, iMaps)) = cloneTy i ty
+    -- FIXME: clone/propagate constraints
+    ty' <$ modify (setMaxU j)
+
+cloneTy :: Int -> T -> (T, (Int, IM.IntMap U))
+cloneTy i ty = flip runState (i, IM.empty) $ cloneTyM ty
+    where cloneTyM (TyVar (Nm n (U j) l')) = do
+                st <- gets snd
+                case IM.lookup j st of
+                    Just k -> pure (TyVar (Nm n k l'))
+                    Nothing -> do
+                        k <- gets fst
+                        let j' = U$k+1
+                        TyVar (Nm n j' l') <$ modify (\(u, s) -> (u+1, IM.insert j j' s))
+          cloneTyM (TyArr tyϵ ty')               = TyArr <$> cloneTyM tyϵ <*> cloneTyM ty'
+          cloneTyM (TyApp tyϵ ty')               = TyApp <$> cloneTyM tyϵ <*> cloneTyM ty'
+          cloneTyM (TyTup tys)                   = TyTup <$> traverse cloneTyM tys
+          cloneTyM tyϵ@TyB{}                     = pure tyϵ
+
+checkType :: Ord a => T -> (C, a) -> TyM a ()
+checkType TyVar{} _                             = pure ()
+checkType (TyB TyStr) (IsSemigroup, _)          = pure ()
+checkType (TyB TyInteger) (IsSemigroup, _)      = pure ()
+checkType (TyB TyFloat) (IsSemigroup, _)        = pure ()
+checkType (TyB TyInteger) (IsNum, _)            = pure ()
+checkType (TyB TyFloat) (IsNum, _)              = pure ()
+checkType (TyB TyInteger) (IsEq, _)             = pure ()
+checkType (TyB TyFloat) (IsEq, _)               = pure ()
+checkType (TyB TyBool) (IsEq, _)                = pure ()
+checkType (TyB TyStr) (IsEq, _)                 = pure ()
+checkType (TyTup tys) (c@IsEq, l)               = traverse_ (`checkType` (c, l)) tys
+checkType (Rho _ rs) (c@IsEq, l)                = traverse_ (`checkType` (c, l)) (IM.elems rs)
+checkType (TyApp (TyB TyVec) ty) (c@IsEq, l)    = checkType ty (c, l)
+checkType (TyApp (TyB TyOption) ty) (c@IsEq, l) = checkType ty (c, l)
+checkType (TyB TyInteger) (IsParse, _)          = pure ()
+checkType (TyB TyFloat) (IsParse, _)            = pure ()
+checkType (TyB TyFloat) (IsOrd, _)              = pure ()
+checkType (TyB TyInteger) (IsOrd, _)            = pure ()
+checkType (TyB TyStr) (IsOrd, _)                = pure ()
+checkType (TyB TyVec) (Functor, _)              = pure ()
+checkType (TyB TyStream) (Functor, _)           = pure ()
+checkType (TyB TyOption) (Functor, _)           = pure ()
+checkType (TyB TyStream) (Witherable, _)        = pure ()
+checkType (TyB TyVec) (Foldable, _)             = pure ()
+checkType (TyB TyStream) (Foldable, _)          = pure ()
+checkType (TyB TyStr) (IsPrintf, _)             = pure ()
+checkType (TyB TyFloat) (IsPrintf, _)           = pure ()
+checkType (TyB TyInteger) (IsPrintf, _)         = pure ()
+checkType (TyB TyBool) (IsPrintf, _)            = pure ()
+checkType (TyTup tys) (c@IsPrintf, l)           = traverse_ (`checkType` (c, l)) tys
+checkType (Rho _ rs) (c@IsPrintf, l)            = traverse_ (`checkType` (c, l)) (IM.elems rs)
+checkType ty (c, l)                             = throwError $ Doesn'tSatisfy l ty c
+
+checkClass :: Ord a
+           => IM.IntMap T -- ^ Unification result
+           -> Int
+           -> S.Set (C, a)
+           -> TyM a ()
+checkClass tys i cs = {-# SCC "checkClass" #-}
+    case substInt tys i of
+        Just ty -> traverse_ (checkType ty) (S.toList cs)
+        Nothing -> pure ()
+
+lookupVar :: Nm a -> TyM a T
+lookupVar n@(Nm _ (U i) l) = do
+    st <- gets varEnv
+    case IM.lookup i st of
+        Just ty -> pure ty -- liftCloneTy ty
+        Nothing -> throwError $ IllScoped l n
+
+tyOf :: Ord a => E a -> TyM a T
+tyOf = fmap eLoc . tyE
+
+tyDS :: Ord a => Subst -> D a -> TyM a (D T, Subst)
+tyDS s (SetFS bs) = pure (SetFS bs, s)
+tyDS s FlushDecl  = pure (FlushDecl, s)
+tyDS s (FunDecl n@(Nm _ (U i) _) [] e) = do
+    (e', s') <- tyES s e
+    let t=eLoc e'
+    modify (addVarEnv i t) $> (FunDecl (n$>t) [] e', s')
+tyDS _ FunDecl{}   = error "Internal error. Should have been desugared by now."
+
+isAmbiguous :: T -> Bool
+isAmbiguous TyVar{}        = True
+isAmbiguous (TyArr ty ty') = isAmbiguous ty || isAmbiguous ty'
+isAmbiguous (TyApp ty ty') = isAmbiguous ty || isAmbiguous ty'
+isAmbiguous (TyTup tys)    = any isAmbiguous tys
+isAmbiguous TyB{}          = False
+isAmbiguous Rho{}          = True
+
+checkAmb :: E T -> TyM a ()
+checkAmb e@(BB ty _) | isAmbiguous ty = throwError $ Ambiguous ty (void e)
+checkAmb TB{} = pure () -- don't fail on ternary builtins, we don't need it anyway... better error messages
+checkAmb e@(UB ty _) | isAmbiguous ty = throwError $ Ambiguous ty (void e)
+checkAmb (Implicit _ e') = checkAmb e'
+checkAmb (Guarded _ p e') = checkAmb p *> checkAmb e'
+checkAmb (EApp _ e' e'') = checkAmb e' *> checkAmb e'' -- more precise errors
+checkAmb (Tup _ es) = traverse_ checkAmb es
+checkAmb e@(Arr ty _) | isAmbiguous ty = throwError $ Ambiguous ty (void e)
+checkAmb e@(Var ty _) | isAmbiguous ty = throwError $ Ambiguous ty (void e)
+checkAmb (Let _ bs e) = traverse_ checkAmb [e, snd bs]
+checkAmb (Lam _ _ e) = checkAmb e -- I think
+checkAmb _ = pure ()
+
+tS _ s []     = pure ([], s)
+tS f s (t:ts) = do {(x, next) <- f s t; first (x:) <$> tS f next ts}
+
+tyP :: Ord a => Program a -> TyM a (Program T)
+tyP (Program ds e) = do
+    (ds', s0) <- tS tyDS mempty ds
+    (e', s1) <- tyES s0 e
+    toCheck <- gets (IM.toList . classVars)
+    traverse_ (uncurry (checkClass s1)) toCheck
+    let res = {-# SCC "aT" #-} fmap (aT s1) (Program ds' e')
+    checkAmb (expr res) $> res
+
+tyNumOp :: Ord a => a -> TyM a T
+tyNumOp l = do
+    m <- freshName "m"
+    modify (mapCV (addC m (IsNum, l)))
+    let m' = var m
+    pure $ tyArr m' (tyArr m' m')
+
+tySemiOp :: Ord a => a -> TyM a T
+tySemiOp l = do
+    m <- freshName "m"
+    modify (mapCV (addC m (IsSemigroup, l)))
+    let m' = var m
+    pure $ tyArr m' (tyArr m' m')
+
+tyOrd :: Ord a => a -> TyM a T
+tyOrd l = do
+    a <- freshName "a"
+    modify (mapCV (addC a (IsOrd, l)))
+    let a' = var a
+    pure $ tyArr a' (tyArr a' tyB)
+
+tyEq :: Ord a => a -> TyM a T
+tyEq l = do
+    a <- freshName "a"
+    modify (mapCV (addC a (IsEq, l)))
+    let a' = var a
+    pure $ tyArr a' (tyArr a' tyB)
+
+-- min/max
+tyM :: Ord a => a -> TyM a T
+tyM l = do
+    a <- freshName "a"
+    modify (mapCV (addC a (IsOrd, l)))
+    let a' = var a
+    pure $ tyArr a' (tyArr a' a')
+
+desugar :: a
+desugar = error "Should have been de-sugared in an earlier stage!"
+
+tyE :: Ord a => E a -> TyM a (E T)
+tyE e = do
+    (e', s) <- tyES mempty e
+    cvs <- gets (IM.toList . classVars)
+    traverse_ (uncurry (checkClass s)) cvs
+    pure (fmap (aT s) e')
+
+tyES :: Ord a => Subst -> E a -> TyM a (E T, Subst)
+tyES s (BLit _ b)         = pure (BLit tyB b, s)
+tyES s (ILit _ i)         = pure (ILit tyI i, s)
+tyES s (FLit _ f)         = pure (FLit tyF f, s)
+tyES s (StrLit _ str)     = pure (StrLit tyStr str, s)
+tyES s (RegexLit _ rr)    = pure (RegexLit tyR rr, s)
+tyES s (Column _ i)       = pure (Column (tyStream tyStr) i, s)
+tyES s (IParseCol _ i)    = pure (IParseCol (tyStream tyI) i, s)
+tyES s (FParseCol _ i)    = pure (FParseCol (tyStream tyF) i, s)
+tyES s (Field _ i)        = pure (Field tyStr i, s)
+tyES s LastField{}        = pure (LastField tyStr, s)
+tyES s AllField{}         = pure (AllField tyStr, s)
+tyES s AllColumn{}        = pure (AllColumn (tyStream tyStr), s)
+tyES s (NB _ Ix)    = pure (NB tyI Ix, s)
+tyES s (NB _ Fp)    = pure (NB tyStr Fp, s)
+tyES s (NB _ Nf)    = pure (NB tyI Nf, s)
+tyES s (BB l Plus)  = do {t <- tySemiOp l; pure (BB t Plus, s)}
+tyES s (BB l Minus) = do {t <- tyNumOp l; pure (BB t Minus, s)}
+tyES s (BB l Times) = do {t <- tyNumOp l; pure (BB t Times, s)}
+tyES s (BB l Exp)   = do {t <- tyNumOp l; pure (BB t Exp, s)}
+tyES s (BB l Gt)    = do {t <- tyOrd l; pure (BB t Gt, s)}
+tyES s (BB l Lt)    = do {t <- tyOrd l; pure (BB t Lt, s)}
+tyES s (BB l Geq)   = do {t <- tyOrd l; pure (BB t Geq, s)}
+tyES s (BB l Leq)   = do {t <- tyOrd l; pure (BB t Leq, s)}
+tyES s (BB l Eq)    = do {t <- tyEq l; pure (BB t Eq, s)}
+tyES s (BB l Neq)   = do {t <- tyEq l; pure (BB t Neq, s)}
+tyES s (BB l Min)   = do {t <- tyM l; pure (BB t Min, s)}
+tyES s (BB l Max)   = do {t <- tyM l; pure (BB t Max, s)}
+tyES s (BB _ Split) = pure (BB (tyArr tyStr (tyArr tyR (tyV tyStr))) Split, s)
+tyES s (BB _ Splitc) = pure (BB (tyArr tyStr (tyArr tyStr (tyV tyStr))) Splitc, s)
+tyES s (BB _ Matches) = pure (BB (tyArr tyStr (tyArr tyR tyB)) Matches, s)
+tyES s (BB _ NotMatches) = pure (BB (tyArr tyStr (tyArr tyR tyB)) NotMatches, s)
+tyES s (UB _ Tally) = pure (UB (tyArr tyStr tyI) Tally, s)
+tyES s (BB _ Div) = pure (BB (tyArr tyF (tyArr tyF tyF)) Div, s)
+tyES s (UB _ Not) = pure (UB (tyArr tyB tyB) Not, s)
+tyES s (BB _ And) = pure (BB (tyArr tyB (tyArr tyB tyB)) And, s)
+tyES s (BB _ Or) = pure (BB (tyArr tyB (tyArr tyB tyB)) Or, s)
+tyES s (BB _ Match) = pure (BB (tyArr tyStr (tyArr tyR (tyOpt $ TyTup [tyI, tyI]))) Match, s)
+tyES s (TB _ Substr) = pure (TB (tyArr tyStr (tyArr tyI (tyArr tyI tyStr))) Substr, s)
+tyES s (UB _ IParse) = pure (UB (tyArr tyStr tyI) IParse, s)
+tyES s (UB _ FParse) = pure (UB (tyArr tyStr tyF) FParse, s)
+tyES s (UB _ Floor) = pure (UB (tyArr tyF tyI) Floor, s)
+tyES s (UB _ Ceiling) = pure (UB (tyArr tyF tyI) Ceiling, s)
+tyES s (UB _ TallyList) = do {a <- var <$> freshName "a"; pure (UB (tyArr a tyI) TallyList, s)}
+tyES s (UB l Negate) = do {a <- freshName "a"; modify (mapCV (addC a (IsNum, l))); let a'=var a in pure (UB (tyArr a' a') Negate, s)}
+tyES s (UB _ Some) = do {a <- var <$> freshName "a"; pure (UB (tyArr a (tyOpt a)) Some, s)}
+tyES s (NB _ None) = do {a <- freshName "a"; pure (NB (tyOpt (var a)) None, s)}
+tyES s (ParseCol l i) = do {a <- freshName "a"; modify (mapCV (addC a (IsParse, l))); pure (ParseCol (tyStream (var a)) i, s)}
+tyES s (UB l Parse) = do {a <- freshName "a"; modify (mapCV (addC a (IsParse, l))); pure (UB (tyArr tyStr (var a)) Parse, s)}
+tyES s (BB l Sprintf) = do {a <- freshName "a"; modify (mapCV (addC a (IsPrintf, l))); pure (BB (tyArr tyStr (tyArr (var a) tyStr)) Sprintf, s)}
+tyES s (BB l DedupOn) = do {a <- var <$> freshName "a"; b <- freshName "b"; modify (mapCV (addC b (IsEq, l))); let b'=var b in pure (BB (tyArr (tyArr a b') (tyArr (tyStream a) (tyStream b'))) DedupOn, s)}
+tyES s (UB _ (At i)) = do {a <- var <$> freshName "a"; pure (UB (tyArr (tyV a) a) (At i), s)}
+tyES s (UB l Dedup) = do {a <- freshName "a"; modify (mapCV (addC a (IsEq, l))); let sA=tyStream (var a) in pure (UB (tyArr sA sA) Dedup, s)}
+tyES s (UB _ Const) = do {a <- var <$> freshName "a"; b <- var <$> freshName "b"; pure (UB (tyArr a (tyArr b a)) Const, s)}
+tyES s (UB l CatMaybes) = do {a <- freshName "a"; f <- freshName "f"; modify (mapCV (addC f (Witherable, l))); let a'=var a; f'=var f in pure (UB (tyArr (TyApp f' (tyOpt a')) (TyApp f' a')) CatMaybes, s)}
+tyES s (BB l Filter) = do {a <- freshName "a"; f <- freshName "f"; modify (mapCV (addC f (Witherable, l))); let a'=var a; f'=var f; w=TyApp f' a' in pure (BB (tyArr (tyArr a' tyB) (tyArr w w)) Filter, s)}
+tyES s (UB _ (Select i)) = do
+    ρ <- freshName "ρ"; a <- var <$> freshName "a"
+    pure (UB (tyArr (Rho ρ (IM.singleton i a)) a) (Select i), s)
+tyES s (BB l MapMaybe) = do
+    a <- var <$> freshName "a"; b <- var <$> freshName "b"
+    f <- freshName "f"
+    modify (mapCV (addC f (Witherable, l)))
+    let f'=var f
+    pure (BB (tyArr (tyArr a (tyOpt b)) (tyArr (TyApp f' a) (TyApp f' b))) MapMaybe, s)
+tyES s (BB l Map) = do
+    a <- var <$> freshName "a"; b <- var <$> freshName "b"
+    f <- freshName "f"
+    let f'=var f
+    modify (mapCV (addC f (Functor, l)))
+    pure (BB (tyArr (tyArr a b) (tyArr (TyApp f' a) (TyApp f' b))) Map, s)
+tyES s (TB l Fold) = do
+    a <- var <$> freshName "a"; b <- var <$> freshName "b"
+    f <- freshName "f"
+    let f'=var f
+    modify (mapCV (addC f (Foldable, l)))
+    pure (TB (tyArr (tyArr b (tyArr a b)) (tyArr b (tyArr (TyApp f' a) b))) Fold, s)
+tyES s (BB l Fold1) = do
+    a <- var <$> freshName "a"
+    f <- freshName "f"
+    let f'=var f
+    modify (mapCV (addC f (Foldable, l)))
+    pure (BB (tyArr (tyArr a (tyArr a a)) (tyArr (TyApp f' a) a)) Fold1, s)
+tyES s (TB _ Captures) = pure (TB (tyArr tyStr (tyArr tyI (tyArr tyR (tyOpt tyStr)))) Captures, s)
+tyES s (BB _ Prior) = do
+    a <- var <$> freshName "a"; b <- var <$> freshName "b"
+    pure (BB (tyArr (tyArr a (tyArr a b)) (tyArr (tyStream a) (tyStream b))) Prior, s)
+tyES s (TB _ ZipW) = do
+    a <- var <$> freshName "a"; b <- var <$> freshName "b"; c <- var <$> freshName "c"
+    pure (TB (tyArr (tyArr a (tyArr b c)) (tyArr (tyStream a) (tyArr (tyStream b) (tyStream c)))) ZipW, s)
+tyES s (TB _ Scan) = do
+    a <- var <$> freshName "a"; b <- var <$> freshName "b"
+    pure (TB (tyArr (tyArr b (tyArr a b)) (tyArr b (tyArr (tyStream a) (tyStream b)))) Scan, s)
+tyES s (TB _ Option) = do
+    a <- var <$> freshName "a"; b <- var <$> freshName "b"
+    pure (TB (tyArr b (tyArr (tyArr a b) (tyArr (tyOpt a) b))) Option, s)
+tyES s (TB _ AllCaptures) = pure (TB (tyArr tyStr (tyArr tyI (tyArr tyR (tyV tyStr)))) AllCaptures, s)
+tyES s (Implicit _ e) = do {(e',s') <- tyES s e; pure (Implicit (tyStream (eLoc e')) e', s')}
+tyES s (Guarded l e se) = do
+    (se', s0) <- tyES s se
+    (e', s1) <- tyES s0 e
+    s2 <- liftEither $ mguPrep l s1 tyB (eLoc e')
+    pure (Guarded (tyStream (eLoc se')) e' se', s2)
+tyES s (EApp l e0 e1)     = do
+    a <- freshName "a"; b <- freshName "b"
+    let a'=var a; b'=var b; e0Ty=tyArr a' b'
+    (e0', s0) <- tyES s e0
+    (e1', s1) <- tyES s0 e1
+    s2 <- liftEither $ mguPrep l s1 (eLoc e0') e0Ty
+    s3 <- liftEither $ mguPrep l s2 (eLoc e1') a'
+    pure (EApp b' e0' e1', s3)
+tyES s (Lam _ n@(Nm _ (U i) _) e) = do
+    a <- var <$> freshName "a"
+    modify (addVarEnv i a)
+    (e', s') <- tyES s e
+    pure (Lam (tyArr a (eLoc e')) (n$>a) e', s')
+tyES s (Let _ (n@(Nm _ (U i) _), eϵ) e) = do
+    (eϵ', s0) <- tyES s eϵ
+    let bTy=eLoc eϵ'
+    modify (addVarEnv i bTy)
+    (e', s1) <- tyES s0 e
+    pure (Let (eLoc e') (n$>bTy, eϵ') e', s1)
+tyES s (Tup _ es) = do {(es', s') <- tS tyES s es; pure (Tup (TyTup (fmap eLoc es')) es', s')}
+tyES s (Var _ n) = do {t <- lookupVar n; pure (Var t (n$>t), s)}
+tyES s (OptionVal _ (Just e)) = do {(e', s') <- tyES s e; pure (OptionVal (tyOpt (eLoc e')) (Just e'), s')}
+tyES s (OptionVal _ Nothing) = do {a <- var <$> freshName "a"; pure (OptionVal (tyOpt a) Nothing, s)}
+tyES s (Arr l v) | V.null v = do
+    a <- var <$> freshName "a"
+    pure (Arr (tyV a) V.empty, s)
+                 | otherwise = do
+    (v',s0) <- tS tyES s (V.toList v)
+    let vt=fmap eLoc v'
+    s1 <- liftEither $ zS (mguPrep l) s0 vt (tail vt)
+    pure (Arr (head vt) (V.fromList v'), s1)
+tyES s (Cond l p e0 e1) = do
+    (p', s0) <- tyES s p
+    (e0', s1) <- tyES s0 e0
+    (e1', s2) <- tyES s1 e1
+    let t=eLoc e0'
+    s3 <- liftEither $ mguPrep l s2 tyB (eLoc p')
+    s4 <- liftEither $ mguPrep l s3 t (eLoc e1')
+    pure (Cond t p' e0' e1', s4)
+tyES s (Anchor l es) = do
+    (es', s') <- tS (\sϵ e -> do {(e',s0) <- tyES sϵ e; a <- var <$> freshName "a"; s1 <- liftEither $ mguPrep l s0 (tyStream a) (eLoc e'); pure (e', s1)}) s es
+    pure (Anchor (TyB TyUnit) es', s')
+tyES _ RC{} = error "Regex should not be compiled at this stage."
+tyES _ Dfn{} = desugar; tyES _ ResVar{} = desugar; tyES _ Paren{} = desugar
diff --git a/src/Ty/Const.hs b/src/Ty/Const.hs
new file mode 100644
--- /dev/null
+++ b/src/Ty/Const.hs
@@ -0,0 +1,18 @@
+module Ty.Const ( tyStream, tyOpt, tyV
+                , tyStr, tyR, tyI, tyF, tyB
+                ) where
+
+import           A
+
+-- | argument assumed to have kind 'Star'
+tyStream :: T -> T
+tyStream = TyApp (TyB TyStream)
+
+tyB, tyI, tyF, tyStr, tyR :: T
+tyB=TyB TyBool; tyI=TyB TyInteger; tyF=TyB TyFloat; tyStr=TyB TyStr; tyR=TyB TyR
+
+tyOpt :: T -> T
+tyOpt = TyApp (TyB TyOption)
+
+tyV :: T -> T
+tyV = TyApp (TyB TyVec)
diff --git a/src/U.hs b/src/U.hs
new file mode 100644
--- /dev/null
+++ b/src/U.hs
@@ -0,0 +1,3 @@
+module U ( U (..) ) where
+
+newtype U = U { unU :: Int } deriving (Eq, Ord)
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -2,19 +2,20 @@
 
 module Main (main) where
 
-import           Control.Monad          ((<=<))
-import qualified Data.ByteString        as BS
-import qualified Data.ByteString.Lazy   as BSL
-import           Data.Foldable          (toList)
-import           Data.Functor           (void)
-import           Jacinda.AST
-import           Jacinda.File
-import           Jacinda.Parser
-import           Jacinda.Parser.Rewrite
+import           A
+import           Control.Monad    ((<=<))
+import qualified Data.ByteString  as BS
+import           Data.Foldable    (toList)
+import           Data.Functor     (void)
+import qualified Data.Text        as T
+import qualified Data.Text.IO     as TIO
+import           File
 import           Jacinda.Regex
-import           Jacinda.Ty.Const
+import           Parser
+import           Parser.Rw
 import           Test.Tasty
 import           Test.Tasty.HUnit
+import           Ty.Const
 
 main :: IO ()
 main = defaultMain $
@@ -30,14 +31,14 @@
         , splitWhitespaceT "      55 ./src/Jacinda/File.hs" ["55", "./src/Jacinda/File.hs"]
         , splitWhitespaceT "" []
         , splitWhitespaceT "5" ["5"]
-        , testCase "type of" (tyOfT sumBytes (TyB Star TyInteger))
-        , testCase "type of" (tyOfT krakRegex (TyApp Star (TyB (KArr Star Star) TyStream) (TyB Star TyStr))) -- stream of str
-        , testCase "type of" (tyOfT krakCol (TyApp Star (TyB (KArr Star Star) TyStream) (TyB Star TyStr))) -- stream of str
+        , testCase "type of" (tyOfT sumBytes (TyB TyInteger))
+        , testCase "type of" (tyOfT krakRegex (TyApp (TyB TyStream) (TyB TyStr))) -- stream of str
+        , testCase "type of" (tyOfT krakCol (TyApp (TyB TyStream) (TyB TyStr))) -- stream of str
         , testCase "type of (zip)" (tyOfT ",(-) $3:i $6:i" (tyStream tyI))
         , testCase "type of (filter)" (tyOfT "(>110) #. #\"$0" (tyStream tyI))
         , testCase "typechecks dfn" (tyOfT "[(+)|0 x] $1:i" tyI)
-        , testCase "count bytes" (tyOfT "(+)|0 #\"$0" tyI)
-        , testCase "running count (lines)" (tyOfT "(+)^0 [:1\"$0" (tyStream tyI))
+        , testCase "count bytes" (tyOfT "(+)|0 #¨$0" tyI)
+        , testCase "running count (lines)" (tyOfT "(+)^0 [:1¨$0" (tyStream tyI))
         , testCase "type of (tally)" (tyOfT "#'hello world'" tyI)
         , testCase "typechecks dfn" (tyFile "test/examples/ab.jac")
         , testCase "parses parens" (tyFile "examples/lib.jac")
@@ -48,7 +49,7 @@
         , testCase "if...then...else" (evalTo "if #t then 0 else 1" "0")
         ]
 
-evalTo :: BSL.ByteString -> String -> Assertion
+evalTo :: T.Text -> String -> Assertion
 evalTo bsl expected =
     let actual = show (exprEval bsl)
         in actual @?= expected
@@ -57,11 +58,11 @@
 pAst =
     EApp ()
         (EApp ()
-            (BBuiltin () Gt)
+            (BB () Gt)
             (EApp ()
-                (UBuiltin () Tally)
+                (UB () Tally)
                 (AllField ())))
-        (IntLit () 72)
+        (ILit () 72)
 
 splitWhitespaceT :: BS.ByteString -> [BS.ByteString] -> TestTree
 splitWhitespaceT haystack expected =
@@ -69,13 +70,13 @@
         toList (splitBy defaultRurePtr haystack) @?= expected
 
 -- example: ls -l | ja '(+)|0 $5:i'
-sumBytes :: BSL.ByteString
+sumBytes :: T.Text
 sumBytes = "(+)|0 $5:i"
 
-krakRegex :: BSL.ByteString
+krakRegex :: T.Text
 krakRegex = "{% /Krakatoa/}{`0}"
 
-krakCol :: BSL.ByteString
+krakCol :: T.Text
 krakCol = "{`3:i > 4}{`0}"
 
 sumBytesAST :: E ()
@@ -83,28 +84,28 @@
     EApp ()
         (EApp ()
             (EApp ()
-                (TBuiltin () Fold)
-                (BBuiltin () Plus))
-            (IntLit () 0))
+                (TB () Fold)
+                (BB () Plus))
+            (ILit () 0))
             (IParseCol () 5)
 
 tyFile :: FilePath -> Assertion
-tyFile = tcIO [] <=< BSL.readFile
+tyFile = tcIO [] <=< TIO.readFile
 
-tyOfT :: BSL.ByteString -> T K -> Assertion
+tyOfT :: T.Text -> T -> Assertion
 tyOfT src expected =
     tySrc src @?= expected
 
-parseTo :: BSL.ByteString -> E () -> Assertion
+parseTo :: T.Text -> E () -> Assertion
 parseTo src e =
-    case rewriteProgram . snd <$> parse src of
+    case rwP . snd <$> parse src of
         Left err     -> assertFailure (show err)
         Right actual -> void (expr actual) @?= e
 
 parseFile :: FilePath -> TestTree
-parseFile fp = testCase ("Parses " ++ fp) $ parseNoErr =<< BSL.readFile fp
+parseFile fp = testCase ("Parses " ++ fp) $ parseNoErr =<< TIO.readFile fp
 
-parseNoErr :: BSL.ByteString -> Assertion
+parseNoErr :: T.Text -> Assertion
 parseNoErr src =
     case parse src of
         Left err -> assertFailure (show err)
diff --git a/test/examples/evenOdd.jac b/test/examples/evenOdd.jac
--- a/test/examples/evenOdd.jac
+++ b/test/examples/evenOdd.jac
@@ -7,14 +7,11 @@
 fn isOdd() :=
   (~ /(1|3|5|7|9)$/);
 
-fn divTen() :=
-  (~/0$/);
-
 let
   val iStream := $0
   val even := count (isEven #. iStream)
   val odd := count (isOdd #. iStream)
-  val tens := count (divTen #. iStream)
-  {. FIXME: if different lengths, all fucked (and... wrong :(
+  val tens := (+)|> {%/0$/}{1}
+  {. val tens := count {%/0$/}{`0}
   val total := odd + even
-in (total . odd . even) end
+in (total . even . odd . tens) end
diff --git a/test/examples/polymorphic.jac b/test/examples/polymorphic.jac
new file mode 100644
--- /dev/null
+++ b/test/examples/polymorphic.jac
@@ -0,0 +1,2 @@
+let val sum := [(+)|0 x]
+  in sum {|sum (let val l := splitc `0 ' ' in l:i end)} end
diff --git a/test/examples/sillyPragmas.jac b/test/examples/sillyPragmas.jac
new file mode 100644
--- /dev/null
+++ b/test/examples/sillyPragmas.jac
@@ -0,0 +1,2 @@
+{. run ja run test/examples/sillyPragmas.jac -i src/A.hs
+[x+', '+y]|'' .?{|`0 ~* 1 /\{-#\s*LANGUAGE\s*([^\s]*)\s*#-\}/}
diff --git a/test/examples/sillyPragmas2.jac b/test/examples/sillyPragmas2.jac
new file mode 100644
--- /dev/null
+++ b/test/examples/sillyPragmas2.jac
@@ -0,0 +1,1 @@
+[x+', '+y]|'' [x ~* 1 /\{-#\s*LANGUAGE\s*([^\s]*)\s*#-\}/]:?$0
