diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,21 @@
+# 0.3.1.0
+
+  * Performance improvements
+  * Bug fix, dfns are renamed properly
+  * Add `-.` negate function
+  * Work with shell shebangs
+  * Implement `=` for boolean
+  * Add `captures` as a builtin
+  * Add `|>`, fold without seed
+  * Allow `fn...` declarations with no arguments and no parentheses
+  * Add conditionals
+  * Fix bug in normalizing `Some` and `None`
+  * Fix bug in indexing + filter
+  * Fix bug in polymorphic functions used at multiple sites
+  * Change parsing/rewrite so `f a b + c` parses as `(f a b) + c` rather than `f a (b + c)`
+  * Fix bug in parser rewriting in `@include`d files
+  * Include searches current directory
+
 # 0.3.0.0
 
   * Fix renaming bug that was inveigling folds with lambdas
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -84,9 +84,6 @@
 Intentionally missing features:
 
   * No loops
-  * No conditionals
-
-The latter in particular I may add if necessary/requested
 
 # Further Advantages
 
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/hsLibversion.jac b/examples/hsLibversion.jac
new file mode 100644
--- /dev/null
+++ b/examples/hsLibversion.jac
@@ -0,0 +1,1 @@
+.?{| `0 ~* 1 /(^[A-Za-z][A-Za-z0-9\-]*\-\d+(\.\d+)*)\-[0-9a-f]{64}$/}
diff --git a/examples/hsLibversion2.jac b/examples/hsLibversion2.jac
new file mode 100644
--- /dev/null
+++ b/examples/hsLibversion2.jac
@@ -0,0 +1,1 @@
+[x ~* 1 /(^[A-Za-z][A-Za-z0-9\-]*\-\d+(\.\d+)*)\-[0-9a-f]{64}$/]:? $0
diff --git a/examples/hsLibversionMac.jac b/examples/hsLibversionMac.jac
new file mode 100644
--- /dev/null
+++ b/examples/hsLibversionMac.jac
@@ -0,0 +1,1 @@
+~..?{| `0 ~* 1 /(^[A-Za-z][A-Za-z0-9\-]*\-\d+(\.\d+)*)\-[0-9a-zA-Z]{22}$/}
diff --git a/examples/hsLibversionMac2.jac b/examples/hsLibversionMac2.jac
new file mode 100644
--- /dev/null
+++ b/examples/hsLibversionMac2.jac
@@ -0,0 +1,1 @@
+~.[x ~* 1 /(^[A-Za-z][A-Za-z0-9\-]*\-\d+(\.\d+)*)\-[0-9a-zA-Z]{22}$/]:? $0
diff --git a/examples/hsLibversionPerf.jac b/examples/hsLibversionPerf.jac
new file mode 100644
--- /dev/null
+++ b/examples/hsLibversionPerf.jac
@@ -0,0 +1,1 @@
+.?{% /[0-9a-f]{64}$/}{`0 ~* 1 /(^[A-Za-z][A-Za-z0-9\-]*\-\d+(\.\d+)*)\-[0-9a-f]{64}$/}
diff --git a/examples/libversion.jac b/examples/libversion.jac
new file mode 100644
--- /dev/null
+++ b/examples/libversion.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/longline.jac b/examples/longline.jac
new file mode 100644
--- /dev/null
+++ b/examples/longline.jac
@@ -0,0 +1,1 @@
+{#`0>180}{sprintf'%s:%i %s' (fp.ix.`0)}
diff --git a/examples/nl.jac b/examples/nl.jac
new file mode 100644
--- /dev/null
+++ b/examples/nl.jac
@@ -0,0 +1,14 @@
+{. FIXME: figure out @include'lib/string.jac'??
+@include'lib/string.jac'
+
+fn step(acc, line) :=
+  if empty line
+    then (acc->1 . '')
+    else (acc->1 + 1 . line);
+
+fn process(x) :=
+  if !empty (x->2)
+    then sprintf '    %i\t%s' x
+    else '';
+
+process"step^(0 . '') $0
diff --git a/examples/path.jac b/examples/path.jac
--- a/examples/path.jac
+++ b/examples/path.jac
@@ -1,5 +1,5 @@
 {. echo $PATH | ja run examples/path.jac
 fn path(x) :=
-  ([x+'\n'+y])|'' (splitc x ':');
+  ([x+'\n'+y]) |> (splitc x ':');
 
 path"$0
diff --git a/examples/pathx.jac b/examples/pathx.jac
new file mode 100644
--- /dev/null
+++ b/examples/pathx.jac
@@ -0,0 +1,5 @@
+#!/usr/bin/env -S ja run
+fn path(x) :=
+  ([x+'\n'+y]) |> (splitc x ':');
+
+path"$0
diff --git a/examples/sed.jac b/examples/sed.jac
new file mode 100644
--- /dev/null
+++ b/examples/sed.jac
@@ -0,0 +1,14 @@
+{. program equiv to s/^(type|interface)/export \1/
+
+@include'prelude/fn.jac'
+
+fn replaceAtWith(line, ixes) :=
+  let
+    val pre := take (ixes->1) line
+    val at := drop (ixes->1) line {. in our case
+  in pre + 'export ' + at end;
+
+fn replaceAt(line) :=
+  option line (replaceAtWith line) (match line /^(type|interface)/);
+
+replaceAt"$0
diff --git a/examples/sedreplace.jac b/examples/sedreplace.jac
new file mode 100644
--- /dev/null
+++ b/examples/sedreplace.jac
@@ -0,0 +1,11 @@
+{. s/^var/export const/ in sed
+
+@include'prelude/fn.jac'
+
+fn insert(line, str, ixes) :=
+  take (ixes->1) line + str + drop (ixes->2) line;
+
+fn replace(re, str, line) :=
+  option line (insert line str) (match line re);
+
+(replace /^var/ 'export const')"$0
diff --git a/examples/span2.jac b/examples/span2.jac
--- a/examples/span2.jac
+++ b/examples/span2.jac
@@ -1,9 +1,6 @@
 :set fs:=/\|/;
 
 fn printSpan(str) :=
-  let
-    val p := match str /\^+/
-    val str := (sprintf '%i-%i')"p
-  in str end;
+  (sprintf '%i-%i')"(match str /\^+/);
 
 printSpan:?{% /\|/}{`2}
diff --git a/examples/stripUnprintable.jac b/examples/stripUnprintable.jac
new file mode 100644
--- /dev/null
+++ b/examples/stripUnprintable.jac
@@ -0,0 +1,3 @@
+@include'lib/string.jac'
+
+(replace /[^[:print:]]/ '')"$0
diff --git a/examples/tags.jac b/examples/tags.jac
--- a/examples/tags.jac
+++ b/examples/tags.jac
@@ -3,7 +3,7 @@
 
 fn processStr(s) :=
   let
-    val line := split s /[ \(]+/
+    val line := split s /[ \(:]+/
     val outLine := sprintf '%s\t%s\t%s' (line.2 . fp . mkEx s)
   in outLine end;
 
diff --git a/examples/trailingLine.jac b/examples/trailingLine.jac
new file mode 100644
--- /dev/null
+++ b/examples/trailingLine.jac
@@ -0,0 +1,1 @@
+if [y]|>$0 = '' then fp else ''
diff --git a/examples/trimwhitespace.jac b/examples/trimwhitespace.jac
new file mode 100644
--- /dev/null
+++ b/examples/trimwhitespace.jac
@@ -0,0 +1,3 @@
+@include'lib/sed.jac'
+
+(replace1 /\s+$/ '')"$0
diff --git a/examples/uniq.jac b/examples/uniq.jac
new file mode 100644
--- /dev/null
+++ b/examples/uniq.jac
@@ -0,0 +1,6 @@
+fn step(acc, this) :=
+  if this = acc->1
+    then (this . None)
+    else (this . Some this);
+
+(->2):?step^(''.None) $0
diff --git a/examples/year.jac b/examples/year.jac
new file mode 100644
--- /dev/null
+++ b/examples/year.jac
@@ -0,0 +1,2 @@
+{. extract year from column of dates in YYYY-MM-DD format
+.? {|`1 ~* 1 /(\d{4})-\d{2}-\d{2}/}
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:            0.3.0.0
+version:            0.3.1.0
 license:            AGPL-3
 license-file:       COPYING
 maintainer:         vamchale@gmail.com
@@ -12,6 +12,10 @@
 
 category:           Language, Interpreters, Text, Data
 build-type:         Simple
+data-files:
+    lib/*.jac
+    prelude/*.jac
+
 extra-source-files:
     CHANGELOG.md
     README.md
@@ -20,10 +24,6 @@
     test/examples/*.jac
     examples/*.jac
 
-data-files:
-    lib/*.jac
-    prelude/*.jac
-
 source-repository head
     type:     git
     location: https://github.com/vmchale/jacinda
@@ -56,8 +56,8 @@
         Data.List.Ext
         Data.Vector.Ext
         Paths_jacinda
-    autogen-modules:  Paths_jacinda
 
+    autogen-modules:  Paths_jacinda
     default-language: Haskell2010
     ghc-options:      -Wall -O2
     build-depends:
@@ -101,7 +101,7 @@
     other-modules:    Paths_jacinda
     autogen-modules:  Paths_jacinda
     default-language: Haskell2010
-    ghc-options:      -Wall -rtsopts -with-rtsopts=-A200k
+    ghc-options:      -Wall -rtsopts "-with-rtsopts=-A200k -k32k"
     build-depends:
         base,
         jacinda-lib,
@@ -149,13 +149,12 @@
     if impl(ghc >=8.10)
         ghc-options: -Wunused-packages
 
-
 benchmark jacinda-bench
     type:             exitcode-stdio-1.0
     main-is:          Bench.hs
     hs-source-dirs:   bench
     default-language: Haskell2010
-    ghc-options:      -Wall
+    ghc-options:      -Wall -rtsopts "-with-rtsopts=-A200k -k32k"
     build-depends:
         base,
         criterion,
diff --git a/lib/sed.jac b/lib/sed.jac
new file mode 100644
--- /dev/null
+++ b/lib/sed.jac
@@ -0,0 +1,8 @@
+@include'prelude/fn.jac'
+
+{. example: (replace /^var/ 'export const')"$0
+fn replace1(re, str, line) :=
+  let 
+    val insert := \line. \str. \ixes.
+      take (ixes->1) line + str + drop (ixes->2) line
+  in option line (insert line str) (match line re) end;
diff --git a/lib/string.jac b/lib/string.jac
--- a/lib/string.jac
+++ b/lib/string.jac
@@ -1,5 +1,8 @@
-fn intercalate(b, strs) :=
-  [x+b+y]|'' strs;
+fn intercalate(b) :=
+  ([x+b+y] |>);
 
 fn replace(re, t, str) :=
-  [x+t+y]|'' (split str re);
+  [x+t+y] |> (split str re);
+
+fn empty(str) :=
+  #str = 0;
diff --git a/man/ja.1 b/man/ja.1
--- a/man/ja.1
+++ b/man/ja.1
@@ -1,5 +1,19 @@
-.\" Automatically generated by Pandoc 2.17.0.1
+.\" Automatically generated by Pandoc 2.17.1.1
 .\"
+.\" Define V font for inline verbatim, using C font in formats
+.\" that render this, and otherwise B font.
+.ie "\f[CB]x\f[]"x" \{\
+. ftr V B
+. ftr VI BI
+. ftr VB B
+. ftr VBI BI
+.\}
+.el \{\
+. ftr V CR
+. ftr VI CI
+. ftr VB CB
+. ftr VBI CBI
+.\}
 .TH "ja (1)" "" "" "" ""
 .hy
 .SH NAME
@@ -9,7 +23,7 @@
 .PP
 ja run src.jac -i data.txt
 .PP
-cat FILE1 FILE2 | ja \[aq]#\[lq]$0\[cq]
+cat FILE1 FILE2 | ja \[aq]#\[dq]$0\[cq]
 .PP
 ja tc script.jac
 .PP
@@ -55,10 +69,13 @@
 \f[B]|\f[R] Ternary operator: fold
 Foldable f :=> (b -> a -> b) -> b -> f a -> b
 .TP
+\f[B]|>\f[R] Fold without seed
+Foldable f :=> (a -> a -> a) -> f a -> a
+.TP
 \f[B]\[ha]\f[R] Ternary operator: scan
 (b -> a -> b) -> b -> Stream a -> Stream b
 .TP
-\f[B]\[lq]\f[R] Binary operator: map
+\f[B]\[dq]\f[R] Binary operator: map
 Functor f :=> a -> b -> f a -> f b
 .TP
 \f[B][:\f[R] Unary operator: const
@@ -104,6 +121,8 @@
 \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
@@ -114,6 +133,12 @@
 Str -> Regex -> Option (Int .
 Int)
 .TP
+\f[B]\[ti]*\f[R] Match, returning nth capture group
+Str -> Int -> Regex -> Option Str
+.TP
+\f[B]captures\f[R] Return all captures
+Str -> Int -> Regex -> List Str
+.TP
 \f[B]:?\f[R] mapMaybe
 Witherable f :=> (a -> Option b) -> f a -> f b
 .TP
@@ -149,6 +174,40 @@
 \f[B]{.\f[R] Line comment
 .PP
 \f[B]\[at]include\[aq]/path/file.jac\[cq]\f[R] File include
+.SH INFLUENTIAL ENVIRONMENT VARIABLES
+.PP
+\f[V]JAC_PATH\f[R] - colon-separated list of directories to search
+.SH EXAMPLES
+.TP
+[#x>72] #. $0
+Print lines longer than 72 bytes
+.TP
+{#\[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
+: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
+Sum first column
+.TP
+(+)|0 [:1\[dq]$0
+Count lines
+.TP
+[y]|0 {|ix}
+Count lines
+.TP
+(+)|0 [#x+1]\[dq]$0
+Count bytes (+1 for newlines)
+.TP
+(+)|0 {|#\[ga]0+1}
+Count bytes
+.TP
+{|sprintf \[aq]%i: %s\[aq] (ix.\[ga]0)}
+Display with line numbers
 .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
@@ -11,7 +11,7 @@
 {. rounds down on .5
 fn round(x) := |. (x+0.5);
 
-fn itostring() :=
+fn itostring :=
   sprintf '%i';
 
 fn any(p, xs) :=
@@ -24,3 +24,9 @@
 
 fn fromMaybe(a, x) :=
   option a [x] x;
+
+fn last :=
+  ([y]|>);
+
+fn head :=
+  ([[:x]|>);
diff --git a/src/Data/List/Ext.hs b/src/Data/List/Ext.hs
--- a/src/Data/List/Ext.hs
+++ b/src/Data/List/Ext.hs
@@ -1,5 +1,5 @@
 module Data.List.Ext ( imap
-                     , ifilter
+                     , ifilter'
                      , prior
                      ) where
 
@@ -9,5 +9,5 @@
 imap :: (Int -> a -> b) -> [a] -> [b]
 imap f xs = fmap (uncurry f) (zip [1..] xs)
 
-ifilter :: (Int -> a -> Bool) -> [a] -> [a]
-ifilter p xs = snd <$> filter (uncurry p) (zip [1..] xs)
+ifilter' :: (Int -> a -> Bool) -> [a] -> [(Int, a)]
+ifilter' p xs = filter (uncurry p) (zip [1..] xs)
diff --git a/src/Jacinda/AST.hs b/src/Jacinda/AST.hs
--- a/src/Jacinda/AST.hs
+++ b/src/Jacinda/AST.hs
@@ -48,6 +48,10 @@
        | 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
@@ -117,6 +121,7 @@
          | Some
          | Dedup
          | CatMaybes
+         | Negate
          deriving (Eq)
 
 instance Pretty BUn where
@@ -133,6 +138,7 @@
     pretty Some       = "Some"
     pretty Dedup      = "~."
     pretty CatMaybes  = ".?"
+    pretty Negate     = "-."
 
 -- ternary
 data BTer = ZipW
@@ -141,15 +147,17 @@
           | 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 ZipW        = ","
+    pretty Fold        = "|"
+    pretty Scan        = "^"
+    pretty Substr      = "substr"
+    pretty Option      = "option"
+    pretty Captures    = "~*"
+    pretty AllCaptures = "captures"
 
 -- builtin
 data BBin = Plus
@@ -176,6 +184,7 @@
           | Sprintf
           | Match
           | MapMaybe
+          | Fold1
           -- TODO: floor functions, sqrt, sin, cos, exp. (power)
           deriving (Eq)
 
@@ -204,6 +213,7 @@
     pretty Sprintf    = "sprintf"
     pretty Match      = "match"
     pretty MapMaybe   = ":?"
+    pretty Fold1      = "|>"
 
 data DfnVar = X | Y deriving (Eq)
 
@@ -250,6 +260,7 @@
          | 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 }
          -- TODO: regex literal
          deriving (Functor, Generic)
          -- TODO: side effects: allow since it's strict?
@@ -287,6 +298,7 @@
             | 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)
@@ -298,58 +310,60 @@
     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 AllField{}                                                = "`0"
-    pretty (Field _ i)                                               = "`" <> pretty i
-    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 _ 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{}                                           = error "Nonsense."
-    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 (Column _ i)                                                 = "$" <> pretty i
+    pretty AllColumn{}                                                  = "$0"
+    pretty (IParseCol _ i)                                              = "$" <> pretty i <> ":i"
+    pretty (FParseCol _ i)                                              = "$" <> pretty i <> ":f"
+    pretty AllField{}                                                   = "`0"
+    pretty (Field _ i)                                                  = "`" <> pretty i
+    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{}                                              = error "Nonsense."
+    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
diff --git a/src/Jacinda/Backend/Normalize.hs b/src/Jacinda/Backend/Normalize.hs
--- a/src/Jacinda/Backend/Normalize.hs
+++ b/src/Jacinda/Backend/Normalize.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE OverloadedStrings #-}
 
--- TODO: test this module?
 module Jacinda.Backend.Normalize ( compileR
                                  , compileIn
                                  , eClosed
@@ -14,11 +13,13 @@
                                  , parseAsF
                                  , the
                                  , asTup
+                                 , EvalError (..)
                                  -- * Monad
                                  , runEvalM
                                  , eNorm
                                  ) where
 
+import           Control.Exception          (Exception, throw)
 import           Control.Monad.State.Strict (State, evalState, gets, modify, runState)
 import           Control.Recursion          (cata, embed)
 import           Data.Bifunctor             (second)
@@ -38,6 +39,12 @@
 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
 
@@ -82,6 +89,7 @@
 compileR = cata a where -- TODO: combine with eNorm pass?
     a (RegexLitF _ rr) = RegexCompiled (compileDefault rr)
     a x                = embed x
+    -- TODO: is cata performant enough?
 
 compileIn :: Program a -> Program a
 compileIn (Program ds e) = Program (compileD <$> ds) (compileR e)
@@ -183,6 +191,7 @@
 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
@@ -270,7 +279,7 @@
     pure $ case (eI, eI') of
         (IntLit _ i, IntLit _ j) -> i `seq` j `seq` IntLit tyI (i*j)
         _                        -> EApp ty (EApp ty' op eI) eI'
-eNorm (EApp ty (EApp ty' op@(BBuiltin (TyArr _ (TyB _ TyFloat) _) Plus) e) e') = do
+eNorm (EApp ty (EApp ty' op@(BBuiltin (TyArr _ (TyB _ TyFloat) _) Plus) e) e') = do -- TODO: don't match on type, do that below
     eI <- eNorm e
     eI' <- eNorm e'
     pure $ case (eI, eI') of
@@ -383,6 +392,18 @@
     pure $ case (eI, eI') of
         (StrLit _ i, StrLit _ j) -> BoolLit tyBool (i /= j)
         _                        -> EApp ty (EApp ty' op eI) eI'
+eNorm (EApp ty (EApp ty' op@(BBuiltin (TyArr _ (TyB _ TyBool) _) Eq) e) e') = do
+    eI <- eNorm e
+    eI' <- eNorm e'
+    pure $ case (eI, eI') of
+        (BoolLit _ b, BoolLit _ b') -> BoolLit tyBool (b == b')
+        _                           -> EApp ty (EApp ty' op eI) eI'
+eNorm (EApp ty (EApp ty' op@(BBuiltin (TyArr _ (TyB _ TyBool) _) Neq) e) e') = do
+    eI <- eNorm e
+    eI' <- eNorm e'
+    pure $ case (eI, eI') of
+        (BoolLit _ b, BoolLit _ b') -> BoolLit tyBool (b /= b')
+        _                           -> EApp ty (EApp ty' op eI) eI'
 eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ And) e) e') = do
     eI <- eNorm e
     eI' <- eNorm e'
@@ -408,6 +429,12 @@
     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
@@ -469,6 +496,13 @@
     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
@@ -501,6 +535,12 @@
     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
@@ -522,13 +562,17 @@
         -- (Arr _ es, Arr _ es') -> Arr undefined <$> V.zipWithM (applyOp f') es 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
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ Map) x) y) = EApp ty0 <$> (EApp ty1 op <$> eNorm x) <*> eNorm y
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ MapMaybe) x) y) = EApp ty0 <$> (EApp ty1 op <$> eNorm x) <*> eNorm y
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ Prior) x) y) = EApp ty0 <$> (EApp ty1 op <$> eNorm x) <*> eNorm y
-eNorm (EApp ty0 (EApp ty1 op@(BBuiltin _ Filter) x) y) = EApp ty0 <$> (EApp ty1 op <$> eNorm x) <*> eNorm y
--- FIXME: this will almost surely run into trouble; if the above pattern matches
--- are not complete it will bottom!
+-- 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: specialize types after inlining hm
 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
+        _               -> pure $ Cond ty p' e0 e1 -- don't eval further, might bottom?
diff --git a/src/Jacinda/Backend/TreeWalk.hs b/src/Jacinda/Backend/TreeWalk.hs
--- a/src/Jacinda/Backend/TreeWalk.hs
+++ b/src/Jacinda/Backend/TreeWalk.hs
@@ -14,6 +14,7 @@
 import           Data.Maybe                (mapMaybe)
 import           Data.Semigroup            ((<>))
 import qualified Data.Vector               as V
+import           Debug.Trace               (traceShow, traceShowId)
 import           Jacinda.AST
 import           Jacinda.Backend.Normalize
 import           Jacinda.Backend.Printf
@@ -25,7 +26,6 @@
                  | UnevalFun
                  | TupOfStreams -- ^ Reject a tuple of streams
                  | BadCtx
-                 | IndexOutOfBounds Int
                  deriving (Show)
 
 type FileBS = BS.ByteString
@@ -37,44 +37,39 @@
     Just x  -> x
     Nothing -> throw $ IndexOutOfBounds ix
 
-noRes :: a
-noRes = error "Internal error: did not normalize to appropriate type."
+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 _            = noRes
+asInt e            = noRes e "Int"
 
 asBool :: E a -> Bool
 asBool (BoolLit _ b) = b
-asBool _             = noRes
+asBool e             = noRes e "Bool"
 
 asStr :: E a -> BS.ByteString
 asStr (StrLit _ str) = str
-asStr _              = noRes
+asStr e              = noRes e "Str"
 
 asFloat :: E a -> Double
 asFloat (FloatLit _ f) = f
-asFloat _              = noRes
+asFloat e              = noRes e "Float"
 
 asRegex :: E a -> RurePtr
 asRegex (RegexCompiled re) = re
-asRegex _                  = noRes
+asRegex e                  = noRes e "Regex"
 
 asArr :: E a -> V.Vector (E a)
 asArr (Arr _ es) = es
-asArr _          = noRes
+asArr e          = noRes e "List"
 
 asOpt :: E a -> Maybe (E a)
 asOpt (OptionVal _ e) = e
-asOpt _               = noRes
-
--- 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
+asOpt e               = noRes e "Option"
 
 -- TODO: do I want to interleave state w/ eNorm or w/e
 
@@ -101,11 +96,21 @@
     go (EApp ty op@BBuiltin{} e) = EApp ty op (go e)
     go (NBuiltin _ Ix) = mkI (fromIntegral ix)
     go (NBuiltin _ Fp) = mkStr fp
+    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 (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
@@ -121,14 +126,14 @@
         in case (eI, eI') of
             (RegexCompiled reϵ, StrLit _ strϵ) -> BoolLit tyBool (isMatch' reϵ strϵ)
             (StrLit _ strϵ, RegexCompiled reϵ) -> BoolLit tyBool (isMatch' reϵ strϵ)
-            _                                  -> noRes
+            _                                  -> noRes eI "Regex or Str"
     go (EApp _ (EApp _ (BBuiltin _ NotMatches) e) e') =
         let eI = go e
             eI' = go e'
         in case (eI, eI') of
             (RegexCompiled reϵ, StrLit _ strϵ) -> BoolLit tyBool (not $ isMatch' reϵ strϵ)
             (StrLit _ strϵ, RegexCompiled reϵ) -> BoolLit tyBool (not $ isMatch' reϵ strϵ)
-            _                                  -> noRes
+            _                                  -> noRes eI "Regex or Str"
     go (EApp _ (EApp _ (BBuiltin _ Match) e) e') =
         let eI = asRegex (go e)
             eI' = asStr (go e')
@@ -138,6 +143,11 @@
             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')
@@ -207,6 +217,14 @@
         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')
@@ -267,12 +285,12 @@
         let eI = go e
             in case eI of
                 (Arr _ es) -> go (es V.! (i-1))
-                _          -> noRes
+                _          -> noRes eI "List"
     go (EApp _ (UBuiltin _ (Select i)) e) =
         let eI = go e
             in case eI of
                 (Tup _ es) -> go (es !! (i-1))
-                _          -> noRes
+                _          -> noRes eI "Tuple"
     go (EApp _ (EApp _ (BBuiltin _ Sprintf) e) e') =
         let eI = asStr (go e)
             eI' = go e'
@@ -298,8 +316,26 @@
         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
 
+
+-- 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)
@@ -341,7 +377,7 @@
         e' = compileR e
     -- FIXME: compile e too?
     -- TODO: normalize before stream
-        in imap (\ix line -> eEval (mkCtx fp re ix line) e') . ifilter (\ix line -> asBool (eEval (mkCtx fp re ix line) pe'))
+        in fmap (uncurry (\ix line -> eEval (mkCtx fp re ix line) e')) . ifilter' (\ix line -> asBool (eEval (mkCtx fp re ix line) pe'))
 ir fp re (EApp _ (EApp _ (BBuiltin _ Map) op) stream) = let op' = compileR (withFp fp op) in fmap (applyUn op') . ir fp re stream
 ir fp re (EApp _ (EApp _ (BBuiltin _ Filter) op) stream) =
     let op' = compileR (withFp fp op)
@@ -381,6 +417,17 @@
             -> E (T K)
 foldWithCtx fp re op seed streamExpr = foldl' (applyOp op) seed . ir fp re streamExpr
 
+fold1 :: FileBS
+      -> RurePtr
+      -> E (T K)
+      -> E (T K)
+      -> [BS.ByteString]
+      -> E (T K)
+fold1 fp re op streamExpr bs =
+    case ir fp re streamExpr bs of
+        e:es -> foldl' (applyOp op) e es
+        _    -> throw EmptyFold
+
 runJac :: FileBS
        -> RurePtr -- ^ Record separator
        -> Int
@@ -391,6 +438,7 @@
 -- evaluate something that has a fold nested in it
 eWith :: FileBS -> RurePtr -> E (T K) -> [BS.ByteString] -> E (T K)
 eWith fp re (EApp _ (EApp _ (EApp _ (TBuiltin (TyArr _ _ (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _))) Fold) op) seed) stream) = foldWithCtx fp re op seed stream
+eWith fp re (EApp _ (EApp _ (BBuiltin (TyArr _ _ (TyArr _ (TyApp _ (TyB _ TyStream) _) _)) Fold1) op) stream)                          = fold1 fp re op stream
 eWith fp re (EApp ty e0 e1)                                                                                                            = \bs -> eClosed undefined (EApp ty (eWith fp re e0 bs) (eWith fp re e1 bs))
 eWith _ _ e@BBuiltin{}                                                                                                                 = const e
 eWith _ _ e@UBuiltin{}                                                                                                                 = const e
@@ -401,6 +449,8 @@
 eWith _ _ e@BoolLit{}                                                                                                                  = const e
 eWith fp re (Tup ty es)                                                                                                                = \bs -> Tup ty ((\e -> eWith fp re e bs) <$> es)
 eWith fp re (OptionVal ty e)                                                                                                           = \bs -> OptionVal ty ((\eϵ -> eWith fp re eϵ bs) <$> e)
+eWith fp re (Cond ty p e e')                                                                                                           = \bs -> eClosed undefined (Cond ty (eWith fp re p bs) (eWith fp re e bs) (eWith fp re e' bs))
+eWith fp _ (NBuiltin _ Fp)                                                                                                             = const (mkStr fp)
 -- TODO: rewrite tuple-of-folds as fold-of-tuples ... "compile" to E (T K) -> E (T K)
 -- OR "compile" to [(Int, E (T K)] -> ...
 
diff --git a/src/Jacinda/File.hs b/src/Jacinda/File.hs
--- a/src/Jacinda/File.hs
+++ b/src/Jacinda/File.hs
@@ -37,7 +37,7 @@
     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 ++ 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
diff --git a/src/Jacinda/Include.hs b/src/Jacinda/Include.hs
--- a/src/Jacinda/Include.hs
+++ b/src/Jacinda/Include.hs
@@ -7,7 +7,7 @@
 import           Data.List.Split    (splitWhen)
 import           Data.Maybe         (listToMaybe)
 import           Paths_jacinda      (getDataDir)
-import           System.Directory   (doesFileExist)
+import           System.Directory   (doesFileExist, getCurrentDirectory)
 import           System.Environment (lookupEnv)
 import           System.FilePath    ((</>))
 
@@ -19,7 +19,8 @@
 defaultIncludes = do
     path <- jacPath
     d <- getDataDir
-    pure $ (d:) . (++path)
+    dot <- getCurrentDirectory
+    pure $ (dot:) . (d:) . (++path)
 
 -- | Parsed @JAC_PATH@
 jacPath :: IO [FilePath]
diff --git a/src/Jacinda/Lexer.x b/src/Jacinda/Lexer.x
--- a/src/Jacinda/Lexer.x
+++ b/src/Jacinda/Lexer.x
@@ -67,6 +67,7 @@
         $white+                  ;
 
         "{.".*                   ;
+        "#!".*                   ; -- shebang
 
         ":="                     { mkSym DefEq }
         "≔"                      { mkSym DefEq }
@@ -84,6 +85,7 @@
         "|"                      { mkSym FoldTok }
         \"                       { mkSym Quot }
         "^"                      { mkSym Caret }
+        "|>"                     { mkSym Fold1Tok }
 
         "="                      { mkSym EqTok }
         "!="                     { mkSym NeqTok }
@@ -116,6 +118,7 @@
         ".?"                     { mkSym CatMaybesTok }
         ":?"                     { mkSym MapMaybeTok }
         "~*"                     { mkSym CapTok }
+        "-."                     { mkSym NegTok }
 
         in                       { mkKw KwIn }
         let                      { mkKw KwLet }
@@ -124,6 +127,9 @@
         :set                     { mkKw KwSet }
         fn                       { mkKw KwFn }
         "@include"               { mkKw KwInclude }
+        if                       { mkKw KwIf }
+        then                     { mkKw KwThen }
+        else                     { mkKw KwElse }
 
         fs                       { mkRes VarFs }
         ix                       { mkRes VarIx }
@@ -142,6 +148,7 @@
         floor                    { mkBuiltin BuiltinFloor }
         ceil                     { mkBuiltin BuiltinCeil }
         match                    { mkBuiltin BuiltinMatch }
+        captures                 { mkBuiltin BuiltinCaptures }
         Some                     { mkBuiltin BuiltinSome }
         None                     { mkBuiltin BuiltinNone }
         fp                       { mkBuiltin BuiltinFp }
@@ -236,6 +243,7 @@
          | MinusTok
          | PercentTok
          | FoldTok
+         | Fold1Tok
          | Quot
          | TimesTok
          | DefEq
@@ -276,12 +284,14 @@
          | CatMaybesTok
          | MapMaybeTok
          | CapTok
+         | NegTok
 
 instance Pretty Sym where
     pretty PlusTok       = "+"
     pretty MinusTok      = "-"
     pretty PercentTok    = "%"
     pretty FoldTok       = "|"
+    pretty Fold1Tok      = "|>"
     pretty TimesTok      = "*"
     pretty DefEq         = ":="
     pretty Colon         = ":"
@@ -322,6 +332,7 @@
     pretty CatMaybesTok  = ".?"
     pretty MapMaybeTok   = ":?"
     pretty CapTok        = "~*"
+    pretty NegTok        = "-."
 
 data Keyword = KwLet
              | KwIn
@@ -330,6 +341,9 @@
              | KwSet
              | KwFn
              | KwInclude
+             | KwIf
+             | KwThen
+             | KwElse
 
 -- | Reserved/special variables
 data Var = VarX
@@ -358,6 +372,9 @@
     pretty KwSet     = ":set"
     pretty KwFn      = "fn"
     pretty KwInclude = "@include"
+    pretty KwIf      = "if"
+    pretty KwThen    = "then"
+    pretty KwElse    = "else"
 
 data Builtin = BuiltinIParse
              | BuiltinFParse
@@ -369,24 +386,26 @@
              | 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 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 }
diff --git a/src/Jacinda/Parser.y b/src/Jacinda/Parser.y
--- a/src/Jacinda/Parser.y
+++ b/src/Jacinda/Parser.y
@@ -70,11 +70,13 @@
 
     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 }
@@ -105,6 +107,9 @@
     set { TokKeyword $$ KwSet }
     fn { TokKeyword $$ KwFn }
     include { TokKeyword $$ KwInclude }
+    if { TokKeyword $$ KwIf }
+    then { TokKeyword $$ KwThen }
+    else { TokKeyword $$ KwElse }
 
     x { TokResVar $$ VarX }
     y { TokResVar $$ VarY }
@@ -126,6 +131,7 @@
     some { TokBuiltin $$ BuiltinSome }
     none { TokBuiltin $$ BuiltinNone }
     fp { TokBuiltin $$ BuiltinFp }
+    captures { TokBuiltin $$ BuiltinCaptures }
 
     iParse { TokBuiltin $$ BuiltinIParse }
     fParse { TokBuiltin $$ BuiltinFParse }
@@ -175,6 +181,7 @@
      | or { Or }
      | backslashdot { Prior }
      | filter { Filter }
+     | fold1 { Fold1 }
 
 Bind :: { (Name AlexPosn, E AlexPosn) }
      : val name defEq E { ($2, $4) }
@@ -187,6 +194,7 @@
 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) }
@@ -257,6 +265,7 @@
   | 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 }
@@ -264,6 +273,7 @@
   | 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 }
@@ -274,6 +284,7 @@
   | 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 }
 
 {
 
diff --git a/src/Jacinda/Parser/Rewrite.hs b/src/Jacinda/Parser/Rewrite.hs
--- a/src/Jacinda/Parser/Rewrite.hs
+++ b/src/Jacinda/Parser/Rewrite.hs
@@ -34,8 +34,15 @@
     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
@@ -47,4 +54,6 @@
     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 _ 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
@@ -9,6 +9,7 @@
                      , compileDefault
                      , substr
                      , findCapture
+                     , captures'
                      ) where
 
 import           Control.Exception        (Exception, throwIO)
@@ -37,9 +38,16 @@
 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]
+captures' re haystack@(BS.BS fp _) ix = unsafeDupablePerformIO $ fmap go <$> captures re haystack ix
+    where go (RureMatch s e) =
+            let e' = fromIntegral e
+                s' = fromIntegral s
+                in BS.BS (fp `plusForeignPtr` s') (e'-s')
+
 {-# NOINLINE findCapture #-}
 findCapture :: RurePtr -> BS.ByteString -> CSize -> Maybe BS.ByteString
-findCapture re haystack@(BS.BS fp l) ix = unsafeDupablePerformIO $ fmap go <$> findCaptures re haystack ix 0
+findCapture re haystack@(BS.BS fp _) ix = unsafeDupablePerformIO $ fmap go <$> findCaptures re haystack ix 0
     where go (RureMatch s e) =
             let e' = fromIntegral e
                 s' = fromIntegral s
diff --git a/src/Jacinda/Rename.hs b/src/Jacinda/Rename.hs
--- a/src/Jacinda/Rename.hs
+++ b/src/Jacinda/Rename.hs
@@ -91,19 +91,25 @@
 mkLam :: [Name a] -> E a -> E a
 mkLam ns e = foldr (\n -> Lam (loc n) n) e ns
 
+-- TODO: investigate performance w/out cata
+
 -- | 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 _                = False
+    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@
@@ -133,14 +139,14 @@
 renameE (Lam l n e)     = do
     (n', modR) <- withName n
     Lam l n' <$> withRenames modR (renameE e)
-renameE (Dfn l e) | hasY e = do
+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 (replaceXY (Name nX uX) (Name nY uY) e)
+    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 (replaceX (Name n u) e)
+    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."
@@ -152,4 +158,5 @@
 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
--- a/src/Jacinda/Ty.hs
+++ b/src/Jacinda/Ty.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE OverloadedStrings #-}
 
 module Jacinda.Ty ( TypeM
@@ -11,7 +12,7 @@
 import           Control.Exception          (Exception)
 import           Control.Monad              (forM)
 import           Control.Monad.Except       (throwError)
-import           Control.Monad.State.Strict (StateT, gets, modify, runStateT)
+import           Control.Monad.State.Strict (StateT, gets, modify, runState, runStateT)
 import           Data.Bifunctor             (first, second)
 import           Data.Foldable              (traverse_)
 import           Data.Functor               (void, ($>))
@@ -33,12 +34,16 @@
              | Doesn'tSatisfy a (T ()) C
              | IllScoped a (Name a)
              | Ambiguous (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 e)                = "type 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
@@ -47,7 +52,7 @@
 
 -- solve, unify etc. THEN check that all constraints are satisfied?
 -- (after accumulating classVar membership...)
-data TyState a = TyState { maxU        :: Int
+data TyState a = TyState { maxU        :: !Int
                          , kindEnv     :: IM.IntMap K
                          , classVars   :: IM.IntMap (S.Set (C, a))
                          , varEnv      :: IM.IntMap (T K)
@@ -64,6 +69,10 @@
 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
 
@@ -129,7 +138,7 @@
 unify = unifyPrep IM.empty
 
 unifyM :: S.Set (l, T a, T a) -> TypeM l (IM.IntMap (T a))
-unifyM s = unify (S.toList s)
+unifyM s = {-# SCC "unifyM" #-} unify (S.toList s)
 
 substInt :: IM.IntMap (T a) -> Int -> Maybe (T a)
 substInt tys k =
@@ -159,10 +168,12 @@
 
 higherOrder :: T.Text -> TypeM a (Name K)
 higherOrder t = freshName t (KArr Star Star)
+-- TODO: this should modify kind environment
 
 -- of kind 'Star'
 dummyName :: T.Text -> TypeM a (Name K)
 dummyName n = freshName n Star
+-- TODO: this should modify kind environment
 
 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
@@ -181,6 +192,68 @@
 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) = cloneTy i ty
+    ty <$ modify (setMaxU i)
+
+cloneTy :: Int -> T a -> (T a, Int)
+cloneTy i ty = second fst $ 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 k
+                    Nothing -> do
+                        k <- gets fst
+                        let ty' = TyVar l (Name n (Unique $ k+1) l')
+                        ty' <$ modify (\(u, s) -> (u+1, IM.insert j ty' 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
+
 -- TODO: this will need some class context if we permit custom types (Optional)
 checkType :: Ord a => T K -> (C, a) -> TypeM a ()
 checkType TyVar{} _                            = pure () -- TODO: I think this is right
@@ -235,16 +308,16 @@
            -> Int
            -> S.Set (C, a)
            -> TypeM a ()
-checkClass tys i cs =
+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: do we need to check var is well-kinded for constraint?
+        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
+        Just ty -> liftCloneTy ty
         Nothing -> throwError $ IllScoped l n
 
 tyOf :: Ord a => E a -> TypeM a (T K)
@@ -290,10 +363,9 @@
     traverse_ (uncurry (checkClass backNames)) toCheck
     backNames' <- unifyM =<< gets constraints
     -- FIXME: not sure if termination/whatever is guaranteed, need 2 think..
-    let res = fmap (substConstraints backNames') (Program ds' e')
+    let res = {-# SCC "substConstraints" #-} fmap (substConstraints backNames') (Program ds' e')
     checkAmb (expr res) $> res
 
--- FIXME kind check
 tyE :: Ord a => E a -> TypeM a (E (T K))
 tyE e = do
     e' <- tyE0 e
@@ -341,12 +413,6 @@
 desugar :: a
 desugar = error "Should have been de-sugared in an earlier stage!"
 
-tyVec :: T K
-tyVec = TyB (KArr Star Star) TyVec
-
-mkVec :: T K -> T K
-mkVec = hkt tyVec
-
 tyE0 :: Ord a => E a -> TypeM a (E (T K))
 tyE0 (BoolLit _ b)           = pure $ BoolLit tyBool b
 tyE0 (IntLit _ i)            = pure $ IntLit tyI i
@@ -388,6 +454,11 @@
 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 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
@@ -408,7 +479,7 @@
 tyE0 (UBuiltin _ (At i)) = do
     a <- dummyName "a"
     let a' = var a
-        tyV = hkt tyVec a'
+        tyV = mkVec a'
     pure $ UBuiltin (tyArr tyV a') (At i)
 tyE0 (UBuiltin l (Select i)) = do
     a <- dummyName "a"
@@ -477,6 +548,14 @@
         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 tyStr (tyOpt tyStr)))) Captures
 -- (a -> a -> a) -> Stream a -> Stream a
@@ -510,6 +589,8 @@
         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 tyStr (mkVec tyStr)))) AllCaptures
 tyE0 (Implicit _ e) = do
     e' <- tyE0 e
     pure $ Implicit (tyStream (eLoc e')) e'
@@ -576,3 +657,11 @@
         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
--- a/src/Jacinda/Ty/Const.hs
+++ b/src/Jacinda/Ty/Const.hs
@@ -5,6 +5,7 @@
                         , tyBool
                         , hkt
                         , tyOpt
+                        , mkVec
                         ) where
 
 import           Jacinda.AST
@@ -30,3 +31,9 @@
 
 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/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -42,6 +42,7 @@
         , testCase "typechecks/parses correctly" (tyFile "test/examples/line.jac")
         , testCase "split eval" (evalTo "[x+' '+y]|'' split '01-23-1987' /-/" " 01 23 1987")
         , testCase "captureE" (evalTo "'01-23-1987' ~* 3 /(\\d{2})-(\\d{2})-(\\d{4})/" "Some 1987")
+        , testCase "if...then...else" (evalTo "if #t then 0 else 1" "0")
         ]
 
 evalTo :: BSL.ByteString -> String -> Assertion
