diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -30,8 +30,10 @@
       - run: du -hd3 ~/.cabal ~/.ghc || true
 
       - run: haddock --version || sudo apt-get install ghc-haddock
-      - run: ghc   --version
-      - run: cabal --version
+      - run: ghc     --version
+      - run: cabal   --version
+      - run: haddock --version
+      - run: ghc-pkg list
 
       - name: Check out repository
         uses: actions/checkout@v2
@@ -86,8 +88,10 @@
       - run: make --version || apt-get update
       - run: make --version || apt-get install make
 
-      - run: ghc   --version
-      - run: cabal --version
+      - run: ghc     --version
+      - run: cabal   --version
+      - run: haddock --version
+      - run: ghc-pkg list
 
       - name: Check out repository
         uses: actions/checkout@v2
@@ -116,6 +120,8 @@
           path: ~/.stack
           key:          v1-${{ runner.os }}-stack-${{ hashFiles('stack.yaml') }}
           restore-keys: v1-${{ runner.os }}-stack-
+
+      - run: stack --version
 
       - name: Check out repository
         uses: actions/checkout@v2
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -37,7 +37,7 @@
   $(EGS)
 GHCIMPORTDIRS = src:test
 GHCFLAGS = -O2 $(shell grep -q "Arch Linux" /etc/lsb-release && echo -dynamic)
-HUGSIMPORTDIRS = .:./src:./test:./etc/hugs-backports:/usr/lib/hugs/packages/*
+HUGSIMPORTDIRS = .:./src:./test:./etc/hugs-backports:/usr/lib/hugs/packages/*:../leancheck/src
 HUGSFLAGS = -98 -h32M
 RUNPARAMETERS =
 LIB_DEPS = base template-haskell
@@ -72,6 +72,11 @@
 
 ghci: mk/All.ghci
 
+hugs: src/Data/Express/Core.hugs
+
+hugs-test: \
+  test/typecheck.runhugs
+
 install:
 	@echo "use \`cabal install' instead"
 
@@ -97,9 +102,6 @@
 	./$< >           test/model/$<.out
 
 test-via-everything: test test-via-cabal test-via-stack
-
-hugs-test:
-	echo 'TBA'
 
 legacy-test: # needs ghc-8.8 .. ghc-7.8 installed as such
 	make clean  &&  make test -j GHC=ghc-8.8
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -416,6 +416,10 @@
 
 For more examples, see the [eg](eg) and [bench](bench) folders.
 
+Express is subject to a paper
+"[Express: Applications of Dynamically Typed Haskell Expressions](https://matela.com.br/express.pdf)"
+(2021).
+
 
 [Express's Haddock documentation]: https://hackage.haskell.org/package/express/docs/Data-Express.html
 
diff --git a/TODO.md b/TODO.md
--- a/TODO.md
+++ b/TODO.md
@@ -1,14 +1,4 @@
-TO DO list for express
+TO DO list for Express
 ======================
 
-Here is a list of things TO DO for express:
-
-* add partial application functions `-+`, `-:`, `-==` to fixtures
-
-* document and test the `Instances` module
-
-* review Haddock of `Data.Express`
-
-* sweep occurrences of `TODO:` throughout source files
-
-* Document `test/Test.ListableExpr` module thoroughly;
+Nothing planned at the moment.
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -2,6 +2,19 @@
 =====================
 
 
+v0.1.16
+-------
+
+* add `five`, `six`, ... `twelve` to `Data.Express.Fixtures`.
+
+* add `cs_` to `Data.Express.Fixtures`.
+
+* improve backwards compatibility:
+  `Data.Express.Core/Hole/Match/Map/Name/Triexpr/Utils` now work on Hugs.
+
+* 100% Haddock coverage on most modules including REPL examples.
+
+
 v0.1.14
 -------
 
diff --git a/etc/hugs-backports/Data/Function.hs b/etc/hugs-backports/Data/Function.hs
new file mode 100644
--- /dev/null
+++ b/etc/hugs-backports/Data/Function.hs
@@ -0,0 +1,13 @@
+-- Backport of Data.Function for Hugs 2006-09.
+-- Only exports `on`
+--
+-- Copyright (c) 2018-2021 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+module Data.Function
+  ( on
+  )
+where
+
+on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
+(?) `on` f  =  \x y -> f x ? f y
+infixl 0 `on`
diff --git a/express.cabal b/express.cabal
--- a/express.cabal
+++ b/express.cabal
@@ -1,11 +1,11 @@
 -- Cabal file for express
 name: express
-version: 0.1.14
-synopsis: Dynamically-typed expressions involving applications and variables.
+version: 0.1.16
+synopsis: Dynamically-typed expressions involving function application and variables.
 description:
   Express is a library for manipulating dynamically typed Haskell expressions.
-  It's like @ Data.Dynamic @ but with support for encoding applications and
-  variables.
+  It's like @ Data.Dynamic @ but
+  with support for encoding function aplication and variables.
   .
   It provides the @ Expr @ type and over a hundred functions for
   building, evaluating, comparing, folding, canonicalizing and matching
@@ -43,6 +43,7 @@
                   , bench/runtime/zero/versions
                   , bench/runtime/zero/bench/*.runtime
                   , bench/runtime/zero/eg/*.runtime
+                  , etc/hugs-backports/Data/Function.hs
 tested-with: GHC==9.0
            , GHC==8.10
            , GHC==8.8
@@ -52,7 +53,8 @@
            , GHC==8.0
            , GHC==7.10
            , GHC==7.8
-
+           , Hugs==2006.9
+-- for Hugs, not all modules are supported, see test/typecheck.hs
 
 source-repository head
   type:           git
@@ -61,7 +63,7 @@
 source-repository this
   type:           git
   location:       https://github.com/rudymatela/express
-  tag:            v0.1.14
+  tag:            v0.1.16
 
 library
   exposed-modules:     Data.Express
@@ -79,6 +81,7 @@
                      , Data.Express.Canon
                      , Data.Express.Match
                      , Data.Express.Triexpr
+                     , Data.Express.Utils
                      , Data.Express.Utils.List
                      , Data.Express.Utils.String
                      , Data.Express.Utils.Typeable
@@ -226,6 +229,14 @@
 test-suite utils
   type:                exitcode-stdio-1.0
   main-is:             utils.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.9.10
+  default-language:    Haskell2010
+
+test-suite typecheck
+  type:                exitcode-stdio-1.0
+  main-is:             typecheck.hs
   hs-source-dirs:      test
   other-modules:       Test, Test.ListableExpr
   build-depends:       base >= 4 && < 5, express, leancheck >= 0.9.10
diff --git a/mk/depend.mk b/mk/depend.mk
--- a/mk/depend.mk
+++ b/mk/depend.mk
@@ -4,6 +4,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -31,6 +32,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -58,6 +60,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -85,6 +88,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Triexpr.hs \
   src/Data/Express.hs \
@@ -113,6 +117,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -140,6 +145,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -167,6 +173,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -195,6 +202,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -217,6 +225,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -239,6 +248,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -254,10 +264,13 @@
   src/Data/Express/Canon.hs \
   src/Data/Express/Basic.hs \
   eg/u-speculate.hs
+etc/hugs-backports/Data/Function.o: \
+  etc/hugs-backports/Data/Function.hs
 mk/All.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Triexpr.hs \
   src/Data/Express.hs \
@@ -281,6 +294,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Triexpr.hs \
   src/Data/Express.hs \
@@ -301,6 +315,7 @@
 src/Data/Express/Basic.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Map.hs \
   src/Data/Express/Hole.hs \
@@ -310,6 +325,7 @@
 src/Data/Express/Canon.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Name.hs \
   src/Data/Express/Map.hs \
@@ -323,12 +339,14 @@
 src/Data/Express/Core.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Core.hs
 src/Data/Express/Express/Derive.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Express.hs \
   src/Data/Express/Express/Derive.hs \
@@ -336,6 +354,7 @@
 src/Data/Express/Express.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Express.hs \
   src/Data/Express/Core.hs
@@ -345,6 +364,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -363,6 +383,7 @@
 src/Data/Express/Fold.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Fold.hs \
   src/Data/Express/Core.hs
@@ -371,12 +392,14 @@
 src/Data/Express/Hole.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Hole.hs \
   src/Data/Express/Core.hs
 src/Data/Express/Instances.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Name.hs \
   src/Data/Express/Map.hs \
@@ -389,19 +412,17 @@
 src/Data/Express/Map.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Map.hs \
   src/Data/Express/Core.hs
 src/Data/Express/Match.o: \
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Match.hs \
-  src/Data/Express/Map.hs \
-  src/Data/Express/Hole.hs \
-  src/Data/Express/Fold.hs \
-  src/Data/Express/Core.hs \
-  src/Data/Express/Basic.hs
+  src/Data/Express/Core.hs
 src/Data/Express/Name/Derive.o: \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
@@ -414,6 +435,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -430,31 +452,26 @@
   src/Data/Express/Basic.hs
 src/Data/Express/Triexpr.o: \
   src/Data/Express/Utils/Typeable.hs \
-  src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Triexpr.hs \
-  src/Data/Express.hs \
-  src/Data/Express/Name.hs \
-  src/Data/Express/Name/Derive.hs \
   src/Data/Express/Match.hs \
-  src/Data/Express/Map.hs \
-  src/Data/Express/Instances.hs \
-  src/Data/Express/Hole.hs \
-  src/Data/Express/Fold.hs \
-  src/Data/Express/Express.hs \
-  src/Data/Express/Express/Derive.hs \
-  src/Data/Express/Core.hs \
-  src/Data/Express/Canon.hs \
-  src/Data/Express/Basic.hs
+  src/Data/Express/Core.hs
 src/Data/Express/Utils/List.o: \
   src/Data/Express/Utils/List.hs
+src/Data/Express/Utils.o: \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
+  src/Data/Express/Utils/List.hs
 src/Data/Express/Utils/String.o: \
   src/Data/Express/Utils/String.hs
 src/Data/Express/Utils/TH.o: \
   src/Data/Express/Utils/TH.hs
 src/Data/Express/Utils/Typeable.o: \
   src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs
 test/canon.o: \
   test/Test.hs \
@@ -463,6 +480,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -490,6 +508,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -517,6 +536,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -544,6 +564,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -571,6 +592,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -598,6 +620,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -625,6 +648,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -652,6 +676,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -679,6 +704,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -706,6 +732,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -733,6 +760,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -760,6 +788,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -787,6 +816,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -814,6 +844,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -841,6 +872,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -868,6 +900,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -893,6 +926,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -914,6 +948,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
@@ -940,6 +975,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express/Triexpr.hs \
   src/Data/Express.hs \
@@ -961,6 +997,21 @@
   test/Test.hs \
   test/Test/ListableExpr.hs \
   mk/toplibs
+test/typecheck.o: \
+  test/typecheck.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
+  src/Data/Express/Utils/List.hs \
+  src/Data/Express/Triexpr.hs \
+  src/Data/Express/Name.hs \
+  src/Data/Express/Match.hs \
+  src/Data/Express/Map.hs \
+  src/Data/Express/Hole.hs \
+  src/Data/Express/Core.hs
+test/typecheck: \
+  test/typecheck.hs \
+  mk/toplibs
 test/utils.o: \
   test/utils.hs \
   test/Test.hs \
@@ -968,6 +1019,7 @@
   src/Data/Express/Utils/Typeable.hs \
   src/Data/Express/Utils/TH.hs \
   src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils.hs \
   src/Data/Express/Utils/List.hs \
   src/Data/Express.hs \
   src/Data/Express/Name.hs \
diff --git a/src/Data/Express/Core.hs b/src/Data/Express/Core.hs
--- a/src/Data/Express/Core.hs
+++ b/src/Data/Express/Core.hs
@@ -14,7 +14,10 @@
 -- The complexity of most functions are given in big O notation
 -- where /n/ is the size of the expression being manipulated or produced.
 -- There may still be a /m/ cost associated with the values being stored in 'Expr's.
-{-# LANGUAGE DeriveDataTypeable #-} -- for GHC < 7.10
+{-# LANGUAGE CPP #-}
+#if __GLASGOW_HASKELL__ == 708
+{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}
+#endif
 module Data.Express.Core
   (
   -- * The Expr datatype
@@ -76,14 +79,7 @@
 where
 
 import Data.Dynamic
-import Data.Function (on)
-import Data.List (intercalate, sort)
-import Data.Maybe (fromMaybe, isNothing, isJust)
-import Data.Either (either)
-import Data.Monoid ((<>))
-
-import Data.Express.Utils.List
-import Data.Express.Utils.String
+import Data.Express.Utils
 import Data.Express.Utils.Typeable
 
 -- |
@@ -129,8 +125,11 @@
 -- represents a 'var'iable.
 data Expr  =  Value String Dynamic -- ^ a 'value' enconded as 'String' and 'Dynamic'
            |  Expr :$ Expr         -- ^ function application between expressions
-  deriving Typeable -- for GHC < 7.10
 
+#if __GLASGOW_HASKELL__ == 708
+deriving instance Typeable Expr
+#endif
+
 -- | /O(1)/.
 -- It takes a string representation of a value and a value, returning an
 -- 'Expr' with that terminal value.
@@ -535,9 +534,32 @@
 showsOpExpr :: String -> Expr -> String -> String
 showsOpExpr op = showsPrecExpr (prec op + 1)
 
+-- | /O(n)/.
+-- Like 'showPrecExpr' but
+-- the precedence is taken from the given operator name.
+--
+-- > > showOpExpr "*" (two -*- three)
+-- > "(2 * 3)"
+--
+-- > > showOpExpr "+" (two -*- three)
+-- > "2 * 3"
+--
+-- To imply that the surrounding environment is a function application,
+-- use @" "@ as the given operator.
+--
+-- > > showOpExpr " " (two -*- three)
+-- > "(2 * 3)"
 showOpExpr :: String -> Expr -> String
 showOpExpr op = showPrecExpr (prec op + 1)
 
+-- | /O(n)/.
+-- Like 'showExpr' but allows specifying the surrounding precedence.
+--
+-- > > showPrecExpr 6 (one -+- two)
+-- > "1 + 2"
+--
+-- > > showPrecExpr 7 (one -+- two)
+-- > "(1 + 2)"
 showPrecExpr :: Int -> Expr -> String
 showPrecExpr n e = showsPrecExpr n e ""
 
@@ -560,9 +582,9 @@
 --
 -- This instance works for ill-typed expressions.
 instance Eq Expr where
-  Value s1 d1 == Value s2 d2  =  s1 == s2 && dynTypeRep d1 == dynTypeRep d2
-  ef1 :$ ex1  == ef2 :$ ex2   =  ef1 == ef2 && ex1 == ex2
-  _           == _            =  False
+  Value s1 d1  == Value s2 d2   =  s1 == s2 && dynTypeRep d1 == dynTypeRep d2
+  (ef1 :$ ex1) == (ef2 :$ ex2)  =  ef1 == ef2 && ex1 == ex2
+  _            == _             =  False
 
 -- | /O(n)/.
 -- Does not evaluate values when comparing, but rather uses their
diff --git a/src/Data/Express/Express.hs b/src/Data/Express/Express.hs
--- a/src/Data/Express/Express.hs
+++ b/src/Data/Express/Express.hs
@@ -100,11 +100,14 @@
 
 instance (Integral a, Express a) => Express (Ratio a) where
   expr  =  val
+-- note that the "Integral a" restriction above is needed on GHC <= 7.10
+
+-- TODO: deeply encode ratios
+-- NOTE:
 -- The following would allow zero denominators
 -- expr (n % d) = constant "%" ((%) -:> n) :$ expr n :$ expr d
--- TODO: allow zero denominators as it is not our problem
---       but only after refactoring Extrapolate to use Express
--- note that the "Integral a" restriction above is needed on GHC <= 7.10
+-- but that is not our problem here!
+-- Nevertheless, we should change and see the effect on Extrapolate first...
 
 instance (Express a, Express b, Express c, Express d, Express e)
       => Express (a,b,c,d,e) where
diff --git a/src/Data/Express/Fixtures.hs b/src/Data/Express/Fixtures.hs
--- a/src/Data/Express/Fixtures.hs
+++ b/src/Data/Express/Fixtures.hs
@@ -42,7 +42,7 @@
 -- or playing around on GHCi.
 --
 -- Since the 'Expr' type only allows monomorphic values,
--- encoded polimorphic values are monomorphized
+-- encoded polymorphic values are monomorphized
 -- usually to the 'Int' type.
 --
 -- /Beware:/ lifted 'Expr' functions sometimes work for different types.
@@ -116,7 +116,9 @@
   , i_, xx, yy, zz, xx'
   , ii, jj, kk, ii'
   , ll, mm, nn
-  , zero, one, two, three, four, minusOne, minusTwo
+  , zero, one, two, three, four, five, six
+  , seven, eight, nine, ten, eleven, twelve
+  , minusOne, minusTwo
   , idE, negateE, absE, signumE
   , idInt
   , idBool
@@ -125,7 +127,7 @@
   , idBools
   , idString
   , id', const', negate', abs', signum'
-  , plus, times
+  , plus, times, minus
   , (-+-), (-*-)
   , ff, ffE
   , gg, ggE
@@ -136,7 +138,7 @@
   , even'
 
   -- ** Chars
-  , c_
+  , c_, cs_
   , cc, dd, ccs
   , ae, bee, cee, dee, zed, zee
   , space, lineBreak
@@ -292,10 +294,18 @@
 orE :: Expr
 orE  =  value "||" (||)
 
+-- | The function @==>@ lifted over 'Expr's.
+--
+-- > > false -==>- true
+-- > False ==> True :: Bool
+--
+-- > > evl $ false -==>- true :: Bool
+-- > True
 (-==>-) :: Expr -> Expr -> Expr
 e1 -==>- e2  =  implies :$ e1 :$ e2
 infixr 0 -==>-
 
+-- | The @==>@ operator encoded as an 'Expr'
 implies :: Expr
 implies  =  value "==>" (==>)
   where
@@ -463,6 +473,62 @@
 four :: Expr
 four  =  val (4 :: Int)
 
+-- | The value @5@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > five
+-- > 5 :: Int
+five :: Expr
+five  =  val (5 :: Int)
+
+-- | The value @6@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > six
+-- > 6 :: Int
+six :: Expr
+six  =  val (6 :: Int)
+
+-- | The value @7@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > seven
+-- > 7 :: Int
+seven :: Expr
+seven  =  val (7 :: Int)
+
+-- | The value @8@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > eight
+-- > 8 :: Int
+eight :: Expr
+eight  =  val (8 :: Int)
+
+-- | The value @9@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > nine
+-- > 9 :: Int
+nine :: Expr
+nine  =  val (9 :: Int)
+
+-- | The value @10@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > ten
+-- > 10 :: Int
+ten :: Expr
+ten  =  val (10 :: Int)
+
+-- | The value @11@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > eleven
+-- > 11 :: Int
+eleven :: Expr
+eleven  =  val (11 :: Int)
+
+-- | The value @12@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > twelve
+-- > 12 :: Int
+twelve :: Expr
+twelve  =  val (12 :: Int)
+
 -- | The value @-1@ bound to the 'Int' type encoded as an 'Expr'.
 --
 -- > > minusOne
@@ -602,6 +668,13 @@
 times :: Expr
 times  =  value "*" ((*) :: Int -> Int -> Int)
 
+-- | The subtraction '-' operator encoded as an 'Expr'.
+--
+-- > > minus :$ one
+-- > (1 -) :: Int -> Int
+--
+-- > > minus :$ one :$ zero
+-- > 1 - 0 :: Int
 minus :: Expr
 minus  =  value "-" ((-) :: Int -> Int -> Int)
 
@@ -660,6 +733,12 @@
 idString  =  value "id" (id :: Id String)
 type Id a = a -> a
 
+-- | The 'const' function lifted over the 'Expr' type.
+--
+-- > > const' zero one
+-- > const 0 1 :: Int
+--
+-- This works for the argument types 'Int', 'Char', 'Bool' and their lists.
 const' :: Expr -> Expr -> Expr
 const' e1 e2  =  (:$ e2) . headOr err $ mapMaybe ($$ e1)
   [ value "const" (const :: Int -> Int -> Int)
@@ -723,9 +802,23 @@
 signumE :: Expr
 signumE  =  value "signum" (signum :: Int -> Int)
 
+-- | 'odd' with an 'Int' argument lifted over the 'Expr' type.
+--
+-- > > odd' (xx -+- one)
+-- > odd (x + 1) :: Bool
+--
+-- > > evl (odd' two) :: Bool
+-- > False
 odd' :: Expr -> Expr
 odd' = (oddE :$) where oddE = value "odd" (odd :: Int -> Bool)
 
+-- | 'even' with an 'Int' argument lifted over the 'Expr' type.
+--
+-- > > even' (xx -+- two)
+-- > even (x + 2) :: Bool
+--
+-- > > evl (even' two) :: Bool
+-- > True
 even' :: Expr -> Expr
 even' = (evenE :$) where evenE = value "even" (even :: Int -> Bool)
 
@@ -736,18 +829,44 @@
 c_ :: Expr
 c_  =  hole char
 
+-- | A hole of 'String' type encoded as an 'Expr'.
+--
+-- > > cs_
+-- > _ :: [Char]
+cs_ :: Expr
+cs_  =  hole [char]
+
+-- | A variable named @c@ of type 'Char' encoded as an 'Expr'.
+--
+-- > > cc
+-- > c :: Char
 cc :: Expr
 cc  =  var "c" char
 
+-- | A variable named @c@ of type 'Char' encoded as an 'Expr'.
+--
+-- > > dd
+-- > d :: Char
 dd :: Expr
 dd  =  var "d" char
 
+-- | A variable named @cs@ of type 'String' encoded as an 'Expr'.
+--
+-- > > ccs
+-- > cs :: [Char]
 ccs :: Expr
 ccs  =  var "cs" [char]
 
--- The English name for letter 'a' is not really 'ae', but simply 'a'.
+-- | The character @\'a\'@ encoded as an 'Expr'.
+--
+-- > > ae
+-- > 'a' :: Char
+--
+-- > > evl ae :: Char
+-- > 'a'
 ae :: Expr
 ae  =  val 'a'
+-- The English name for letter 'a' is not really 'ae', but simply 'a'.
 
 -- | The character @\'b\'@ encoded as an 'Expr'
 --
@@ -803,15 +922,31 @@
 zee :: Expr
 zee  =  val 'z'
 
+-- | The space character encoded as an 'Expr'
+--
+-- > > space
+-- > ' ' :: Char
 space :: Expr
 space = val ' '
 
+-- | The line break character encoded as an 'Expr'
+--
+-- > > lineBreak
+-- > '\n' :: Char
 lineBreak :: Expr
 lineBreak = val '\n'
 
+-- | The 'ord' function lifted over 'Expr'
+--
+-- > > ord' bee
+-- > ord 'b' :: Int
+--
+-- > > evl (ord' bee)
+-- > 98
 ord' :: Expr -> Expr
 ord' = (ordE :$)
 
+-- | The 'ord' function encoded as an 'Expr'
 ordE :: Expr
 ordE = value "ord" ord
 
@@ -919,6 +1054,7 @@
   err  =  error $ "(-:-): unhandled type " ++ show (typ e1)
 infixr 5 -:-
 
+-- | Append for list of 'Int's encoded as an 'Expr'.
 appendInt :: Expr
 appendInt  =  value "++" ((++) :: [Int] -> [Int] -> [Int])
 
@@ -1029,6 +1165,20 @@
   where
   err  =  error $ "length': cannot apply `length :: [a] -> a` to `" ++ show exs ++ "'."
 
+-- | List 'sort' lifted over the 'Expr' type.
+--   Works for the element types 'Int', 'Char' and 'Bool'.
+--
+-- > > sort' $ unit one
+-- > sort [1] :: Int
+--
+-- > > sort' $ unit bee
+-- > sort "b" :: Int
+--
+-- > > sort' $ zero -:- unit two
+-- > sort [0,2] :: Int
+--
+-- > > evl $ sort' $ two -:- unit one :: [Int]
+-- > [1,2]
 sort' :: Expr -> Expr
 sort' exs = headOr err $ mapMaybe ($$ exs)
   [ value "sort" (sort :: [Int] -> [Int])
@@ -1038,6 +1188,14 @@
   where
   err  =  error $ "sort': unhandled type " ++ show (typ exs)
 
+-- | List 'insert' lifted over the 'Expr' type.
+--   Works for the element types 'Int', 'Char' and 'Bool'.
+--
+-- > > insert' zero nilInt
+-- > insert 0 [] :: [Int]
+--
+-- > > insert' false (false -:- unit true)
+-- > insert False [False,True] :: [Bool]
 insert' :: Expr -> Expr -> Expr
 insert' ex exs  =  (:$ exs) . headOr err $ mapMaybe ($$ ex)
   [ value "insert" (insert :: Int -> [Int] -> [Int])
@@ -1047,6 +1205,14 @@
   where
   err  =  error $ "insert': unhandled type " ++ show (typ ex)
 
+-- | List 'elem' lifted over the 'Expr' type.
+--   Works for the element types 'Int', 'Char' and 'Bool'.
+--
+-- > > elem' false (false -:- unit true)
+-- > elem False [False,True] :: Bool
+--
+-- > > evl $ elem' false (false -:- unit true) :: Bool
+-- > True
 elem' :: Expr -> Expr -> Expr
 elem' ex exs  =  (:$ exs) . headOr err $ mapMaybe ($$ ex)
   [ value "elem" (elem :: Int -> [Int] -> Bool)
@@ -1056,6 +1222,12 @@
   where
   err  =  error $ "elem': unhandled type " ++ show (typ ex)
 
+-- | '$' lifted over 'Expr's
+--
+-- > > absE -$- one
+-- > abs $ 1 :: Int
+--
+-- Works for 'Int', 'Bool', 'Char' argument types and their lists.
 (-$-) :: Expr -> Expr -> Expr
 ef -$- ex = (:$ ex) . headOr err $ mapMaybe ($$ ef)
   [ value "$" (($) :: Apply Int)
@@ -1070,6 +1242,15 @@
 infixl 6 -$-
 type Apply a = (a -> a) -> a -> a
 
+-- | Constructs an equation between two 'Expr's.
+--
+-- > > xx -==- zero
+-- > x == 0 :: Bool
+--
+-- > > cc -==- dee
+-- > c == 'd' :: Bool
+--
+-- This works for the 'Int', 'Bool', 'Char' argument types and their lists.
 (-==-) :: Expr -> Expr -> Expr
 ex -==- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value "==" ((==) :: Comparison ())
@@ -1085,6 +1266,13 @@
 infix 4 -==-
 type Comparison a = a -> a -> Bool
 
+-- | Constructs an inequation between two 'Expr's.
+--
+-- > > xx -/=- zero
+-- > x /= 0 :: Bool
+--
+-- > > cc -/=- ae
+-- > c /= 'a' :: Bool
 (-/=-) :: Expr -> Expr -> Expr
 ex -/=- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value "/=" ((/=) :: Comparison ())
@@ -1099,6 +1287,13 @@
   err  =  error $ "(-/=-): unhandled type " ++ show (typ ex)
 infix 4 -/=-
 
+-- | Constructs a less-than-or-equal inequation between two 'Expr's.
+--
+-- > > xx -<=- zero
+-- > x <= 0 :: Bool
+--
+-- > > cc -<=- ae
+-- > c <= 'a' :: Bool
 (-<=-) :: Expr -> Expr -> Expr
 ex -<=- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value "<=" ((<=) :: Comparison ())
@@ -1113,6 +1308,13 @@
   err  =  error $ "(-<=-): unhandled type " ++ show (typ ex)
 infix 4 -<=-
 
+-- | Constructs a less-than inequation between two 'Expr's.
+--
+-- > > xx -<- zero
+-- > x < 0 :: Bool
+--
+-- > > cc -<- bee
+-- > c < 'b' :: Bool
 (-<-) :: Expr -> Expr -> Expr
 ex -<- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value "<" ((<) :: Comparison ())
@@ -1157,6 +1359,13 @@
   iff p x y  =  if p then x else y
 type If a = Bool -> a -> a -> a
 
+-- | Constructs an 'Expr'-encoded 'compare' operation between two 'Expr's.
+--
+-- > > xx `compare'` zero
+-- > compare x 0 :: Ordering
+--
+-- > > compare' ae bee
+-- > compare 'a' 'b' :: Ordering
 compare' :: Expr -> Expr -> Expr
 compare' ex ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value "compare" (compare :: Compare ())
@@ -1171,18 +1380,36 @@
   err  =  error $ "(-<-): unhandled type " ++ show (typ ex)
 type Compare a = a -> a -> Ordering
 
+-- | 'Nothing' bound to the 'Maybe' 'Int' type encoded as an 'Expr'.
+--
+-- This is an alias to 'nothingInt'.
 nothing :: Expr
 nothing  =  nothingInt
 
-nothingInt, nothingBool :: Expr
+-- | 'Nothing' bound to the 'Maybe' 'Int' type encoded as an 'Expr'.
+nothingInt :: Expr
 nothingInt   =  val (Nothing :: Maybe Int)
+
+-- | 'Nothing' bound to the 'Maybe' 'Bool' type encoded as an 'Expr'.
+nothingBool :: Expr
 nothingBool  =  val (Nothing :: Maybe Bool)
 
-justInt, justBool :: Expr
-justInt      =  value "Just" (Just :: Just Int)
-justBool     =  value "Just" (Just :: Just Bool)
-type Just a  =  a -> Maybe a
+-- | The 'Just' constructor of the 'Int' element type encoded as an 'Expr'.
+justInt :: Expr
+justInt      =  value "Just" (Just :: Int -> Maybe Int)
 
+-- | The 'Just' constructor of the 'Bool' element type encoded as an 'Expr'.
+justBool :: Expr
+justBool     =  value "Just" (Just :: Bool -> Maybe Bool)
+
+-- | The 'Just' constructor lifted over the 'Expr' type.
+--
+-- This works for the 'Bool' and 'Int' argument types.
+--
+-- > > just zero
+-- > Just 0 :: Maybe Int
+-- > > just false
+-- > Just False :: Maybe Bool
 just :: Expr -> Expr
 just ex  =  headOr err $ mapMaybe ($$ ex)
   [ justInt
@@ -1191,9 +1418,14 @@
   where
   err  =  error $ "just: unhandled type " ++ show (typ ex)
 
+-- | An infix synonym of 'pair'.
 (-|-) :: Expr -> Expr -> Expr
 (-|-) = pair
 
+-- | The pair constructor lifted over 'Expr's.
+--
+-- This works for the 'Int' and 'Bool' element types
+-- by differently from 'foldPair' by returning a well-typed expression.
 pair :: Expr -> Expr -> Expr
 pair x y  =  comma :$ x :$ y
   where
@@ -1205,24 +1437,37 @@
             (t,t')          -> error $ "(-:-): unhandled types " ++ t ++ " " ++ t'
 type Pair a b = a -> b -> (a,b)
 
+-- | The pair constructor (@ :: ... -> (Int,Int) @) encoded as an 'Expr'.
 comma :: Expr
 comma = value "," ((,) :: Pair Int Int)
 
+-- | The triple/trio constructor lifted over 'Expr's.
+--
+-- This only works for the 'Int' element type.
 triple :: Expr -> Expr -> Expr -> Expr
 triple e1 e2 e3 = ccE :$ e1 :$ e2 :$ e3
   where
   ccE = value ",," ((,,) :: Int -> Int -> Int -> (Int,Int,Int))
 
+-- | The quadruple constructor lifted over 'Expr's.
+--
+-- This only works for the 'Int' element type.
 quadruple :: Expr -> Expr -> Expr -> Expr -> Expr
 quadruple e1 e2 e3 e4 = cccE :$ e1 :$ e2 :$ e3 :$ e4
   where
   cccE = value ",,," ((,,,) :: Int -> Int -> Int -> Int -> (Int,Int,Int,Int))
 
+-- | The quintuple constructor lifted over 'Expr's.
+--
+-- This only works for the 'Int' element type.
 quintuple :: Expr -> Expr -> Expr -> Expr -> Expr -> Expr
 quintuple e1 e2 e3 e4 e5 = ccccE :$ e1 :$ e2 :$ e3 :$ e4 :$ e5
   where
   ccccE = value ",,,," ((,,,,) :: Int -> Int -> Int -> Int -> Int -> (Int,Int,Int,Int,Int))
 
+-- | The sixtuple constructor lifted over 'Expr's.
+--
+-- This only works for the 'Int' element type.
 sixtuple :: Expr -> Expr -> Expr -> Expr -> Expr -> Expr -> Expr
 sixtuple e1 e2 e3 e4 e5 e6 = cccccE :$ e1 :$ e2 :$ e3 :$ e4 :$ e5 :$ e6
   where
@@ -1301,9 +1546,22 @@
 headOr x []     =  x
 headOr _ (x:_)  =  x
 
+-- | Function composition encoded as an 'Expr':
+--
+-- > > compose
+-- > (.) :: (Int -> Int) -> (Int -> Int) -> Int -> Int
 compose :: Expr
 compose  =  value "." ((.) :: Compose Int)
 
+-- | Function composition '.' lifted over 'Expr'.
+--
+-- > > absE -.- negateE
+-- > abs . negate :: Int -> Int
+--
+-- > > absE -.- negateE :$ one
+-- > (abs . negate) 1 :: Int
+--
+-- This works for 'Int', 'Bool', 'Char' and their lists.
 (-.-) :: Expr -> Expr -> Expr
 ex -.- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value "." ((.) :: Compose ())
@@ -1318,9 +1576,17 @@
   err  =  error $ "(-.-): unhandled type " ++ show (typ ex)
 type Compose a = (a -> a) -> (a -> a) -> (a -> a)
 
+-- | 'map' over the 'Int' element type encoded as an 'Expr'
+--
+-- > > mapE
+-- > map :: (Int -> Int) -> [Int] -> [Int]
 mapE :: Expr
 mapE  =  value "map" (map :: Map Int)
 
+-- | 'map' lifted over 'Expr's.
+--
+-- > > map' absE (unit one)
+-- > map abs [1] :: [Int]
 map' :: Expr -> Expr -> Expr
 map' ef exs  =  (:$ exs) . headOr err $ mapMaybe ($$ ef)
   [ value "map" (map :: Map ())
@@ -1335,6 +1601,12 @@
   err  =  error $ "map': unhandled type " ++ show (typ ef)
 type Map a = (a -> a) -> [a] -> [a]
 
+-- | 'enumFrom' lifted over 'Expr's.
+--
+-- > > enumFrom' zero
+-- > enumFrom 0 :: [Int]
+--
+-- Works for 'Int's, 'Bool's and 'Char's.
 enumFrom' :: Expr -> Expr
 enumFrom' ex  =  headOr err $ mapMaybe ($$ ex)
   [ value "enumFrom" (enumFrom :: EnumFrom Int)
@@ -1345,6 +1617,12 @@
   err  =  error $ "enumFrom': unhandled type " ++ show (typ ex)
 type EnumFrom a  =  (a -> [a])
 
+-- | 'enumFrom' lifted over 'Expr's named as @".."@ for pretty-printing.
+--
+-- > > (-..) one
+-- > [1..] :: [Int]
+--
+-- Works for 'Int's, 'Bool's and 'Char's.
 (-..) :: Expr -> Expr
 (-..) ex  =  headOr err $ mapMaybe ($$ ex)
   [ value ".." (enumFrom :: EnumFrom Int)
@@ -1354,6 +1632,10 @@
   where
   err  =  error $ "(-..): unhandled type " ++ show (typ ex)
 
+-- | 'enumFromTo' lifted over 'Expr's
+--
+-- > > enumFromTo' zero four
+-- > enumFromTo 0 4 :: [Int]
 enumFromTo' :: Expr -> Expr -> Expr
 enumFromTo' ex ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value "enumFromTo" (enumFromTo :: EnumFromTo Int)
@@ -1364,6 +1646,10 @@
   err  =  error $ "enumFromTo': unhandled type " ++ show (typ ex)
 type EnumFromTo a  =  (a -> a -> [a])
 
+-- | 'enumFromTo' lifted over 'Expr's but named as @".."@ for pretty-printing.
+--
+-- > > zero -..- four
+-- > [0..4] :: [Int]
 (-..-) :: Expr -> Expr -> Expr
 ex -..- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value ".." (enumFromTo :: EnumFromTo Int)
@@ -1373,6 +1659,10 @@
   where
   err  =  error $ "-..-: unhandled type " ++ show (typ ex)
 
+-- | 'enumFromThen' lifted over 'Expr's
+--
+-- > > enumFromThen' zero ten
+-- > enumFromThen 0 10 :: [Int]
 enumFromThen' :: Expr -> Expr -> Expr
 enumFromThen' ex ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value "enumFromThen" (enumFromThen :: EnumFromThen Int)
@@ -1383,6 +1673,10 @@
   err  =  error $ "enumFromThen': unhandled type " ++ show (typ ex)
 type EnumFromThen a  =  (a -> a -> [a])
 
+-- | 'enumFromThen' lifted over 'Expr's but named as @",.."@ for pretty printing.
+--
+-- > > zero -... ten
+-- > [0,10..] :: [Int]
 (-...) :: Expr -> Expr -> Expr
 ex -... ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value ",.." (enumFromThen :: EnumFromThen Int)
@@ -1392,6 +1686,10 @@
   where
   err  =  error $ "-..-: unhandled type " ++ show (typ ex)
 
+-- | 'enumFromThenTo' lifted over 'Expr's.
+--
+-- > > enumFromThenTo' zero two ten
+-- > enumFromThenTo 0 2 10 :: [Int]
 enumFromThenTo' :: Expr -> Expr -> Expr -> Expr
 enumFromThenTo' ex ey ez  =  (:$ ez) . (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value "enumFromThenTo" (enumFromThenTo :: EnumFromThenTo Int)
@@ -1402,6 +1700,10 @@
   err  =  error $ "enumFromThenTo': unhandled type " ++ show (typ ex)
 type EnumFromThenTo a  =  (a -> a -> a -> [a])
 
+-- | 'enumFromThenTo' lifted over 'Expr's but named as @",.."@ for pretty-printing.
+--
+-- > > (zero -...- two) ten
+-- > [0,2..10] :: [Int]
 (-...-) :: Expr -> Expr -> Expr -> Expr
 (ex -...- ey) ez  =  (:$ ez) . (:$ ey) . headOr err $ mapMaybe ($$ ex)
   [ value ",.." (enumFromThenTo :: EnumFromThenTo Int)
diff --git a/src/Data/Express/Fold.hs b/src/Data/Express/Fold.hs
--- a/src/Data/Express/Fold.hs
+++ b/src/Data/Express/Fold.hs
@@ -5,7 +5,10 @@
 -- Maintainer  : Rudy Matela <rudy@matela.com.br>
 --
 -- Defines utilities for folding and unfolding 'Expr's.
-{-# LANGUAGE DeriveDataTypeable #-} -- for GHC <= 7.8
+{-# LANGUAGE CPP #-}
+#if __GLASGOW_HASKELL__ == 708
+{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-}
+#endif
 module Data.Express.Fold
   ( fold
   , unfold
@@ -19,10 +22,9 @@
 where
 
 import Data.Express.Core
-import Data.Typeable (Typeable) -- for GHC <= 7.8
+import Data.Express.Utils.Typeable
 
 data ExprPair = ExprPair
-  deriving Typeable -- for GHC <= 7.8
 
 -- | /O(n)/.
 -- Folds a list of 'Expr' with function application (':$').
@@ -48,7 +50,8 @@
 -- Folds a pair of 'Expr' values into a single 'Expr'.
 -- (cf. 'unfoldPair')
 --
--- This /always/ generates an ill-typed expression.
+-- This /always/ generates an ill-typed expression,
+-- as it uses a fake pair constructor.
 --
 -- > > foldPair (val False, val (1::Int))
 -- > (False,1) :: ill-typed # ExprPair $ Bool #
@@ -82,18 +85,49 @@
 unfoldPair _  =  error "unfoldPair: not an Expr pair"
 
 data ExprTrio = ExprTrio
-  deriving Typeable -- for GHC <= 7.8
 
+-- | /O(1)/.
+-- Folds a trio/triple of 'Expr' values into a single 'Expr'.
+-- (cf. 'unfoldTrio')
+--
+-- This /always/ generates an ill-typed expression
+-- as it uses a fake trio/triple constructor.
+--
+-- > > foldTrio (val False, val (1::Int), val 'a')
+-- > (False,1,'a') :: ill-typed # ExprTrio $ Bool #
+--
+-- > > foldTrio (val (0::Int), val True, val 'b')
+-- > (0,True,'b') :: ill-typed # ExprTrio $ Int #
+--
+-- This is useful when applying transformations on pairs of 'Expr's, such as
+-- 'Data.Express.Canon.canonicalize',
+-- 'Data.Express.Map.mapValues' or
+-- 'Data.Express.Canon.canonicalVariations'.
+--
+-- > > let ii = var "i" (undefined::Int)
+-- > > let kk = var "k" (undefined::Int)
+-- > > let zz = var "z" (undefined::Int)
+-- > > unfoldPair $ canonicalize $ foldPair (ii,kk,zz)
+-- > (x :: Int,y :: Int,z :: Int)
 foldTrio :: (Expr,Expr,Expr) -> Expr
 foldTrio (e1,e2,e3)  =  value ",," (undefined :: ExprTrio) :$ e1 :$ e2 :$ e3
 
+-- | /O(1)/.
+-- Unfolds an 'Expr' representing a trio/triple.
+-- This reverses the effect of 'foldTrio'.
+--
+-- > > value ",," ((,,) :: Bool->Bool->Bool->(Bool,Bool,Bool)) :$ val True :$ val False :$ val True
+-- > (True,False,True) :: (Bool,Bool,Bool)
+-- > > unfoldTrio $ value ",," ((,,) :: Bool->Bool->Bool->(Bool,Bool,Bool)) :$ val True :$ val False :$ val True
+-- > (True :: Bool,False :: Bool,True :: Bool)
+--
+-- (cf. 'unfoldPair')
 unfoldTrio :: Expr -> (Expr,Expr,Expr)
 unfoldTrio (Value ",," _ :$ e1 :$ e2 :$ e3) = (e1,e2,e3)
 unfoldTrio (Value "(,,)" _ :$ e1 :$ e2 :$ e3) = (e1,e2,e3)
 unfoldTrio _  =  error "unfoldTrio: not an Expr trio"
 
 data ExprList = ExprList
-  deriving Typeable -- for GHC <= 7.8
 
 -- | /O(n)/.
 -- Folds a list of 'Expr's into a single 'Expr'.
@@ -131,3 +165,9 @@
 unfold (Value "[]" _)             =  []
 unfold (Value ":"  _ :$ e :$ es)  =  e : unfold es
 unfold e  =  error $ "unfold: cannot unfold expression: " ++ show e
+
+#if __GLASGOW_HASKELL__ == 708
+deriving instance Typeable ExprPair
+deriving instance Typeable ExprTrio
+deriving instance Typeable ExprList
+#endif
diff --git a/src/Data/Express/Instances.hs b/src/Data/Express/Instances.hs
--- a/src/Data/Express/Instances.hs
+++ b/src/Data/Express/Instances.hs
@@ -49,8 +49,6 @@
   )
 where
 
--- TODO: document and test functions of the Instances module
-
 import Data.Express.Basic
 import Data.Express.Name
 import Data.Express.Express
@@ -118,8 +116,6 @@
 reifyName :: (Typeable a, Name a) => a -> [Expr]
 reifyName a  =  mkName (name -:> a)
 
--- todo: reifyExpr and related functions
-
 -- | /O(1)/.
 -- Builds a reified 'Eq' instance from the given '==' function.
 -- (cf. 'reifyEq')
@@ -175,6 +171,9 @@
 
 -- searching for functions --
 
+-- | /O(n)./
+-- Lookups for a comparison function (@:: a -> a -> Bool@)
+-- with the given name and argument type.
 lookupComparison :: String -> TypeRep -> [Expr] -> Maybe Expr
 lookupComparison n' t  =  find (\i@(Value n _) -> n == n' && typ i == mkComparisonTy t)
 
@@ -256,6 +255,11 @@
 isEqOrd :: [Expr] -> Expr -> Bool
 isEqOrd is e  =  isEq is e && isOrd is e
 
+-- | /O(n+m)./
+-- Like 'mkEquation', 'mkComparisonLE' and 'mkComparisonLT'
+-- but allows providing the binary operator name.
+--
+-- When not possible, this function returns 'False' encoded as an 'Expr'.
 mkComparison :: String -> [Expr] -> Expr -> Expr -> Expr
 mkComparison n' is e1 e2  =  fromMaybe (val False) $ do
   e1e <- findValidApp os e1
@@ -263,15 +267,46 @@
   where
   os = [eq | eq@(Value n _) <- is, n == n']
 
+-- | /O(n+m)./
+-- Returns an equation between two expressions
+-- given that it is possible to do so from '==' operators
+-- given in the argument instances list.
+--
+-- When not possible, this function returns 'False' encoded as an 'Expr'.
 mkEquation :: [Expr] -> Expr -> Expr -> Expr
 mkEquation  =  mkComparison "=="
 
+-- | /O(n+m)./
+-- Returns a less-than inequation between two expressions
+-- given that it is possible to do so from '<' operators
+-- given in the argument instances list.
+--
+-- When not possible, this function returns 'False' encoded as an 'Expr'.
 mkComparisonLT :: [Expr] -> Expr -> Expr -> Expr
 mkComparisonLT  =  mkComparison "<"
 
+-- | /O(n+m)./
+-- Returns a less-than-or-equal-to inequation between two expressions
+-- given that it is possible to do so from '<=' operators
+-- given in the argument instances list.
+--
+-- When not possible, this function returns 'False' encoded as an 'Expr'.
 mkComparisonLE :: [Expr] -> Expr -> Expr -> Expr
 mkComparisonLE  =  mkComparison "<="
 
+-- | /O(n+m)./
+-- Like 'name' but lifted over an instance list and an 'Expr'.
+--
+-- > > lookupName preludeNameInstances (val False)
+-- > "p"
+--
+-- > > lookupName preludeNameInstances (val (0::Int))
+-- > "x"
+--
+-- This function defaults to @"x"@ when no appropriate 'name' is found.
+--
+-- > > lookupName [] (val False)
+-- > "x"
 lookupName :: [Expr] -> Expr -> String
 lookupName is e  =  fromMaybe d $ eval "x" <$> findValidApp es e
   where
@@ -280,18 +315,29 @@
     | otherwise  =  'x' : replicate (countListTy t) 's'
   es = [e | e@(Value "name" _) <- is]
 
+-- | /O(n+m)./
+-- A mix between 'lookupName' and 'names':
+-- this returns an infinite list of names
+-- based on an instances list and an 'Expr'.
 lookupNames :: [Expr] -> Expr -> [String]
 lookupNames is  =  variableNamesFromTemplate . lookupName is
 
+-- | /O(n+m)./
+-- Like 'lookupNames' but returns a list of variables encoded as 'Expr's.
 listVarsWith :: [Expr] -> Expr -> [Expr]
 listVarsWith is e  =  lookupName is e `listVarsAsTypeOf` e
 
 
 -- helpers --
 
+-- |
+-- Given a list of functional expressions and another expression,
+-- returns a list of valid applications.
 validApps :: [Expr] -> Expr -> [Expr]
 validApps es e  =  mapMaybe ($$ e) es
 
+-- |
+-- Like 'validApps' but returns a 'Maybe' value.
 findValidApp :: [Expr] -> Expr -> Maybe Expr
 findValidApp es  =  listToMaybe . validApps es
 
@@ -302,6 +348,9 @@
 
 -- reified instances --
 
+-- |
+-- A list of reified 'Name' instances
+-- for an arbitrary selection of types from the Haskell "Prelude".
 preludeNameInstances :: [Expr]
 preludeNameInstances = concat
   [ reifyName (u :: ())
diff --git a/src/Data/Express/Match.hs b/src/Data/Express/Match.hs
--- a/src/Data/Express/Match.hs
+++ b/src/Data/Express/Match.hs
@@ -15,10 +15,8 @@
   )
 where
 
-import Data.Express.Basic
-import Data.Maybe
-import Data.Functor ((<$>))
-import Control.Monad ((>=>))
+import Data.Express.Core
+import Data.Express.Utils
 
 -- |
 -- Given two expressions, returns a 'Just' list of matches
diff --git a/src/Data/Express/Triexpr.hs b/src/Data/Express/Triexpr.hs
--- a/src/Data/Express/Triexpr.hs
+++ b/src/Data/Express/Triexpr.hs
@@ -38,17 +38,23 @@
   )
 where
 
-import Data.Express
+import Data.Express.Core
+import Data.Express.Match
 import Data.Maybe
 import Prelude hiding (map, lookup)
 
--- "Nothing" should match an App, "Just Expr" an expression
+-- | A trie of 'Expr's.
+--
+-- In the representation,
+-- 'Nothing' matches an App and 'Just' 'Expr' an expression.
 data Triexpr a = Triexpr [(Maybe Expr, Either (Triexpr a) (Expr,a))]
   deriving (Eq, Ord, Show)
 
+-- | An empty 'Triexpr'.
 empty :: Triexpr a
 empty  =  Triexpr []
 
+-- | Constructs a 'Triexpr' encoding a single expression.
 unit :: Expr -> a -> Triexpr a
 unit e x  =  u e (Right (e,x))
   where
@@ -56,6 +62,7 @@
   u (e1 :$ e2) et  =  Triexpr [(Nothing, Left $ u e1 $ Left $ u e2 et)]
   u e          et  =  Triexpr [(Just e,  et)]
 
+-- | Merges two 'Triexpr's.
 merge :: Triexpr a -> Triexpr a -> Triexpr a
 merge (Triexpr ms1) (Triexpr ms2)  =  Triexpr $ m ms1 ms2
   where
@@ -68,18 +75,22 @@
           (Left t1, Left t2) -> (e1, Left $ t1 `merge` t2) : m ms1 ms2
           (_,_) -> (e1,mt1) : (e2,mt2) : m ms1 ms2
 
+-- | Inserts an 'Expr' into a 'Triexpr'.
 insert :: Expr -> a -> Triexpr a -> Triexpr a
 insert e x t  =  unit e x `merge` t
 
+-- | List all 'Expr' stored in a 'Triexpr' along with their associated values.
 toList :: Triexpr a -> [(Expr, a)]
 toList (Triexpr ms)  =  concatMap to ms
   where
   to (_, Right ex)  =  [ex]
   to (_, Left t)  =  toList t
 
+-- | Constructs a 'Triexpr' form a list of key 'Expr's and associated values.
 fromList :: [(Expr, a)] -> Triexpr a
 fromList  =  foldr (uncurry insert) empty
 
+-- | Maps a function to the stored values in a 'Triexpr'.
 map :: (a -> b) -> Triexpr a -> Triexpr b
 map f (Triexpr ms)  =  Triexpr [(ex, mapEither (map f) (mapSnd f) eth) | (ex, eth) <- ms]
   where
@@ -89,6 +100,7 @@
   mapSnd :: (a -> b) -> (c,a) -> (c,b)
   mapSnd f (x,y)  =  (x, f y)
 
+-- | Performs a lookup in a 'Triexpr'.
 lookup :: Expr -> Triexpr a -> [ (Expr, [(Expr,Expr)], a) ]
 lookup e t  =  [(e, bs, x) | (bs, Right (e,x)) <- look (Just e) t []]
   where
diff --git a/src/Data/Express/Utils.hs b/src/Data/Express/Utils.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Utils.hs
@@ -0,0 +1,46 @@
+-- |
+-- Module      : Data.Express.Utils.List
+-- Copyright   : (c) 2019-2021 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Re-exports a few standard Haskell modules module along with additional
+-- functions.
+{-# LANGUAGE CPP #-}
+module Data.Express.Utils
+  ( module Data.Express.Utils.List
+  , module Data.Express.Utils.String
+  , module Data.Monoid
+  , module Data.Maybe
+  , module Data.Either
+  , module Data.Function
+  , module Control.Monad
+#if __GLASGOW_HASKELL__ < 704
+  , (<>)
+#endif
+#if __HUGS__
+  , (>=>)
+#endif
+  )
+where
+
+import Data.Express.Utils.List
+import Data.Express.Utils.String
+import Data.Function
+import Data.Maybe
+import Data.Either
+import Data.Monoid
+import Control.Monad
+
+#if __GLASGOW_HASKELL__ < 704
+-- Data.Monoid exports <> since GHC 7.4 / base 4.5.0.0
+-- GHC 7.2 / base 4.4.1.0 / Hugs 2006.9 do not define <>
+(<>) :: Monoid m => m -> m -> m
+(<>)  =  mappend
+infixr 6 <>
+#endif
+
+#if __HUGS__
+(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
+f >=> g  =  \x -> f x >>= g
+#endif
diff --git a/src/Data/Express/Utils/List.hs b/src/Data/Express/Utils/List.hs
--- a/src/Data/Express/Utils/List.hs
+++ b/src/Data/Express/Utils/List.hs
@@ -19,6 +19,9 @@
 #if __GLASGOW_HASKELL__ < 710
   , isSubsequenceOf
 #endif
+#ifdef __HUGS__
+  , intercalate
+#endif
   )
 where
 
@@ -46,6 +49,7 @@
   nnub [x] = [x]
   nnub (x:xs) = x : nnub (dropWhile (==x) xs)
 
+-- | Like 'nubSort' but allows providing a function to 'compare' values.
 nubSortBy :: (a -> a -> Ordering) -> [a] -> [a]
 nubSortBy cmp  =  nnub . sortBy cmp
   where
@@ -100,10 +104,17 @@
 lookupId :: Eq a => a -> [(a,a)] -> a
 lookupId x = fromMaybe x . lookup x
 
+-- | Merges two lists discarding repeated elements.
+--
+-- The argument lists need to be in order.
+--
+-- > > [1,10,100] +++ [9,10,11]
+-- > [1,9,10,11,100]
 (+++) :: Ord a => [a] -> [a] -> [a]
 (+++)  =  nubMerge
 infixr 5 +++
 
+-- | Like 'nubMerge' but allows providing a function to 'compare' values.
 nubMergeBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
 nubMergeBy cmp (x:xs) (y:ys)  =  case x `cmp` y of
                                  LT -> x:nubMergeBy cmp xs (y:ys)
@@ -111,5 +122,21 @@
                                  EQ -> x:nubMergeBy cmp xs ys
 nubMergeBy _ xs ys  =  xs ++ ys
 
+-- | Merges two lists discarding repeated elements.
+--
+-- The argument lists need to be in order.
 nubMerge :: Ord a => [a] -> [a] -> [a]
 nubMerge  =  nubMergeBy compare
+
+#ifdef __HUGS__
+intercalate :: [a] -> [[a]] -> [a]
+intercalate xs xss  =  concat (intersperse xs xss)
+  where
+  intersperse :: a -> [a] -> [a]
+  intersperse _ []        =  []
+  intersperse sep (x:xs)  =  x : prependToAll sep xs
+    where
+    prependToAll :: a -> [a] -> [a]
+    prependToAll _   []      =  []
+    prependToAll sep (x:xs)  =  sep : x : prependToAll sep xs
+#endif
diff --git a/src/Data/Express/Utils/String.hs b/src/Data/Express/Utils/String.hs
--- a/src/Data/Express/Utils/String.hs
+++ b/src/Data/Express/Utils/String.hs
@@ -8,8 +8,7 @@
 --
 -- At some point, this file was part of the Speculate tool.
 module Data.Express.Utils.String
-  ( module Data.String
-  , module Data.Char
+  ( module Data.Char
   , unquote
   , atomic
   , outernmostPrec
@@ -22,14 +21,13 @@
   )
 where
 
-import Data.String
 import Data.Char
-import Data.Functor ((<$>)) -- for GHC < 7.10
 
 -- | Unquotes a string if possible, otherwise, this is just an identity.
 --
 -- > > unquote "\"string\""
 -- > "string"
+--
 -- > > unquote "something else"
 -- > "something else"
 unquote :: String -> String
@@ -67,12 +65,28 @@
 atomic ('(':s)  | last s == ')'  = True
 atomic _ = False
 
+-- |
+-- Returns the operator precedence of an infix string.
+--
+-- > > outernmostPrec "1 + 2"
+-- > Just 6
 outernmostPrec :: String -> Maybe Int
 outernmostPrec s =
   case words s of
     [l,o,r] | isInfix o -> Just (prec o)
     _                   -> Nothing
 
+-- |
+-- Returns whether the given 'String' represents a negative literal.
+--
+-- > > isNegativeLiteral "1"
+-- > False
+-- > > isNegativeLiteral "-1"
+-- > True
+-- > > isNegativeLiteral "-x"
+-- > False
+-- > > isNegativeLiteral "1 - 3"
+-- > False
 isNegativeLiteral :: String -> Bool
 isNegativeLiteral s | not (atomic s) = False
 isNegativeLiteral "-"                = False
@@ -124,6 +138,13 @@
 prec "<==>"    =   0
 prec _         =   9
 
+-- | Is the given string a prefix function?
+--
+-- > > isPrefix "abs"
+-- > True
+--
+-- > > isPrefix "+"
+-- > False
 isPrefix :: String -> Bool
 isPrefix = not . isInfix
 
@@ -141,6 +162,12 @@
 toPrefix ('`':cs) = init cs
 toPrefix cs = '(':cs ++ ")"
 
+-- |
+-- Cycles through a list of variable names
+-- priming them at each iteration.
+--
+-- > primeCycle ["x","y","z"]
+-- ["x","y","z","x'","y'","z'","x''","y''","z''","x'''",...]
 primeCycle :: [String] -> [String]
 primeCycle []  =  []
 primeCycle ss  =  ss ++ map (++ "'") (primeCycle ss)
@@ -165,7 +192,7 @@
   f "xyz"                        =  ["xyz", "uvw"]
   f cs    | isDigit (last cs)    =  map (\n -> init cs ++ show n) [digitToInt (last cs)..]
   f [c]   | c `elem` ['a'..'x']  =  let x = ord c in map ((:[]) . chr) [x,x+1,x+2]
-  f cs    | last cs == 's'       =  (++ "s") <$> f (init cs)
+  f cs    | last cs == 's'       =  (++ "s") `map` f (init cs)
   f [c,d] | ord d - ord c == 1   =  [[c,d], [chr $ ord c + 2, chr $ ord d + 2]]
   f cs | cs == "y" || cs == "z"  =  cs : map (\n -> cs ++ show n) [1..]
   f cs                           =  [cs]
diff --git a/src/Data/Express/Utils/TH.hs b/src/Data/Express/Utils/TH.hs
--- a/src/Data/Express/Utils/TH.hs
+++ b/src/Data/Express/Utils/TH.hs
@@ -55,6 +55,18 @@
   else
     reallyDerive t
 
+-- |
+-- Encodes a 'Name' as a 'String'.
+-- This is useful when generating error messages.
+--
+-- > > showJustName ''Int
+-- > "Int"
+--
+-- > > showJustName ''String
+-- > "String"
+--
+-- > > showJustName ''Maybe
+-- > "Maybe"
 showJustName :: Name -> String
 showJustName = reverse . takeWhile (/= '.') . reverse . show
 
@@ -99,17 +111,22 @@
   tss <- mapM (`typeConCascadingArgsThat` p') ts
   return $ nubMerges (ts:tss)
 
--- Normalizes a type by applying it to necessary type variables, making it
--- accept "zero" parameters.  The normalized type is tupled with a list of
--- necessary type variables.
+-- |
+-- Normalizes a type by applying it to necessary type variables
+-- making it accept zero type parameters.
+-- The normalized type is paired with a list of necessary type variables.
 --
--- Suppose:
+-- > > putStrLn $(stringE . show =<< normalizeType ''Int)
+-- > (ConT ''Int, [])
 --
--- > data DT a b c ... = ...
+-- > > putStrLn $(stringE . show =<< normalizeType ''Maybe)
+-- > (AppT (ConT ''Maybe) (VarT ''a),[VarT ''a])
 --
--- Then, in pseudo-TH:
+-- > > putStrLn $(stringE . show =<< normalizeType ''Either)
+-- > (AppT (AppT (ConT ''Either) (VarT ''a)) (VarT ''b),[VarT ''a,VarT ''b])
 --
--- > normalizeType [t|DT|] == Q (DT a b c ..., [a, b, c, ...])
+-- > > putStrLn $(stringE . show =<< normalizeType ''[])
+-- > (AppT (ConT ''[]) (VarT a),[VarT a])
 normalizeType :: Name -> Q (Type, [Type])
 normalizeType t = do
   ar <- typeArity t
@@ -122,35 +139,59 @@
     newVarTs n = liftM (map VarT)
                $ newNames (take n . map (:[]) $ cycle ['a'..'z'])
 
--- Normalizes a type by applying it to units (`()`) while possible.
---
--- > normalizeTypeUnits ''Int    === [t| Int |]
--- > normalizeTypeUnits ''Maybe  === [t| Maybe () |]
--- > normalizeTypeUnits ''Either === [t| Either () () |]
+-- |
+-- Normalizes a type by applying it to units to make it star-kinded.
+-- (cf. 'normalizeType')
 normalizeTypeUnits :: Name -> Q Type
 normalizeTypeUnits t = do
   ar <- typeArity t
   return (foldl AppT (ConT t) (replicate ar (TupleT 0)))
 
+-- |
 -- Given a type name and a class name,
 -- returns whether the type is an instance of that class.
+-- The given type must be star-kinded (@ * @)
+-- and the given class double-star-kinded (@ * -> * @.
+--
+-- > > putStrLn $(stringE . show =<< ''Int `isInstanceOf` ''Num)
+-- > True
+--
+-- > > putStrLn $(stringE . show =<< ''Int `isInstanceOf` ''Fractional)
+-- > False
 isInstanceOf :: Name -> Name -> Q Bool
 isInstanceOf tn cl = do
   ty <- normalizeTypeUnits tn
   isInstance cl [ty]
 
+-- |
+-- The negation of 'isInstanceOf'.
 isntInstanceOf :: Name -> Name -> Q Bool
 isntInstanceOf tn cl = liftM not (isInstanceOf tn cl)
 
 -- | Given a type name, return the number of arguments taken by that type.
 -- Examples in partially broken TH:
 --
--- > arity ''Int        === Q 0
--- > arity ''Int->Int   === Q 0
--- > arity ''Maybe      === Q 1
--- > arity ''Either     === Q 2
--- > arity ''Int->      === Q 1
+-- > > putStrLn $(stringE . show =<< typeArity ''Int)
+-- > 0
 --
+-- > > putStrLn $(stringE . show =<< typeArity ''Maybe)
+-- > 1
+--
+-- > > putStrLn $(stringE . show =<< typeArity ''Either)
+-- > 2
+--
+-- > > putStrLn $(stringE . show =<< typeArity ''[])
+-- > 1
+--
+-- > > putStrLn $(stringE . show =<< typeArity ''(,))
+-- > 2
+--
+-- > > putStrLn $(stringE . show =<< typeArity ''(,,))
+-- > 3
+--
+-- > > putStrLn $(stringE . show =<< typeArity ''String)
+-- > 0
+--
 -- This works for Data's and Newtype's and it is useful when generating
 -- typeclass instances.
 typeArity :: Name -> Q Int
@@ -168,20 +209,28 @@
     _ -> error $ "error (typeArity): symbol " ++ show t
               ++ " is not a newtype, data or type synonym"
 
--- Given a type name, returns a list of its type constructor names paired with
+-- |
+-- Given a type 'Name',
+-- returns a list of its type constructor 'Name's
+-- paired with the type arguments they take.
 -- the type arguments they take.
 --
--- > typeConstructors ''()    === Q [('(),[])]
---
--- > typeConstructors ''(,)   === Q [('(,),[VarT a, VarT b])]
+-- > > putStrLn $(stringE . show =<< typeConstructors ''Bool)
+-- > [ ('False, [])
+-- > , ('True, [])
+-- > ]
 --
--- > typeConstructors ''[]    === Q [('[],[]),('(:),[VarT a,AppT ListT (VarT a)])]
+-- > > putStrLn $(stringE . show =<< typeConstructors ''[])
+-- > [ ('[], [])
+-- > , ('(:), [VarT ''a, AppT ListT (VarT ''a)])
+-- > ]
 --
--- > data Pair a = P a a
--- > typeConstructors ''Pair  === Q [('P,[VarT a, VarT a])]
+-- > > putStrLn $(stringE . show =<< typeConstructors ''(,))
+-- > [('(,), [VarT (mkName "a"), VarT (mkName "b")])]
 --
--- > data Point = Pt Int Int
--- > typeConstructors ''Point === Q [('Pt,[ConT Int, ConT Int])]
+-- > > data Point  =  Pt Int Int
+-- > > putStrLn $(stringE . show =<< typeConstructors ''Point)
+-- > [('Pt,[ConT ''Int, ConT ''Int])]
 typeConstructors :: Name -> Q [(Name,[Type])]
 typeConstructors t = do
   ti <- reify t
@@ -201,6 +250,17 @@
   simplify (InfixC  t1 n t2) = (n,[snd t1,snd t2])
   trd (x,y,z) = z
 
+-- |
+-- Is the given 'Name' a type synonym?
+--
+-- > > putStrLn $(stringE . show =<< isTypeSynonym 'show)
+-- > False
+--
+-- > > putStrLn $(stringE . show =<< isTypeSynonym ''Char)
+-- > False
+--
+-- > > putStrLn $(stringE . show =<< isTypeSynonym ''String)
+-- > True
 isTypeSynonym :: Name -> Q Bool
 isTypeSynonym t = do
   ti <- reify t
@@ -208,6 +268,11 @@
     TyConI (TySynD _ _ _) -> True
     _                     -> False
 
+-- |
+-- Resolves a type synonym.
+--
+-- > > putStrLn $(stringE . show =<< typeSynonymType ''String)
+-- > AppT ListT (ConT ''Char)
 typeSynonymType :: Name -> Q Type
 typeSynonymType t = do
   ti <- reify t
@@ -286,6 +351,11 @@
                 return (c,ns)
            | (c,ts) <- cs ]
 
+-- | Lookups the name of a value
+--   throwing an error when it is not found.
+--
+-- > > putStrLn $(stringE . show =<< lookupValN "show")
+-- > 'show
 lookupValN :: String -> Q Name
 lookupValN s = do
   mn <- lookupValueName s
diff --git a/src/Data/Express/Utils/Typeable.hs b/src/Data/Express/Utils/Typeable.hs
--- a/src/Data/Express/Utils/Typeable.hs
+++ b/src/Data/Express/Utils/Typeable.hs
@@ -31,15 +31,23 @@
 where
 
 import Data.Typeable
-import Data.Monoid ((<>))
-import Data.Express.Utils.List
+import Data.Express.Utils
 
--- Different versions of Typeable/GHC provide different orderings for TypeReps.
--- The following is a version independent ordering, with the following
--- properties:
+-- | Compares two 'TypeRep's.
 --
+-- Different versions of Typeable/GHC
+-- provide different orderings for 'TypeRep's.
+-- The following is a version independent ordering,
+-- with the following properties:
+--
 -- * functional types with more arguments are larger;
 -- * type constructors with more arguments are larger.
+--
+-- > > typeOf (undefined :: Int -> Int) `compareTy` typeOf (undefined :: () -> () -> ())
+-- > LT
+--
+-- > > typeOf (undefined :: Int) `compareTy` typeOf (undefined :: ())
+-- > GT
 compareTy :: TypeRep -> TypeRep -> Ordering
 compareTy t1 t2 | t1 == t2 = EQ -- optional optimization
 compareTy t1 t2 = tyArity t1 `compare` tyArity t2
@@ -50,24 +58,71 @@
   (c1,ts1) = splitTyConApp t1
   (c2,ts2) = splitTyConApp t2
 
+-- | Returns the functional arity of the given 'TypeRep'.
+--
+-- > > tyArity $ typeOf (undefined :: Int)
+-- > 0
+--
+-- > > tyArity $ typeOf (undefined :: Int -> Int)
+-- > 1
+--
+-- > > tyArity $ typeOf (undefined :: (Int,Int))
+-- > 0
 tyArity :: TypeRep -> Int
 tyArity t
   | isFunTy t = 1 + tyArity (resultTy t)
   | otherwise = 0
 
+-- | Returns the ultimate result type of the given 'TypeRep'.
+--
+-- > > finalResultTy (typeOf (undefined :: Int))
+-- > Int
+--
+-- > > finalResultTy (typeOf (undefined :: Int -> Char))
+-- > Char
+--
+-- > > finalResultTy (typeOf (undefined :: Int -> Char -> Bool))
+-- > Bool
 finalResultTy :: TypeRep -> TypeRep
 finalResultTy t
   | isFunTy t = finalResultTy (resultTy t)
   | otherwise = t
 
+-- | Deconstructs a functional 'TypeRep' into a pair of 'TypeRep's.
+--
+-- > > unFunTy $ typeOf (undefined :: Int -> Char -> Bool)
+-- > (Int,Char -> Bool)
+--
+-- This function raises an error on non-functional types.
+--
+-- (cf. 'argumentTy' and 'resultTy')
 unFunTy :: TypeRep -> (TypeRep,TypeRep)
 unFunTy t
   | isFunTy t = let (f,[a,b]) = splitTyConApp t in (a,b)
   | otherwise = error $ "error (unFunTy): `" ++ show t ++ "` is not a function type"
 
+-- | Returns the argument 'TypeRep' of a given functional 'TypeRep'.
+--
+-- > argumentTy $ typeOf (undefined :: Int -> Char)
+-- > Int
+--
+-- This function raises an error on non-functional types.
+--
+-- (cf. 'resultTy')
 argumentTy :: TypeRep -> TypeRep
 argumentTy = fst . unFunTy
 
+-- | Returns the result 'TypeRep' of a given functional 'TypeRep'.
+--
+-- > > resultTy $ typeOf (undefined :: Int -> Char)
+-- > Char
+--
+-- > > resultTy $ typeOf (undefined :: Int -> Char -> Bool)
+-- > Char -> Bool
+--
+-- This function raises an error on non-functional types.
+--
+-- (cf. 'argumentTy' and 'finalResultTy')
 resultTy :: TypeRep -> TypeRep
 resultTy = snd . unFunTy
 
@@ -87,21 +142,32 @@
   | isListTy t = let (_,[a]) = splitTyConApp t in a
   | otherwise = error $ "error (elementTy): `" ++ show t ++ "' is not a list type"
 
+-- | The 'Bool' type encoded as a 'TypeRep'.
 boolTy :: TypeRep
 boolTy = typeOf (undefined :: Bool)
 
+-- | The 'Int' type encoded as a 'TypeRep'.
 intTy :: TypeRep
 intTy = typeOf (undefined :: Int)
 
+-- | The 'Ordering' type encoded as a 'TypeRep'.
 orderingTy :: TypeRep
 orderingTy = typeOf (undefined :: Ordering)
 
+-- | The function type constructor as a 'TyCon'
 funTyCon :: TyCon
 funTyCon = typeRepTyCon $ typeOf (undefined :: () -> ())
 
+-- | The list type constructor as a 'TyCon'
 listTyCon :: TyCon
 listTyCon = typeRepTyCon $ typeOf (undefined :: [()])
 
+-- | Returns whether a 'TypeRep' is functional.
+--
+-- > > isFunTy $ typeOf (undefined :: Int -> Int)
+-- > True
+-- > > isFunTy $ typeOf (undefined :: Int)
+-- > False
 isFunTy :: TypeRep -> Bool
 isFunTy t =
   case splitTyConApp t of
@@ -113,14 +179,47 @@
   (con,[_]) | con == listTyCon -> True
   _ -> False
 
+-- | Return the number of outer list nestings in a 'TypeRep'
+--
+-- > > countListTy $ typeOf (undefined :: Int)
+-- > 0
+--
+-- > > countListTy $ typeOf (undefined :: [Bool])
+-- > 1
+--
+-- > > countListTy $ typeOf (undefined :: [[()]])
+-- > 2
+--
+-- > > countListTy $ typeOf (undefined :: String)
+-- > 1
+--
+-- > > countListTy $ typeOf (undefined :: ([Int],[Bool]))
+-- > 0
 countListTy :: TypeRep -> Int
 countListTy t  =  case splitTyConApp t of
   (con,[t']) | con == listTyCon -> 1 + countListTy t'
   _ -> 0
 
+-- | Constructs a comparison type (@ a -> a -> Bool @)
+--   from the given argument type.
+--
+-- > > mkComparisonTy $ typeOf (undefined :: Int)
+-- > Int -> Int -> Bool
+--
+-- > > mkComparisonTy $ typeOf (undefined :: ())
+-- > () -> () -> Bool
 mkComparisonTy :: TypeRep -> TypeRep
 mkComparisonTy a = a ->:: a ->:: boolTy
 
+-- | Constructs a "compare" type (@ a -> a -> Ordering @)
+--   from the given argument type.
+--
+-- > > mkCompareTy $ typeOf (undefined :: Int)
+-- > Int -> Int -> Ordering
+--
+-- > > mkCompareTy $ typeOf (undefined :: ())
+-- > () -> () -> Ordering
+
 mkCompareTy :: TypeRep -> TypeRep
 mkCompareTy a = a ->:: a ->:: orderingTy
 
@@ -158,6 +257,13 @@
 typesIn :: TypeRep -> [TypeRep]
 typesIn t  =  typesInList [t]
 
+-- | Returns types and subtypes from the given list of 'TypeRep's.
+--
+-- > > typesInList [typeOf (undefined :: () -> Int), typeOf (undefined :: String -> String -> Bool)]
+-- > [(),Bool,Char,Int,[Char],() -> Int,[Char] -> Bool,[Char] -> [Char] -> Bool]
+--
+-- > > typesInList [typeOf (undefined :: (Char,Int))]
+-- > [Char,Int,(Char,Int)]
 typesInList :: [TypeRep] -> [TypeRep]
 typesInList ts  =  nubSortBy compareTy $ tins ts []
   where
diff --git a/test/Test/ListableExpr.hs b/test/Test/ListableExpr.hs
--- a/test/Test/ListableExpr.hs
+++ b/test/Test/ListableExpr.hs
@@ -50,8 +50,6 @@
   )
 where
 
--- TODO: StringE
-
 import Test.LeanCheck
 import Test.LeanCheck.Function.ShowFunction
 import Data.Express.Fixtures
diff --git a/test/typecheck.hs b/test/typecheck.hs
new file mode 100644
--- /dev/null
+++ b/test/typecheck.hs
@@ -0,0 +1,19 @@
+-- test/typecheck.hs: imports Express and does nothing
+--
+-- to be used for triggering typechecking using runhugs
+--
+-- Copyright (c) 2019-2021 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Data.Express.Core
+import Data.Express.Hole
+import Data.Express.Map
+import Data.Express.Match
+
+import Data.Express.Name
+import Data.Express.Triexpr
+
+import Data.Express.Utils
+import Data.Express.Utils.Typeable
+
+main :: IO ()
+main  =  return ()
