diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,46 @@
+.cabal-sandbox
+cabal.sandbox.config
+.stack-work
+idx/
+dist/
+log/
+*.swp
+*.swo
+*.swn
+*.swm
+*.o
+*.hi
+*.dyn_hi
+*.dyn_o
+TAGS
+tags
+eg/u-extrapolate
+eg/u-speculate
+bench/compare
+bench/pairs
+bench/tiers
+test/canon
+test/core
+test/express
+test/express-derive
+test/fixtures
+test/hole
+test/instances
+test/listable
+test/main
+test/map
+test/match
+test/name
+test/name-derive
+test/utils
+test/fold
+test/ord
+test/show
+doc/**/*.html
+doc/**/*.css
+doc/**/*.js
+doc/**/*.png
+doc/**/*.gif
+doc/**/*.json
+README.html
+mk/toplibs
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,79 @@
+# .travis.yml file for Express
+#
+# Copyright:   (c) 2017-2019 Rudy Matela
+# License:     3-Clause BSD  (see the file LICENSE)
+# Maintainer:  Rudy Matela <rudy@matela.com.br>
+
+language: c  # not really
+
+notifications:
+  email:
+    on_failure: change
+
+sudo: false
+
+cache:
+  directories:
+  - $HOME/.ghc
+  - $HOME/.cabal
+  - $HOME/.stack
+
+before_install:
+- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
+- ghc --version
+- cabal --version
+- rm -f ~/.cabal/config && cabal update
+# Download and unpack the stack executable
+# "Once Travis whitelists the stack.dev files," simply include stack in the
+# addons section. -- https://docs.haskellstack.org/en/stable/travis_ci/
+- mkdir -p ~/.local/bin
+- export PATH=$HOME/.local/bin:$PATH
+- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
+- stack config set system-ghc --global true
+- stack --version
+- cabal install leancheck
+
+script:
+- make && make test
+- make haddock
+- make test-sdist
+- cabal test main --ghc-option=-O0
+- stack $STACKR --no-terminal --skip-ghc-check test express:test:main --ghc-options=-O0
+
+matrix:
+  allow_failures:
+  - ghc: 'head'
+  include:
+  - ghc: 'head'
+    env:                   GHCVER=head         CABALVER=head
+    addons: {apt: {packages: [ghc-head,   cabal-install-head], sources: hvr-ghc}}
+  - ghc: '8.6'
+    env:                   GHCVER=8.6.1        CABALVER=2.4
+    addons: {apt: {packages: [ghc-8.6.1,  cabal-install-2.4],  sources: hvr-ghc}}
+  - ghc: '8.4'
+    env:                   GHCVER=8.4.4        CABALVER=2.2
+    addons: {apt: {packages: [ghc-8.4.4,  cabal-install-2.2],  sources: hvr-ghc}}
+  - ghc: '8.2'
+    env:                   GHCVER=8.2.2        CABALVER=2.0
+    addons: {apt: {packages: [ghc-8.2.2,  cabal-install-2.0],  sources: hvr-ghc}}
+  - ghc: '8.0'
+    env:                   GHCVER=8.0.2        CABALVER=1.24
+    addons: {apt: {packages: [ghc-8.0.2,  cabal-install-1.24], sources: hvr-ghc}}
+  - ghc: '7.10'
+    env:                   GHCVER=7.10.3       CABALVER=1.22   STACK_YAML=stack-lts-11.yaml
+    addons: {apt: {packages: [ghc-7.10.3, cabal-install-1.22], sources: hvr-ghc}}
+  # we only support stack with GHC >= 7.10
+  - ghc: '7.8'
+    env:                   GHCVER=7.8.4        CABALVER=1.18
+    addons: {apt: {packages: [ghc-7.8.4,  cabal-install-1.18], sources: hvr-ghc}}
+    before_install:
+    - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
+    - ghc --version
+    - cabal --version
+    - rm -f ~/.cabal/config && cabal update
+    - cabal install Cabal==1.18.*
+    - cabal install leancheck
+    script:
+    - make && make test
+    - make haddock HADDOCKFLAGS=
+    - cabal test
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2019, Rudy Matela
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Rudy Matela nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,131 @@
+# Makefile for express
+#
+# Copyright:   (c) 2015-2018 Rudy Matela
+# License:     3-Clause BSD  (see the file LICENSE)
+# Maintainer:  Rudy Matela <rudy@matela.com.br>
+TESTS = \
+  test/main \
+  test/core \
+  test/map \
+  test/instances \
+  test/fixtures \
+  test/express \
+  test/express-derive \
+  test/name \
+  test/name-derive \
+  test/utils \
+  test/canon \
+  test/match \
+  test/hole \
+  test/fold \
+  test/show \
+  test/ord \
+  test/listable
+EGS =
+BENCHS = \
+  eg/u-extrapolate \
+  eg/u-speculate \
+  bench/compare \
+  bench/pairs \
+  bench/tiers \
+  $(EGS)
+GHCIMPORTDIRS = src:test
+GHCFLAGS = -O2 $(shell grep -q "Arch Linux" /etc/lsb-release && echo -dynamic)
+HADDOCKFLAGS = --no-print-missing-docs
+RUNPARAMETERS =
+LIB_DEPS = base template-haskell
+
+all: mk/toplibs
+
+all-all: mk/All.o
+
+test: $(patsubst %,%.run,$(TESTS)) test-sdist diff-test
+
+slow-test: RUNPARAMETERS=50400
+slow-test: test
+
+%.run: %
+	./$< $(RUNPARAMETERS)
+
+.PHONY: bench
+bench: $(patsubst %,%.bench,$(BENCHS))
+	@mkdir -p bench/runtime/$$HOSTNAME
+	./bench/versions | tee bench/runtime/$$HOSTNAME/versions
+
+.PHONY: %.bench
+%.bench: %
+	@mkdir -p bench/runtime/$$HOSTNAME/$<
+	@printf "%-18s " $<
+	@/usr/bin/time -f%e ./$< 2>&1 >/dev/null | tee bench/runtime/$$HOSTNAME/$<.runtime
+
+clean: clean-hi-o clean-haddock
+	rm -f $(TESTS) $(BENCHS) $(EGS) mk/toplibs
+
+ghci: mk/All.ghci
+
+install:
+	@echo "use \`cabal install' instead"
+
+test-sdist:
+	./test/sdist
+
+test-via-cabal:
+	cabal test
+
+test-via-stack:
+	stack test
+
+diff-test: $(patsubst %,%.diff-test,$(BENCHS))
+
+update-diff-test: $(patsubst %,%.update-diff-test,$(BENCHS))
+
+%.diff-test: %
+	./$< | diff -rud test/model/$<.out -
+
+%.update-diff-test: %
+	./$< >           test/model/$<.out
+
+test-via-everything: test test-via-cabal test-via-stack
+
+legacy-test: # needs ghc-8.0 .. ghc-7.8 installed as such
+	make clean  &&  make test GHC=ghc-8.2  GHCFLAGS="-Werror -dynamic"
+	make clean  &&  make test GHC=ghc-8.0  GHCFLAGS="-Werror -dynamic"
+	make clean  &&  make test GHC=ghc-7.10 GHCFLAGS="-Werror -dynamic"
+	make clean  &&  make test GHC=ghc-7.8  GHCFLAGS="-Werror -dynamic"
+	make clean  &&  make test
+
+legacy-test-via-cabal: # needs similarly named cabal wrappers
+	cabal clean  &&  cabal-ghc-8.2  configure  &&  cabal-ghc-8.2  test
+	cabal clean  &&  cabal-ghc-8.0  configure  &&  cabal-ghc-8.0  test
+	cabal clean  &&  cabal-ghc-7.10 configure  &&  cabal-ghc-7.10 test
+	cabal clean  &&  cabal-ghc-7.8  configure  &&  cabal-ghc-7.8  test
+	cabal clean  &&  cabal test
+
+prepare:
+	cabal update  &&  cabal install leancheck
+
+prepare-legacy-test:
+	cabal-ghc-8.2  update  &&  cabal-ghc-8.2  install leancheck
+	cabal-ghc-8.0  update  &&  cabal-ghc-8.0  install leancheck
+	cabal-ghc-7.10 update  &&  cabal-ghc-7.10 install leancheck
+	cabal-ghc-7.8  update  &&  cabal-ghc-7.8  install leancheck
+
+hlint:
+	hlint \
+	  --ignore "Use import/export shortcut" \
+	  --ignore "Redundant bracket" \
+	  .
+
+markdown: README.html
+
+%.html: %.md
+	pandoc $< -o $@
+
+# NOTE: (very hacky!) the following target allows parallel compilation (-jN) of
+# eg and test programs so long as they don't share dependencies _not_ stored
+# in src/ and test/.  Runnable binaries should depend on mk/toplibs instead of
+# actual Haskell source files
+mk/toplibs: mk/Toplibs.o
+	touch mk/toplibs
+
+include mk/haskell.mk
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,316 @@
+Express
+=======
+
+[![Express's Build Status][build-status]][build-log]
+[![Express on Hackage][hackage-version]][express-on-hackage]
+[![Express on Stackage LTS][stackage-lts-badge]][express-on-stackage-lts]
+[![Express on Stackage Nightly][stackage-nightly-badge]][express-on-stackage-nightly]
+
+![Express logo][express-logo]
+
+
+Express is a library for manipulating dynamically typed Haskell expressions.
+It's like [`Data.Dynamic`] but with support for encoding applications and
+variables.
+
+It provides the [`Expr`] type and over a hundred functions for
+building, evaluating, comparing, folding, canonicalizing and matching
+[`Expr`]s.  See [Express's Haddock documentation] for more details.
+
+This library has been used successfully in the implementation of
+[Speculate] and [Extrapolate].
+
+
+Installing
+----------
+
+To install the latest Express version from Hackage, just run:
+
+	$ cabal update
+	$ cabal install express
+
+
+Example 1: heterogeneous lists
+------------------------------
+
+Like with [`Data.Dynamic`], we can use Express to create heterogeneous lists.
+
+For types that are [`Show`] instances, we can use [`val`] to encode values as [`Expr`]s.
+Here, we use applications of [`val`] to create a heterogeneous list:
+
+	> let xs = [val False, val True, val (1::Int), val (2::Int), val (3::Integer), val "123"]
+	> :t xs
+	xs :: [Expr]
+	> xs
+	[ False :: Bool
+	, True :: Bool
+	, 1 :: Int
+	, 2 :: Int
+	, 3 :: Integer
+	, "123" :: [Char]
+	]
+
+We can then apply [`evaluate`] to select values of different types:
+
+	> import Data.Maybe
+	> mapMaybe evaluate xs :: [Bool]
+	[False,True]
+	> mapMaybe evaluate xs :: [Int]
+	[1,2]
+	> mapMaybe evaluate xs :: [Integer]
+	[3]
+	> mapMaybe evaluate xs :: [String]
+	["123"]
+
+
+Example 2: listing applications
+-------------------------------
+
+Carrying on from Example 1, we define an heterogeneous list of functions
+encoded as [`Expr`]s:
+
+	> let fs = [value "not" not, value "&&" (&&), value "abs" (abs :: Int -> Int)]
+	> :t fs
+	fs :: [Expr]
+
+Using [`$$`] we list the type correct applications of functions in `fs` to
+values in `xs`.
+
+	> catMaybes [f $$ x | f <- fs, x <- xs]
+	[ not False :: Bool
+	, not True :: Bool
+	, (False &&) :: Bool -> Bool
+	, (True &&) :: Bool -> Bool
+	, abs 1 :: Int
+	, abs 2 :: Int
+	]
+
+Example 3: u-Extrapolate
+------------------------
+
+[u-Extrapolate] is a property-based testing property-based testing library
+capable of generalizing counter-examples.  It's implementation has under 40
+lines of code.  Besides, using Express to encode expressions, it uses
+[LeanCheck] for generating test values.
+
+	import Data.Express
+	import Test.LeanCheck hiding (counterExample, check)
+
+Given a maximum number of tests and a property, the following `counterExample`
+function returns either `Nothing` when tests pass or `Just` a counterexample
+encoded as an [`Expr`].
+
+	counterExample :: (Listable a, Express a) => Int -> (a -> Bool) -> Maybe Expr
+	counterExample maxTests prop  =  listToMaybe
+	  [expr x | x <- take maxTests list, not (prop x)]
+
+Examples (REPL):
+
+	> counterExample 100 (\(x,y) -> x + y == y + x)
+	Nothing
+	> counterExample 100 (\x -> x == x + x)
+	Just (1 :: Integer)
+	> counterExample 100 (\xs -> nub xs == (xs :: [Int]))
+	Just ([0,0] :: [Int])
+
+Before moving on to generalize counterexamples, we need a way to compute ground
+expressions from an expression with variables.  For that, we will use `grounds`
+and `tiersFor`:
+
+	grounds :: Expr -> [Expr]
+	grounds e  =  map (e //-)
+	           .  concat
+	           $  products [mapT ((,) v) (tiersFor v) | v <- nubVars e]
+
+	tiersFor :: Expr -> [[Expr]]
+	tiersFor e  =  case show (typ e) of
+	  "Int"    ->  mapT val (tiers `asTypeOf` [[undefined :: Int]])
+	  "Bool"   ->  mapT val (tiers `asTypeOf` [[undefined :: Bool]])
+	  "[Int]"  ->  mapT val (tiers `asTypeOf` [[undefined :: [Int]]])
+	  "[Bool]" ->  mapT val (tiers `asTypeOf` [[undefined :: [Bool]]])
+	  _        ->  []
+
+Above, we restrict ourselves to `Int`, `Bool`, `[Int]` and `[Bool]` as test
+types.  So we can now compute the grounds of an expression with variables:
+
+	> grounds (value "not" not :$ var "p" (undefined :: Bool))
+	[ not False :: Bool
+	, not True :: Bool
+	]
+	> grounds (value "&&" (&&) :$ var "p" (undefined :: Bool) :$ var "q" (undefined :: Bool))
+	[ False && False :: Bool
+	, False && True :: Bool
+	, True && False :: Bool
+	, True && True :: Bool
+	]
+
+To compute candidate generalizations from a given counter-example, we use the
+following function:
+
+	candidateGeneralizations :: Expr -> [Expr]
+	candidateGeneralizations  =  map canonicalize
+	                          .  concatMap canonicalVariations
+	                          .  gen
+	  where
+	  gen e@(e1 :$ e2)  =
+	    [holeAsTypeOf e | isListable e]
+	    ++ [g1 :$ g2 | g1 <- gen e1, g2 <- gen e2]
+	    ++ map (:$ e2) (gen e1)
+	    ++ map (e1 :$) (gen e2)
+	  gen e
+	    | isVar e    =  []
+	    | otherwise  =  [holeAsTypeOf e | isListable e]
+	  isListable  =  not . null . tiersFor
+
+The need for `isListable` above makes sure we only replace by variables what we
+can enumerate.  Our candidate generalizations are listed in non-increasing
+order of generality:
+
+	> candidateGeneralizations (value "not" not :$ val False)
+	[ p :: Bool
+	, not p :: Bool
+	]
+	Prelude> candidateGeneralizations (value "||" (||) :$ val False :$ val True)
+	[ p :: Bool
+	, p || q :: Bool
+	, p || p :: Bool
+	, p || True :: Bool
+	, False || p :: Bool
+	]
+
+For a given maximum number of tests, property and counter-example, the
+following function returns a counter-example generalization if one is found.
+It goes through the list of candidate generalizations and returns the first for
+which all tests _fail_.
+
+	counterExampleGeneralization :: Express a => Int -> (a -> Bool) -> Expr -> Maybe Expr
+	counterExampleGeneralization maxTests prop e  =  listToMaybe
+	  [g | g <- candidateGeneralizations e
+	     , all (not . prop . evl) (take maxTests $ grounds g)]
+
+We can finally define our `check` function, that will test a property and
+report a counterexample and a generalization when either are found.
+
+	check :: (Listable a, Express a) => (a -> Bool) -> IO ()
+	check prop  =  putStrLn $ case counterExample 500 prop of
+	  Nothing -> "+++ Tests passed.\n"
+	  Just ce -> "*** Falsified, counterexample:  " ++ show ce
+	          ++ case counterExampleGeneralization 500 prop ce of
+	             Nothing -> ""
+	             Just g -> "\n               generalization:  " ++ show g
+	          ++ "\n"
+
+Now we can find counterexamples and their generalizations:
+
+	> check $ \xs -> sort (sort xs :: [Int]) == sort xs
+	+++ Tests passed.
+
+	> check $ \xs -> length (nub xs :: [Int]) == length xs
+	*** Falsified, counterexample:  [0,0] :: [Int]
+	               generalization:  x:x:xs :: [Int]
+
+	> check $ \x -> x == x + (1 :: Int)
+	*** Falsified, counterexample:  0 :: Int
+	               generalization:  x :: Int
+
+	> check $ \(x,y) -> x /= (y :: Int)
+	*** Falsified, counterexample:  (0,0) :: (Int,Int)
+	               generalization:  (x,x) :: (Int,Int)
+
+[u-Extrapolate] has some limitations:
+
+* it only supports properties with one argument (uncurried);
+* it only supports generalization of `Int`, `Bool`, `[Int]` and `[Bool]` values;
+* there is no way to configure the number of test arguments.
+
+Please see [Extrapolate] for a full-featured version without the above
+limitations and with support for conditional generalizations.
+
+
+Example 4: u-Speculate
+----------------------
+
+Using Express, it takes less than 70 lines of code to define a function
+`speculateAbout` that conjectures equations about a set of functions based on
+the results of testing:
+
+	> speculateAbout [hole (undefined :: Bool), val False, val True, value "not" not]
+	[ not False == True :: Bool
+	, not True == False :: Bool
+	, not (not p) == p :: Bool
+	]
+
+	> speculateAbout
+    >   [ hole (undefined :: Int)
+    >   , hole (undefined :: [Int])
+    >   , val ([] :: [Int])
+    >   , value ":" ((:) :: Int -> [Int] -> [Int])
+    >   , value "++" ((++) :: [Int] -> [Int] -> [Int])
+    >   , value "sort" (sort :: [Int] -> [Int])
+    >   ]
+	[ sort [] == [] :: Bool
+    , xs ++ [] == xs :: Bool
+    , [] ++ xs == xs :: Bool
+    , sort (sort xs) == sort xs :: Bool
+    , sort [x] == [x] :: Bool
+    , [x] ++ xs == x:xs :: Bool
+    , sort (xs ++ ys) == sort (ys ++ xs) :: Bool
+    , sort (x:sort xs) == sort (x:xs) :: Bool
+    , sort (xs ++ sort ys) == sort (xs ++ ys) :: Bool
+    , sort (sort xs ++ ys) == sort (xs ++ ys) :: Bool
+    , (x:xs) ++ ys == x:(xs ++ ys) :: Bool
+    , (xs ++ ys) ++ zs == xs ++ (ys ++ zs) :: Bool
+	]
+
+Please see the [u-Speculate] example in the [eg](eg) folder for the full code
+of `speculateAbout`.
+
+[u-Speculate] has some limitations:
+
+* it sometimes prints redundant equations;
+* although it usually runs quickly with less than 6 symbols,
+  runtime is exponential with the number of symbols given,
+  providing it with more than a dozen symbols can make it run for several
+  minutes or hours;
+* there is no way to configure the size limit of reported equations;
+* it only supports variables of `Int`, `Bool`, `[Int]`, and `[Bool]` types.
+
+Please see [Speculate] for a full-featured version without the above
+limitations.
+
+
+Further reading
+---------------
+
+For a detailed documentation, please see [Express's Haddock documentation].
+
+For more examples, see the [eg](eg) and [bench](bench) folders.
+
+
+[Express's Haddock documentation]: https://hackage.haskell.org/package/express/docs/Data-Express.html
+
+[`Expr`]:         https://hackage.haskell.org/package/express/docs/Data-Express.html#t:val
+[`val`]:          https://hackage.haskell.org/package/express/docs/Data-Express.html#v:val
+[`evaluate`]:     https://hackage.haskell.org/package/express/docs/Data-Express.html#v:evaluate
+[`$$`]:           https://hackage.haskell.org/package/express/docs/Data-Express.html#v:-36--36-
+[`Show`]:         https://hackage.haskell.org/package/base/docs/Prelude.html#t:Show
+[`Data.Dynamic`]: https://hackage.haskell.org/package/base/docs/Data-Dynamic.html
+
+[LeanCheck]:   https://hackage.haskell.org/package/leancheck
+[Extrapolate]: https://hackage.haskell.org/package/extrapolate
+[Speculate]:   https://hackage.haskell.org/package/speculate
+
+[u-Speculate]:   eg/u-speculate.hs
+[u-Extrapolate]: eg/u-extrapolate.hs
+
+[express-logo]: https://github.com/rudymatela/express/raw/master/doc/express.svg?sanitize=true
+
+[build-status]: https://travis-ci.org/rudymatela/express.svg?branch=master
+[build-log]:    https://travis-ci.org/rudymatela/express
+[hackage-version]: https://img.shields.io/hackage/v/express.svg
+[express-on-hackage]: https://hackage.haskell.org/package/express
+[stackage-lts-badge]:          https://stackage.org/package/express/badge/lts
+[stackage-nightly-badge]:      https://stackage.org/package/express/badge/nightly
+[express-on-stackage]:         https://stackage.org/package/express
+[express-on-stackage-lts]:     https://stackage.org/lts/package/express
+[express-on-stackage-nightly]: https://stackage.org/nightly/package/express
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/TODO.md b/TODO.md
new file mode 100644
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,20 @@
+TO DO list for express
+======================
+
+Here is a list of things TO DO for express:
+
+* release on Hackage -- this should coincide with a new release for both
+  Speculate and Extrapolate.
+
+
+* review `Data.Express` haddock
+
+* document the `Instances` module
+
+* test the `Instances` module
+
+* sweep occurrences of `TODO:` throughout source files
+
+* Document `test/Test.ListableExpr` module thoroughly;
+
+
diff --git a/bench/compare.hs b/bench/compare.hs
new file mode 100644
--- /dev/null
+++ b/bench/compare.hs
@@ -0,0 +1,22 @@
+-- ord.hs -- prints results of the Ord Expr's instance
+--
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+import System.Environment (getArgs)
+
+main :: IO ()
+main  =  do
+  as <- getArgs
+  let n = case as of
+          [] -> 1000
+          (n:_) -> read n
+  putStrLn . unlines . map showCompare $ take n (list :: [(Expr,Expr)])
+  where
+  showCompare (e1,e2) = concat
+    [ show e1
+    , " `compare` "
+    , show e2
+    , "  =  "
+    , show $ e1 `compare` e2
+    ]
diff --git a/bench/pairs.hs b/bench/pairs.hs
new file mode 100644
--- /dev/null
+++ b/bench/pairs.hs
@@ -0,0 +1,14 @@
+-- pairs.hs -- a thousand pairs of expressions
+--
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+import System.Environment (getArgs)
+
+main :: IO ()
+main  =  do
+  as <- getArgs
+  let n = case as of
+          [] -> 1000
+          (n:_) -> read n
+  putStrLn . unlines . map show $ take n (list :: [(Expr,Expr)])
diff --git a/bench/runtime/zero/bench/compare.runtime b/bench/runtime/zero/bench/compare.runtime
new file mode 100644
--- /dev/null
+++ b/bench/runtime/zero/bench/compare.runtime
@@ -0,0 +1,1 @@
+0.01
diff --git a/bench/runtime/zero/bench/ord.runtime b/bench/runtime/zero/bench/ord.runtime
new file mode 100644
--- /dev/null
+++ b/bench/runtime/zero/bench/ord.runtime
@@ -0,0 +1,1 @@
+0.00
diff --git a/bench/runtime/zero/bench/pairs.runtime b/bench/runtime/zero/bench/pairs.runtime
new file mode 100644
--- /dev/null
+++ b/bench/runtime/zero/bench/pairs.runtime
@@ -0,0 +1,1 @@
+0.01
diff --git a/bench/runtime/zero/bench/tiers.runtime b/bench/runtime/zero/bench/tiers.runtime
new file mode 100644
--- /dev/null
+++ b/bench/runtime/zero/bench/tiers.runtime
@@ -0,0 +1,1 @@
+0.21
diff --git a/bench/runtime/zero/eg/u-extrapolate.runtime b/bench/runtime/zero/eg/u-extrapolate.runtime
new file mode 100644
--- /dev/null
+++ b/bench/runtime/zero/eg/u-extrapolate.runtime
@@ -0,0 +1,1 @@
+0.01
diff --git a/bench/runtime/zero/eg/u-speculate.runtime b/bench/runtime/zero/eg/u-speculate.runtime
new file mode 100644
--- /dev/null
+++ b/bench/runtime/zero/eg/u-speculate.runtime
@@ -0,0 +1,1 @@
+0.33
diff --git a/bench/runtime/zero/versions b/bench/runtime/zero/versions
new file mode 100644
--- /dev/null
+++ b/bench/runtime/zero/versions
@@ -0,0 +1,1 @@
+GHC 8.6.5
diff --git a/bench/tiers.hs b/bench/tiers.hs
new file mode 100644
--- /dev/null
+++ b/bench/tiers.hs
@@ -0,0 +1,27 @@
+-- tiers.hs -- prints tiers of expressions
+--
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+import Data.List (intercalate, nub)
+import Test.LeanCheck.Tiers (showTiers)
+import Data.Express.Utils.List
+
+showDotsLongerThan :: Show a => Int -> [a] -> String
+showDotsLongerThan n xs = "["
+                       ++ intercalate "," (dotsLongerThan n $ map show xs)
+                       ++ "]"
+  where
+  dotsLongerThan n xs = take n xs ++ ["..." | not . null $ drop n xs]
+
+printTiers :: Show a => Int -> [[a]] -> IO ()
+printTiers n = putStrLn . init . unlines . map ("  " ++) . lines . showTiers n
+
+main :: IO ()
+main  =  do
+  putStrLn $ "isNub (list :: [Expr])  =  "
+          ++ show (isNub (take 5040 list :: [Expr]))
+  putStrLn $ "map length (tiers :: [[ Expr ]])  =  "
+          ++ showDotsLongerThan 9 (map length (tiers :: [[Expr]]))
+  putStrLn $ "tiers :: [[ Expr ]]  ="
+  printTiers 7 (tiers :: [[Expr]])
diff --git a/bench/versions b/bench/versions
new file mode 100644
--- /dev/null
+++ b/bench/versions
@@ -0,0 +1,5 @@
+#!/bin/bash
+get-ghc-v() {
+    ghc --version | sed -e "s/.* version/GHC/"
+}
+get-ghc-v
diff --git a/doc/express.svg b/doc/express.svg
new file mode 100644
--- /dev/null
+++ b/doc/express.svg
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="75mm"
+   height="60mm"
+   viewBox="0 0 74.999998 60.000001"
+   version="1.1"
+   id="svg857"
+   inkscape:version="0.92.4 5da689c313, 2019-01-14"
+   sodipodi:docname="express.svg">
+  <title
+     id="title867">Express</title>
+  <defs
+     id="defs851">
+    <clipPath
+       id="clip1">
+      <path
+         inkscape:connector-curvature="0"
+         d="M 0,340.15625 H 481.89062 V 0 H 0 Z m 0,0"
+         id="path2" />
+    </clipPath>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.70710678"
+     inkscape:cx="-293.54885"
+     inkscape:cy="190.13572"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1630"
+     inkscape:window-height="1063"
+     inkscape:window-x="1366"
+     inkscape:window-y="15"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata854">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Express</dc:title>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
+        <dc:date>2019-08-03</dc:date>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Rudy Matela</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source>https://github.com/rudymatela/express</dc:source>
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>Express</rdf:li>
+            <rdf:li>Haskell</rdf:li>
+            <rdf:li>logo</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+        <dc:description>Express's logo.  An X is seen in blue intersected with a lambda sign.
+
+The proportions are based on the beautiful proportions of the Thompson-Wheeler Haskell logo (2009).</dc:description>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#Reproduction" />
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#Distribution" />
+        <cc:requires
+           rdf:resource="http://creativecommons.org/ns#Notice" />
+        <cc:requires
+           rdf:resource="http://creativecommons.org/ns#Attribution" />
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://creativecommons.org/ns#ShareAlike" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(277.11863,-172.26764)">
+    <path
+       style="clip-rule:nonzero;fill:#7f99b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.17638953"
+       d="m -217.11863,172.26764 -39.99977,59.99992 h 14.99992 l 39.99977,-59.99992 z"
+       id="path9-3"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path9"
+       d="m -257.1184,232.26764 20.00023,-30 -20.00023,-30 h 15 l 39.99977,60 h -15 l -12.49954,-18.74965 -12.50023,18.74965 z m 0,0"
+       style="clip-rule:nonzero;fill:#4c6680;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.17638952" />
+    <path
+       style="clip-rule:nonzero;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1763895"
+       d="m -277.11863,232.26764 20.00023,-30 -20.00023,-30 h 15 l 20.00023,30 -20.00023,30 z m 0,0"
+       id="path7"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/eg/u-extrapolate.hs b/eg/u-extrapolate.hs
new file mode 100644
--- /dev/null
+++ b/eg/u-extrapolate.hs
@@ -0,0 +1,101 @@
+-- u-extrapolate.hs -- micro Extrapolate / Extrapolite
+--
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+--
+-- A small property-based testing library capable of generalizing
+-- counterexamples implemented in under 50 lines of code.
+--
+-- This example works like other property-based testing libraries
+-- like QuickCheck, LeanCheck or SmallCheck:
+--
+-- When given a property, it will test it for 500 test arguments.
+-- If no counterexample is found, "tests passed" is reported.
+-- If a counterexample is found, it is reported.
+--
+-- However, when a counterexample is found, this program will try to generalize
+-- it by replacing subexpressions to variables.  If a generalization that
+-- _fails_ 500 tests is found, it is reported.
+--
+-- Limitations:
+--
+-- * this only supports properties with one argument (uncurried).
+-- * this only supports generalization of Int, Bool, [Int] and [Bool] values.
+-- * there is no way to configure the number of test arguments.
+--
+-- Please see Extrapolate for a full-featured version:
+--
+--   https://github.com/rudymatela/extrapolate
+import Data.List
+import Data.Maybe
+import Data.Express
+import Test.LeanCheck hiding (counterExample, check)
+
+main :: IO ()
+main  =  do
+  putStrLn "sort . sort = sort"
+  check $ \xs -> sort (sort xs :: [Int]) == sort xs
+
+  putStrLn "length . nub = length  (incorrect when there are repeated elements)"
+  check $ \xs -> length (nub xs :: [Int]) == length xs
+
+  putStrLn "\\(x,y) -> x + y == y + x"
+  check $ \(x,y) -> x + y == y + (x :: Int)
+
+  putStrLn "\\x -> x == x + 1  (always incorrect)"
+  check $ \x -> x == x + (1 :: Int)
+
+  putStrLn "\\(x,y) -> x + y == x + x  (incorrect)"
+  check $ \(x,y) -> x + y == x + (x :: Int)
+
+  putStrLn "\\(x,y) -> x /= y  (incorrect whenever x and y are equal)"
+  check $ \(x,y) -> x /= (y :: Int)
+
+
+check :: (Listable a, Express a) => (a -> Bool) -> IO ()
+check prop  =  putStrLn $ case counterExample 500 prop of
+  Nothing -> "+++ Tests passed.\n"
+  Just ce -> "*** Falsified, counterexample:  " ++ show ce
+          ++ case counterExampleGeneralization 500 prop ce of
+             Nothing -> ""
+             Just g -> "\n               generalization:  " ++ show g
+          ++ "\n"
+
+
+counterExample :: (Listable a, Express a) => Int -> (a -> Bool) -> Maybe Expr
+counterExample maxTests prop  =  listToMaybe
+  [expr x | x <- take maxTests list, not (prop x)]
+
+counterExampleGeneralization :: Express a => Int -> (a -> Bool) -> Expr -> Maybe Expr
+counterExampleGeneralization maxTests prop e  =  listToMaybe
+  [g | g <- candidateGeneralizations e
+     , all (not . prop . evl) (take maxTests $ grounds g)]
+
+
+candidateGeneralizations :: Expr -> [Expr]
+candidateGeneralizations  =  map canonicalize
+                          .  concatMap canonicalVariations
+                          .  gen
+  where
+  gen e@(e1 :$ e2)  =
+    [holeAsTypeOf e | isListable e]
+    ++ [g1 :$ g2 | g1 <- gen e1, g2 <- gen e2]
+    ++ map (:$ e2) (gen e1)
+    ++ map (e1 :$) (gen e2)
+  gen e
+    | isVar e    =  []
+    | otherwise  =  [holeAsTypeOf e | isListable e]
+  isListable  =  not . null . tiersFor
+
+grounds :: Expr -> [Expr]
+grounds e  =  map (e //-)
+           .  concat
+           $  products [mapT ((,) v) (tiersFor v) | v <- nubVars e]
+
+tiersFor :: Expr -> [[Expr]]
+tiersFor e  =  case show (typ e) of
+  "Int"    ->  mapT val (tiers `asTypeOf` [[undefined :: Int]])
+  "Bool"   ->  mapT val (tiers `asTypeOf` [[undefined :: Bool]])
+  "[Int]"  ->  mapT val (tiers `asTypeOf` [[undefined :: [Int]]])
+  "[Bool]" ->  mapT val (tiers `asTypeOf` [[undefined :: [Bool]]])
+  _        ->  []
diff --git a/eg/u-speculate.hs b/eg/u-speculate.hs
new file mode 100644
--- /dev/null
+++ b/eg/u-speculate.hs
@@ -0,0 +1,144 @@
+-- u-extrapolate.hs -- micro Speculate / Speculite
+--
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+--
+-- A small library capable of conjecturing laws about Haskell functions
+-- implemented in under 70 lines of code.
+--
+-- This works like the equation conjecturing tools Speculate or QuickSpec,
+-- combining expressions to form equations,
+-- reporting any equations that passes 60 tests.
+-- Redundant equations are then pruned using simple rules.
+--
+-- Limitations:
+--
+-- * even with pruning, this program still prints some redundant rules;
+-- * there is no way to configure maximum number of tests to consider an
+--   equation true;
+-- * runtime is exponential as you add more symbols to Speculate about.
+--
+-- Please see Speculate for a full featured version:
+--
+--   https://github.com/rudymatela/speculate
+import Data.List
+import Data.Maybe
+import Data.Express
+import Test.LeanCheck
+
+main :: IO ()
+main  =  do
+  printEquationsAbout
+    [ hole (undefined :: Int)
+    , val (0 :: Int)
+    , value "+" ((+) :: Int -> Int -> Int)
+    , value "abs" (abs :: Int -> Int)
+    ]
+
+  printEquationsAbout
+    [ hole (undefined :: Bool)
+    , val False
+    , val True
+    , value "not" not
+    ]
+
+  printEquationsAbout
+    [ hole (undefined :: Int)
+    , hole (undefined :: [Int])
+    , val ([] :: [Int])
+    , value ":" ((:) :: Int -> [Int] -> [Int])
+    , value "++" ((++) :: [Int] -> [Int] -> [Int])
+    , value "sort" (sort :: [Int] -> [Int])
+    ]
+
+  {-
+  printEquationsAbout
+    [ hole (undefined :: Bool)
+    , hole (undefined :: [Bool])
+    , val True
+    , val ([] :: [Bool])
+    , value "foldr" (foldr :: (Bool -> Bool -> Bool) -> Bool -> [Bool] -> Bool)
+    , value "&&" (&&)
+    , value "and" (and :: [Bool] -> Bool)
+    ]
+  -}
+
+printEquationsAbout :: [Expr] -> IO ()
+printEquationsAbout es  =  do
+  putStrLn $ "Equations about " ++ intercalate ", " (map showExpr es) ++ ":"
+  putStrLn . unlines . map showEq $ speculateAbout es
+  where
+  showEq eq  =  showExpr (lhs eq) ++ "  =  " ++ showExpr (rhs eq)
+
+speculateAbout :: [Expr] -> [Expr]
+speculateAbout  =  discardLater hasRewrite
+                .  discardLaterInstances
+                .  concatMap trueCanonicalVariations
+                .  discardLaterInstances
+                .  sort
+                .  filter isTrue
+                .  candidateEquationsFrom
+  where
+  hasRewrite e1 e2  =  isRule e2 && e1 `hasInstanceOf` lhs e2
+
+trueCanonicalVariations :: Expr -> [Expr]
+trueCanonicalVariations  =  discardLaterInstances
+                         .  filter isTrue
+                         .  filter isntIdentity
+                         .  canonicalVariations
+
+discardLaterInstances :: [Expr] -> [Expr]
+discardLaterInstances =  discardLater (\e1 e2 -> isntIdentity e2 && e1 `isInstanceOf` e2)
+
+candidateEquationsFrom :: [Expr] -> [Expr]
+candidateEquationsFrom es'  =  [e1 -==- e2 | e1 <- es, e2 <- es, e1 >= e2]
+  where
+  es = candidateExprsFrom es'
+
+candidateExprsFrom :: [Expr] -> [Expr]
+candidateExprsFrom  =  concat . take 5 . expressionsT
+  where
+  expressionsT ds  =  [ds] \/ (delay $ productMaybeWith ($$) es es)
+    where
+    es = expressionsT ds
+
+isTrue :: Expr -> Bool
+isTrue  =  all (eval False) . take 60 . grounds
+
+grounds :: Expr -> [Expr]
+grounds e  =  map (e //-)  .  concat
+           $  products [mapT ((,) v) (tiersFor v) | v <- nubVars e]
+
+tiersFor :: Expr -> [[Expr]]
+tiersFor e  =  case show (typ e) of
+  "Int"    ->  mapT val (tiers `asTypeOf` [[undefined :: Int]])
+  "Bool"   ->  mapT val (tiers `asTypeOf` [[undefined :: Bool]])
+  "[Int]"  ->  mapT val (tiers `asTypeOf` [[undefined :: [Int]]])
+  "[Bool]" ->  mapT val (tiers `asTypeOf` [[undefined :: [Bool]]])
+  _        ->  []
+
+(-==-) :: Expr -> Expr -> Expr
+ex -==- ey  =  headOr (val False) . map (:$ ey) $ mapMaybe ($$ ex)
+  [ value "==" ((==) :: Int -> Int -> Bool)
+  , value "==" ((==) :: Bool -> Bool -> Bool)
+  , value "==" ((==) :: [Int] -> [Int] -> Bool)
+  , value "==" ((==) :: [Bool] -> [Bool] -> Bool)
+  ]
+  where
+  headOr x []     =  x
+  headOr _ (x:_)  =  x
+
+lhs, rhs :: Expr -> Expr
+lhs (((Value "==" _) :$ e) :$ _)  =  e
+rhs (((Value "==" _) :$ _) :$ e)  =  e
+
+isntIdentity, isRule :: Expr -> Bool
+isntIdentity eq  =  lhs eq /= rhs eq
+isRule       eq  =  size (lhs eq) > size (rhs eq)
+
+discardLater :: (a -> a -> Bool) -> [a] -> [a]
+discardLater (?)  =  d
+  where
+  d []      =  []
+  d (x:xs)  =  x : d (discard (? x) xs)
+  discard p  =  filter (not . p)
diff --git a/express.cabal b/express.cabal
new file mode 100644
--- /dev/null
+++ b/express.cabal
@@ -0,0 +1,257 @@
+-- Cabal file for express
+name:                express
+version:             0.1.0
+synopsis:            Express
+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 provides the @ Expr @ type and over a hundred functions for
+  building, evaluating, comparing, folding, canonicalizing and matching
+  @ Expr @s.  See the README and Haddock documentation for more details.
+
+
+homepage:            https://github.com/rudymatela/express#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Rudy Matela <rudy@matela.com.br>
+maintainer:          Rudy Matela <rudy@matela.com.br>
+category:            Testing
+build-type:          Simple
+cabal-version:       1.18
+
+extra-doc-files: README.md
+               , TODO.md
+               , doc/express.svg
+extra-source-files: .gitignore
+                  , .travis.yml
+                  , Makefile
+                  , mk/depend.mk
+                  , mk/haskell.mk
+                  , mk/ghcdeps
+                  , mk/haddock-i
+                  , mk/All.hs
+                  , mk/Toplibs.hs
+                  , stack.yaml
+                  , stack-lts-11.yaml
+                  , test/sdist
+                  , test/model/bench/*.out
+                  , test/model/eg/*.out
+                  , bench/versions
+                  , bench/runtime/zero/versions
+                  , bench/runtime/zero/bench/*.runtime
+                  , bench/runtime/zero/eg/*.runtime
+tested-with: GHC==8.6
+           , GHC==8.4
+           , GHC==8.2
+           , GHC==8.0
+           , GHC==7.10
+           , GHC==7.8
+
+
+source-repository head
+  type:           git
+  location:       https://github.com/rudymatela/express
+
+source-repository this
+  type:           git
+  location:       https://github.com/rudymatela/express
+  tag:            v0.1.0
+
+library
+  exposed-modules:     Data.Express
+                     , Data.Express.Core
+                     , Data.Express.Basic
+                     , Data.Express.Map
+                     , Data.Express.Express
+                     , Data.Express.Express.Derive
+                     , Data.Express.Fixtures
+                     , Data.Express.Fold
+                     , Data.Express.Hole
+                     , Data.Express.Instances
+                     , Data.Express.Name
+                     , Data.Express.Name.Derive
+                     , Data.Express.Canon
+                     , Data.Express.Match
+                     , Data.Express.Utils.List
+                     , Data.Express.Utils.String
+                     , Data.Express.Utils.Typeable
+                     , Data.Express.Utils.TH
+  hs-source-dirs:      src
+  build-depends:       base >= 4 && < 5, template-haskell
+  default-language:    Haskell2010
+
+test-suite main
+  type:                exitcode-stdio-1.0
+  main-is:             main.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite core
+  type:                exitcode-stdio-1.0
+  main-is:             core.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite canon
+  type:                exitcode-stdio-1.0
+  main-is:             canon.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite hole
+  type:                exitcode-stdio-1.0
+  main-is:             hole.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite match
+  type:                exitcode-stdio-1.0
+  main-is:             match.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite map
+  type:                exitcode-stdio-1.0
+  main-is:             map.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite fixtures
+  type:                exitcode-stdio-1.0
+  main-is:             fixtures.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite instances
+  type:                exitcode-stdio-1.0
+  main-is:             instances.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite express-instances
+  type:                exitcode-stdio-1.0
+  main-is:             express.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite express-derive
+  type:                exitcode-stdio-1.0
+  main-is:             express-derive.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite ord
+  type:                exitcode-stdio-1.0
+  main-is:             ord.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite listable
+  type:                exitcode-stdio-1.0
+  main-is:             listable.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite name
+  type:                exitcode-stdio-1.0
+  main-is:             name.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite name-derive
+  type:                exitcode-stdio-1.0
+  main-is:             name-derive.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite fold
+  type:                exitcode-stdio-1.0
+  main-is:             fold.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+test-suite show
+  type:                exitcode-stdio-1.0
+  main-is:             show.hs
+  hs-source-dirs:      test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+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.8
+  default-language:    Haskell2010
+
+benchmark compare
+  type:                exitcode-stdio-1.0
+  main-is:             compare.hs
+  hs-source-dirs:      bench, test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+benchmark pairs
+  type:                exitcode-stdio-1.0
+  main-is:             pairs.hs
+  hs-source-dirs:      bench, test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+benchmark tiers
+  type:                exitcode-stdio-1.0
+  main-is:             tiers.hs
+  hs-source-dirs:      bench, test
+  other-modules:       Test, Test.ListableExpr
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+benchmark u-extrapolate
+  type:                exitcode-stdio-1.0
+  main-is:             u-extrapolate.hs
+  hs-source-dirs:      eg
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
+
+benchmark u-speculate
+  type:                exitcode-stdio-1.0
+  main-is:             u-speculate.hs
+  hs-source-dirs:      eg
+  build-depends:       base >= 4 && < 5, express, leancheck >= 0.8
+  default-language:    Haskell2010
diff --git a/mk/All.hs b/mk/All.hs
new file mode 100644
--- /dev/null
+++ b/mk/All.hs
@@ -0,0 +1,8 @@
+module All
+  ( module Data.Express
+  , module Data.Express.Fixtures
+  )
+where
+
+import Data.Express
+import Data.Express.Fixtures
diff --git a/mk/Toplibs.hs b/mk/Toplibs.hs
new file mode 100644
--- /dev/null
+++ b/mk/Toplibs.hs
@@ -0,0 +1,5 @@
+-- Using ghc --make in this module triggers compilation of every library.
+module Toplibs () where
+
+import Data.Express ()
+import Test ()
diff --git a/mk/depend.mk b/mk/depend.mk
new file mode 100644
--- /dev/null
+++ b/mk/depend.mk
@@ -0,0 +1,814 @@
+bench/compare.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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 \
+  bench/compare.hs
+bench/compare: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  bench/compare.hs \
+  mk/toplibs
+bench/pairs.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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 \
+  bench/pairs.hs
+bench/pairs: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  bench/pairs.hs \
+  mk/toplibs
+bench/tiers.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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 \
+  bench/tiers.hs
+bench/tiers: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  bench/tiers.hs \
+  mk/toplibs
+eg/u-extrapolate: \
+  eg/u-extrapolate.hs \
+  mk/toplibs
+eg/u-extrapolate.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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 \
+  eg/u-extrapolate.hs
+eg/u-speculate: \
+  eg/u-speculate.hs \
+  mk/toplibs
+eg/u-speculate.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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 \
+  eg/u-speculate.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/List.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/Fixtures.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 \
+  mk/All.hs
+mk/Toplibs.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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 \
+  mk/Toplibs.hs
+Setup.o: \
+  Setup.hs
+Setup: \
+  Setup.hs \
+  mk/toplibs
+src/Data/Express/Basic.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Canon: \
+  mk/toplibs
+src/Data/Express/Canon.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.hs \
+  src/Data/Express/Name.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/Core.hs \
+  src/Data/Express/Canon.hs \
+  src/Data/Express/Basic.hs
+src/Data/Express/Core.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/String.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/List.hs \
+  src/Data/Express/Express.hs \
+  src/Data/Express/Express/Derive.hs \
+  src/Data/Express/Core.hs
+src/Data/Express/Express.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.hs \
+  src/Data/Express/Express.hs \
+  src/Data/Express/Core.hs
+src/Data/Express/Fixtures: \
+  mk/toplibs
+src/Data/Express/Fixtures.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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/Fold.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.hs \
+  src/Data/Express/Fold.hs \
+  src/Data/Express/Core.hs
+src/Data/Express/Hole.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/String.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/List.hs \
+  src/Data/Express/Name.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/Core.hs \
+  src/Data/Express/Basic.hs
+src/Data/Express/Map.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/String.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/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/Name/Derive.o: \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Name.hs \
+  src/Data/Express/Name/Derive.hs
+src/Data/Express/Name.o: \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Name.hs
+src/Data/Express.o: \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Utils/List.o: \
+  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
+test/canon.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/canon.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/canon: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/canon.hs \
+  mk/toplibs
+test/core.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/core.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/core: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/core.hs \
+  mk/toplibs
+test/express-derive.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/express-derive.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/express-derive: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/express-derive.hs \
+  mk/toplibs
+test/express.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/express.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/express: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/express.hs \
+  mk/toplibs
+test/fixtures.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/fixtures.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/fixtures: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/fixtures.hs \
+  mk/toplibs
+test/fold.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/fold.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/fold: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/fold.hs \
+  mk/toplibs
+test/hole.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/hole.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/hole: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/hole.hs \
+  mk/toplibs
+test/instances.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/instances.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/instances: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/instances.hs \
+  mk/toplibs
+test/listable.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/listable.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/listable: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/listable.hs \
+  mk/toplibs
+test/main.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/main.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/main: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/main.hs \
+  mk/toplibs
+test/map.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/map.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/map: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/map.hs \
+  mk/toplibs
+test/match.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/match.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/match: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/match.hs \
+  mk/toplibs
+test/name-derive.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/name-derive.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/name-derive: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/name-derive.hs \
+  mk/toplibs
+test/name.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/name.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/name: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/name.hs \
+  mk/toplibs
+test/ord.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/ord.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/ord: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/ord.hs \
+  mk/toplibs
+test/show.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/show.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/show: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  test/show.hs \
+  mk/toplibs
+test/Test/ListableExpr.o: \
+  test/Test/ListableExpr.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/Test.o: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/Test: \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  mk/toplibs
+test/utils.o: \
+  test/utils.hs \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  src/Data/Express/Utils/Typeable.hs \
+  src/Data/Express/Utils/TH.hs \
+  src/Data/Express/Utils/String.hs \
+  src/Data/Express/Utils/List.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/Fixtures.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
+test/utils: \
+  test/utils.hs \
+  test/Test.hs \
+  test/Test/ListableExpr.hs \
+  mk/toplibs
diff --git a/mk/ghcdeps b/mk/ghcdeps
new file mode 100644
--- /dev/null
+++ b/mk/ghcdeps
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+# ghcdeps: generate Haskell make dependencies for compiling with GHC.
+#
+# Copyright (c) 2015-2019 Rudy Matela.
+# Distributed under the 3-Clause BSD licence.
+#
+# From a list of files provided on standard input,
+# generate flat make dependencies.
+#
+# Transitive relations are repeated.
+#
+# Usage:
+#   $ ghcdeps -isomedir:someother <<LIST
+#   program.hs
+#   Library.hs
+#   Path/To/Library.hs
+#   LIST
+#   program: ...
+#   Library.o: ...
+#   Path/To/Library.o: ...
+#
+# Or:
+#   $ find -name \*.hs -o -name \*.lhs | ghcdeps [GHCFLAGS]
+#   ...
+#
+# Note that when using find, you have to remember to exclude unecessary files,
+# e.g.:  dist, Setup.hs
+#
+# Yes, this is hacky.  But hey, it works.
+while read fn
+do
+	ghc "$@" -dep-suffix=. -dep-makefile=tmp.mk -M "$fn" ||
+	exit 1
+	deps="`cat tmp.mk |
+	       grep "hs$" |
+	       sort |
+	       sed -e "s/.*: //" |
+	       tac |
+	       tr '\n' ' ' |
+	       sed -e "s/ $//"`"
+	obj=`echo $fn | sed -e 's,^\./,,;s/\.hs$/.o/'`
+	bin=`echo $fn | sed -e 's,^\./,,;s/\.hs$//'`
+	echo "$obj: $deps"
+	grep -q "main" "$fn" && echo "$bin: `echo $deps | sed -e 's|[^ ]*src[^ ]*||g'` mk/toplibs"
+	rm -f tmp.mk
+done |
+sort |
+sed -e 's,  *, \\\n  ,g'
diff --git a/mk/haddock-i b/mk/haddock-i
new file mode 100644
--- /dev/null
+++ b/mk/haddock-i
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# haddock-i: list haddock's -i parameters.
+#
+# Copyright (c) 2015-2019 Rudy Matela.
+# Distributed under the 3-Clause BSD licence.
+#
+# $ haddock-i <package1> <package2> ... <packageN>
+#
+# will print -i parameters necessary for haddock to link to Haddock
+# documentation installed on your system, so you can:
+#
+# $ haddock-i base template-haskell | xargs haddock <files>
+err() {
+	echo "$@" > /dev/stderr
+}
+
+errxit() {
+	err "$@"
+	exit 1
+}
+
+iface-for() {
+	ghc-pkg field $1 haddock-interfaces | sort -rV | head -1 | sed "s/.*: //"
+}
+
+html-for() {
+	ghc-pkg field $1 haddock-html       | sort -rV | head -1 | sed "s/.*: //"
+}
+
+for pkg in "$@"
+do
+	iface=$(iface-for $pkg)
+	html=$(html-for $pkg)
+	[ -d "$html" -a -f "$iface" ] && echo "-i$html,$iface" || err "haddock-i: warning: could not find interface file for $pkg"
+done
diff --git a/mk/haskell.mk b/mk/haskell.mk
new file mode 100644
--- /dev/null
+++ b/mk/haskell.mk
@@ -0,0 +1,134 @@
+# Implicit rules for compiling Haskell code.
+#
+# Copyright (c) 2015-2019 Rudy Matela.
+# Distributed under the 3-Clause BSD licence.
+#
+# You can optionally configure the "Configuration variables" below in your main
+# makefile, e.g.:
+#
+#   GHCIMPORTDIRS = path/to/dir:path/to/another/dir
+#   GHCFLAGS = -O2 -dynamic
+#   GHC = ghc-7.6
+#   include haskell.mk
+
+
+
+# Configuration variables
+
+# GHC Parameters
+GHCIMPORTDIRS ?=
+GHCFLAGS ?=
+GHC ?= ghc
+GHCCMD = $(GHC) -i$(GHCIMPORTDIRS) $(GHCFLAGS)
+HADDOCK ?= haddock
+
+# Hugs Parameters
+HUGSIMPORTDIRS ?= "/usr/lib/hugs/packages/*"
+HUGSFLAGS ?=
+CPPHS_HUGS ?= cpphs-hugs --noline -D__HUGS__
+HUGS ?= hugs
+RUNHUGS ?= runhugs
+HUGSCMD    = $(HUGS)    -F"$(CPPHS_HUGS)" -P$(HUGSIMPORTDIRS) $(HUGSFLAGS)
+RUNHUGSCMD = $(RUNHUGS) -F"$(CPPHS_HUGS)" -P$(HUGSIMPORTDIRS) $(HUGSFLAGS)
+
+
+# Makefile where to keep the dependencies
+DEPMK ?= mk/depend.mk
+
+# LIB_HSS: all library Haskell files
+# ALL_HSS: all Haskell files
+# You can override ALL/LIB_HSS in your main Makefile
+LIST_LIB_HSS ?= find src -name "*.hs"
+LIST_ALL_HSS ?= find \( -path "./dist" -o -path "./.stack-work" \) -prune \
+                     -o -name "*.*hs" -print
+LIB_HSS ?= $(shell $(LIST_LIB_HSS))
+ALL_HSS ?= $(shell $(LIST_ALL_HSS))
+
+LIB_DEPS ?= base
+ALL_DEPS ?= $(LIB_DEPS)
+
+PKGNAME = $(shell cat *.cabal | grep "^name:"    | sed -e "s/name: *//")
+HADDOCK_VERSION = $(shell $(HADDOCK) --version | grep version | sed -e 's/.*version //;s/,.*//')
+HADDOCK_MAJOR = $(shell echo $(HADDOCK_VERSION) | sed -e 's/\..*//')
+HADDOCK_MINOR = $(shell echo $(HADDOCK_VERSION) | sed -e 's/[0-9]*\.\([0-9]*\).[0-9]*/\1/')
+HADDOCK_PKG_NAME = $(shell [ $(HADDOCK_MAJOR) -gt 2 ] \
+                        || [ $(HADDOCK_MAJOR) -eq 2 -a $(HADDOCK_MINOR) -ge 20 ] \
+                        && echo "--package-name=$(PKGNAME)")
+HADDOCK_HLNK_SRC = $(shell [ $(HADDOCK_MAJOR) -gt 2 ] \
+                        || [ $(HADDOCK_MAJOR) -eq 2 -a $(HADDOCK_MINOR) -ge 17 ] \
+                        && echo "--hyperlinked-source")
+
+
+# Implicit rules
+%.hi %.o: %.hs
+	$(GHCCMD) $< && touch $@
+
+%: %.hs
+	$(GHCCMD) $< && touch $@
+
+.PHONY: %.ghci
+%.ghci: %.hs
+	$(GHCCMD) -O0 --interactive $<
+
+.PHONY: %.hugs
+%.hugs: %.hs
+	$(HUGSCMD) $<
+
+.PHONY: %.runhugs
+%.runhugs: %.hs
+	$(RUNHUGSCMD) $<
+
+
+# Cleaning rule (add as a clean dependency)
+.PHONY: clean-hi-o
+clean-hi-o:
+	find $(ALL_HSS) | sed -e 's/hs$$/o/'      | xargs rm -f
+	find $(ALL_HSS) | sed -e 's/hs$$/hi/'     | xargs rm -f
+	find $(ALL_HSS) | sed -e 's/hs$$/dyn_o/'  | xargs rm -f
+	find $(ALL_HSS) | sed -e 's/hs$$/dyn_hi/' | xargs rm -f
+
+
+# Update dependency file
+.PHONY: depend
+depend:
+	find $(ALL_HSS) | ./mk/ghcdeps -i$(GHCIMPORTDIRS) $(GHCFLAGS) > $(DEPMK)
+
+# haddock rules
+haddock: doc/index.html
+
+clean-haddock:
+	rm -f doc/*.{html,css,js,png,gif,json} doc/src/*.{html,css,js} README.html
+
+upload-haddock:
+	@echo "use \`cabal upload -d' instead"
+	@echo "(but 1st: cabal install --only-dependencies --enable-documentation)"
+	@echo "(to just compile docs: cabal haddock --for-hackage)"
+	@echo "(on Arch Linux, use: cabal haddock --for-hackage --haddock-options=--optghc=-dynamic)"
+
+doc/index.html: $(LIB_HSS)
+	./mk/haddock-i $(LIB_DEPS) | xargs \
+	$(HADDOCK) --html -odoc $(LIB_HSS) \
+	  --title=$(PKGNAME) \
+	  $(HADDOCK_PKG_NAME) \
+	  $(HADDOCK_HLNK_SRC) \
+	  $(HADDOCKFLAGS)
+
+
+# lists all Haskell source files
+list-all-hss:
+	@find $(ALL_HSS)
+
+# lists library Haskell source files
+list-lib-hss:
+	@find $(LIB_HSS)
+
+bootstrap-haskell-mk:
+	@[ -d "$(DEST)" ] || (echo -e "error: no destination found\nusage: \`make bootstrap-haskell-mk DEST=path/to/prj'"; exit 1)
+	mkdir -p mk
+	cp mk/{haskell.mk,ghcdeps,haddock-i} $(DEST)/mk
+	touch $(DEST)/mk/depend.mk
+
+show-pkgname:
+	@echo $(PKGNAME)
+
+include $(DEPMK)
diff --git a/src/Data/Express.hs b/src/Data/Express.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express.hs
@@ -0,0 +1,259 @@
+-- |
+-- Module      : Data.Express
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Express is a library for manipulating dynamically typed Haskell expressions.
+-- It's like "Data.Dynamic" but with support for encoding applications and
+-- variables.
+--
+-- It provides the 'Expr' type and over a hundred functions for
+-- building, evaluating, comparing, folding, canonicalizing and matching
+-- 'Expr's.
+--
+-- /Example:/
+-- Like with "Data.Dynamic", we can use Express to create heterogeneous lists:
+--
+-- > > let xs = [val False, val True, val (1::Int), val (2::Int), val (3::Integer), val "123"]
+-- > > :t xs
+-- > xs :: [Expr]
+-- > > xs
+-- > [ False :: Bool
+-- > , True :: Bool
+-- > , 1 :: Int
+-- > , 2 :: Int
+-- > , 3 :: Integer
+-- > , "123" :: [Char]
+-- > ]
+--
+-- We can then apply 'evaluate' to select values of different types:
+--
+-- > > import Data.Maybe
+-- > > mapMaybe evaluate xs :: [Bool]
+-- > [False,True]
+-- > > mapMaybe evaluate xs :: [Int]
+-- > [1,2]
+-- > > mapMaybe evaluate xs :: [Integer]
+-- > [3]
+-- > > mapMaybe evaluate xs :: [String]
+-- > ["123"]
+--
+-- If define an heterogeneous list of functions encoded as 'Expr's:
+--
+-- > > let fs = [value "not" not, value "&&" (&&), value "abs" (abs :: Int -> Int)]
+-- > > :t fs
+-- > fs :: [Expr]
+--
+-- Using '$$' we can list the type correct applications
+-- between the two previously defined lists:
+--
+-- > > catMaybes [f $$ x | f <- fs, x <- xs]
+-- > [ not False :: Bool
+-- > , not True :: Bool
+-- > , (False &&) :: Bool -> Bool
+-- > , (True &&) :: Bool -> Bool
+-- > , abs 1 :: Int
+-- > , abs 2 :: Int
+-- > ]
+--
+-- Other uses of Express include:
+--
+-- * generalizing counter-examples of property-based testing
+--   in <https://hackage.haskell.org/package/extrapolate Extrapolate>;
+-- * conjecturing equations based on the results of testing
+--   in <https://hackage.haskell.org/package/speculate Speculate>.
+--
+-- In this documentation,
+-- the complexity of most functions is 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 CPP #-}
+module Data.Express
+  ( 
+  -- -- -- Data.Express.Core exports -- -- --
+
+  -- * The Expr datatype
+    Expr (..)
+
+  -- ** Building Exprs
+  , value
+  , val
+  , ($$)
+  , var
+
+  -- ** Evaluating Exprs
+  , evaluate
+  , eval
+  , evl
+  , typ
+  , etyp
+  , mtyp
+  , toDynamic
+
+  -- ** Boolean properties of Exprs
+  , isValue
+  , isApp
+  , isVar
+  , isConst
+  , isIllTyped
+  , isWellTyped
+  , hasVar
+  , isGround
+
+  -- ** Comparing Exprs
+  , compareComplexity
+
+  -- ** Properties of Exprs
+  , arity
+  , size
+  , depth
+  , height
+
+  -- ** Showing Exprs
+  , showExpr
+  , showPrecExpr
+  , showOpExpr
+
+  -- * Subexpressions
+
+  -- ** Listing subexpressions
+  , subexprs
+  , values
+  , vars
+  , consts
+  , nubSubexprs
+  , nubValues
+  , nubVars
+  , nubConsts
+
+  -- -- -- Data.Express.Map exports -- -- --
+
+  -- ** Mapping subexpressions
+  , mapValues
+  , mapVars
+  , mapConsts
+  , mapSubexprs
+  , (//-)
+  , (//)
+  , renameVarsBy
+
+
+  -- -- -- Data.Express.Hole exports -- -- --
+
+  -- * Variables and holes
+
+  -- ** Creating variables
+  , varAsTypeOf
+  , listVars
+  , listVarsAsTypeOf
+
+  -- ** Typed holes
+  , hole
+  , isHole
+  , holes
+  , nubHoles
+  , holeAsTypeOf
+
+
+  -- -- -- Data.Express.Fold exports -- -- --
+
+  -- * Juggling Exprs
+
+  -- ** Folding Exprs
+  , fold
+  , unfold
+  , foldPair
+  , unfoldPair
+  , foldTrio
+  , unfoldTrio
+  , foldApp
+  , unfoldApp
+
+  -- -- -- Data.Express.Canon exports -- -- --
+
+  -- ** Canonicalizing Exprs
+  , canonicalize
+  , canonicalizeWith
+  , canonicalization
+  , canonicalizationWith
+  , isCanonical
+  , isCanonicalWith
+  , canonicalVariations
+  , fastCanonicalVariations
+
+  -- -- -- Data.Express.Match exports -- -- --
+
+  -- ** Matching Exprs
+  , match
+  , matchWith
+  , isInstanceOf
+  , hasInstanceOf
+  , isSubexprOf
+
+  -- -- -- Data.Express.Express exports -- -- --
+
+  -- * Typeclasses
+
+  -- ** The Express typeclass
+  , Express (..)
+  , deriveExpress
+  , deriveExpressCascading
+  , deriveExpressIfNeeded
+
+  -- -- -- Data.Express.Name exports -- -- --
+
+  -- ** The Name typeclass
+  , Name (..)
+  , names
+  , variableNamesFromTemplate
+  , deriveName
+  , deriveNameCascading
+  , deriveNameIfNeeded
+
+  -- -- -- Data.Express.Instances exports -- -- --
+
+  -- ** Typeclass instances as Exprs
+  , reifyEq
+  , reifyOrd
+  , reifyEqOrd
+  , reifyName
+
+  , mkEq
+  , mkOrd
+  , mkOrdLessEqual
+  , mkName
+  , mkNameWith
+
+  , isEq
+  , isOrd
+  , isEqOrd
+  , isEqT
+  , isOrdT
+  , isEqOrdT
+
+  , mkEquation
+  , mkComparisonLE
+  , mkComparisonLT
+  , mkComparison
+  , lookupComparison
+
+  , listVarsWith
+  , lookupName
+  , lookupNames
+
+  , validApps
+  , findValidApp
+
+  , preludeNameInstances
+  )
+where
+
+import Data.Express.Basic
+import Data.Express.Canon
+import Data.Express.Match
+import Data.Express.Name
+import Data.Express.Name.Derive
+import Data.Express.Express
+import Data.Express.Express.Derive
+import Data.Express.Instances
diff --git a/src/Data/Express/Basic.hs b/src/Data/Express/Basic.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Basic.hs
@@ -0,0 +1,30 @@
+-- |
+-- Module      : Data.Express.Basic
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Defines the 'Expr' type and _basic_ utilities involving it, including:
+--
+-- * re-export of "Data.Express.Core"
+-- * re-export of "Data.Express.Map"
+-- * re-export of "Data.Express.Fold"
+-- * re-export of "Data.Express.Hole"
+--
+-- If you're a Express user,
+-- you're probably better of importing "Data.Express".
+{-# LANGUAGE CPP #-}
+module Data.Express.Basic
+  (
+  -- * Module re-exports
+    module Data.Express.Core
+  , module Data.Express.Map
+  , module Data.Express.Fold
+  , module Data.Express.Hole
+  )
+where
+
+import Data.Express.Core
+import Data.Express.Map
+import Data.Express.Fold
+import Data.Express.Hole
diff --git a/src/Data/Express/Canon.hs b/src/Data/Express/Canon.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Canon.hs
@@ -0,0 +1,234 @@
+-- |
+-- Module      : Data.Express.Canon
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Utilities for canonicalizing 'Expr's with variables.
+module Data.Express.Canon
+  ( canonicalize
+  , canonicalizeWith
+  , canonicalization
+  , canonicalizationWith
+  , isCanonical
+  , isCanonicalWith
+  , canonicalVariations
+  , fastCanonicalVariations
+  )
+where
+
+import Data.Express.Basic
+import Data.Express.Name
+import Data.Express.Instances
+import Data.List ((\\))
+
+-- |
+-- Like 'canonicalize' but allows customization
+-- of the list of variable names.
+-- (cf. 'lookupNames', 'variableNamesFromTemplate')
+--
+-- > > canonicalizeWith (const ["i","j","k","l",...]) (xx -+- yy)
+-- > i + j :: Int
+--
+-- The argument 'Expr' of the argument function allows
+-- to provide a different list of names for different types:
+--
+-- > > let namesFor e
+-- > >   | typ e == typeOf (undefined::Char) = variableNamesFromTemplate "c1"
+-- > >   | typ e == typeOf (undefined::Int)  = variableNamesFromTemplate "i"
+-- > >   | otherwise                         = variableNamesFromTemplate "x"
+--
+-- > > canonicalizeWith namesFor ((xx -+- ord' dd) -+- (ord' cc -+- yy))
+-- > (i + ord c1) + (ord c2 + j) :: Int
+canonicalizeWith :: (Expr -> [String]) -> Expr -> Expr
+canonicalizeWith namesFor e = e //- canonicalizationWith namesFor e
+
+-- |
+-- Like 'canonicalization' but allows customization
+-- of the list of variable names.
+-- (cf. 'lookupNames', 'variableNamesFromTemplate')
+canonicalizationWith :: (Expr -> [String]) -> Expr -> [(Expr,Expr)]
+canonicalizationWith namesFor e = cr (vars e) []
+  where
+  cr :: [Expr] -> [(Expr,Expr)] -> [(Expr,Expr)]
+  cr []     bs  =  bs
+  cr (e:es) bs  =  cr es
+                $  if e `elem` map fst bs
+                   then bs
+                   else (e, n `varAsTypeOf` e):bs
+    where
+    existingNames = [n | (_,Value ('_':n) _) <- bs]
+    freshNames = namesFor e \\ existingNames
+    n = head freshNames
+
+-- |
+-- Like 'isCanonical' but allows specifying
+-- the list of variable names.
+isCanonicalWith :: (Expr -> [String]) -> Expr -> Bool
+isCanonicalWith ti e = canonicalizeWith ti e == e
+
+-- |
+-- Canonicalizes an 'Expr' so that variable names appear in order.
+-- Variable names are taken from the 'preludeNameInstances'.
+--
+-- > > canonicalize (xx -+- yy)
+-- > x + y :: Int
+--
+-- > > canonicalize (yy -+- xx)
+-- > x + y :: Int
+--
+-- > > canonicalize (xx -+- xx)
+-- > x + x :: Int
+--
+-- > > canonicalize (yy -+- yy)
+-- > x + x :: Int
+--
+-- Constants are untouched:
+--
+-- > > canonicalize (jj -+- (zero -+- abs' ii))
+-- > x + (y + abs y) :: Int
+--
+-- This also works for variable functions:
+--
+-- > > canonicalize (gg yy -+- ff xx -+- gg xx)
+-- > (f x + g y) + f y :: Int
+canonicalize :: Expr -> Expr
+canonicalize = canonicalizeWith names'
+
+-- |
+-- Return a canonicalization of an 'Expr'
+-- that makes variable names appear in order
+-- using 'names' as provided by 'preludeNameInstances'.
+-- By using '//-' it can 'canonicalize' 'Expr's.
+--
+-- > > canonicalization (gg yy -+- ff xx -+- gg xx)
+-- > [ (x :: Int,        y :: Int)
+-- > , (f :: Int -> Int, g :: Int -> Int)
+-- > , (y :: Int,        x :: Int)
+-- > , (g :: Int -> Int, f :: Int -> Int) ]
+--
+-- > > canonicalization (yy -+- xx -+- yy)
+-- > [ (x :: Int, y :: Int)
+-- > , (y :: Int, x :: Int) ]
+canonicalization :: Expr -> [(Expr,Expr)]
+canonicalization = canonicalizationWith names'
+
+-- |
+-- Returns whether an 'Expr' is canonical:
+-- if applying 'canonicalize' is an identity
+-- using 'names' as provided by 'preludeNameInstances'.
+isCanonical :: Expr -> Bool
+isCanonical = isCanonicalWith names'
+
+-- 'names' lifted over the 'Expr' type for a handful of prelude Name instances.
+names' :: Expr -> [String]
+names' = lookupNames preludeNameInstances
+
+-- |
+-- Returns all canonical variations of an 'Expr'
+-- by filling holes with variables.
+-- Where possible, variations are listed
+-- from most general to least general.
+--
+-- > > canonicalVariations $ i_
+-- > [x :: Int]
+--
+-- > > canonicalVariations $ i_ -+- i_
+-- > [ x + y :: Int
+-- > , x + x :: Int ]
+--
+-- > > canonicalVariations $ i_ -+- i_ -+- i_
+-- > [ (x + y) + z :: Int
+-- > , (x + y) + x :: Int
+-- > , (x + y) + y :: Int
+-- > , (x + x) + y :: Int
+-- > , (x + x) + x :: Int ]
+--
+-- > > canonicalVariations $ i_ -+- ord' c_
+-- > [x + ord c :: Int]
+--
+-- > > canonicalVariations $ i_ -+- i_ -+- ord' c_
+-- > [ (x + y) + ord c :: Int
+-- > , (x + x) + ord c :: Int ]
+--
+-- > > canonicalVariations $ i_ -+- i_ -+- length' (c_ -:- unit c_)
+-- > [ (x + y) + length (c:d:"") :: Int
+-- > , (x + y) + length (c:c:"") :: Int
+-- > , (x + x) + length (c:d:"") :: Int
+-- > , (x + x) + length (c:c:"") :: Int ]
+--
+-- In an expression without holes this functions just returns a singleton list
+-- with the expression itself:
+--
+-- > > canonicalVariations $ val (0 :: Int)
+-- > [0 :: Int]
+--
+-- > > canonicalVariations $ ord' bee
+-- > [ord 'b' :: Int]
+--
+-- > > canonicalVariations $ ii -+- jj
+-- > [i + j :: Int]
+--
+-- Behaviour is undefined when applying this function to expressions already
+-- containing variables.
+canonicalVariations :: Expr -> [Expr]
+canonicalVariations = map canonicalize . fastCanonicalVariations
+
+-- |
+-- A faster version of 'canonicalVariations' that
+-- disregards name clashes across different types.
+-- Results are confusing to the user
+-- but fine for Express which differentiates
+-- between variables with the same name but different types.
+--
+-- Without applying 'canonicalize', the following 'Expr'
+-- may seem to have only one variable:
+--
+-- > > fastCanonicalVariations $ i_ -+- ord' c_
+-- > [x + ord x :: Int]
+--
+-- Where in fact it has two, as the second @ x @ has a different type.
+-- Applying 'canonicalize' disambiguates:
+--
+-- > > map canonicalize . fastCanonicalVariations $ i_ -+- ord' c_
+-- > [x + ord c :: Int]
+--
+-- This function is useful when resulting 'Expr's are
+-- not intended to be presented to the user
+-- but instead to be used by another function.
+-- It is simply faster to skip the step where clashes are resolved.
+fastCanonicalVariations :: Expr -> [Expr]
+fastCanonicalVariations e
+  | null hs'   =  [e]
+  | otherwise  =  concatMap fastCanonicalVariations
+               .  map (fill e) . fillings 0
+               $  [h | h <- hs', typ h == typ h']
+  where
+  hs' = holes e
+  h' = head hs'
+
+  names  =  variableNamesFromTemplate "x"
+
+  fillings :: Int -> [Expr] -> [[Expr]]
+  fillings i []  =  [[]] -- no holes, single empty filling
+  fillings i (h:hs)  =
+    concat $ map (names !! i `varAsTypeOf` h:) (fillings (i+1) hs) -- new var
+           : [ map (n `varAsTypeOf` h:) (fillings i hs) -- no new variable
+             | n <- take i names ]
+
+
+-- | Fill holes in an expression.
+--   Silently skips holes that are not of the right type.
+--   Silently discard remaining expressions.
+fill :: Expr -> [Expr] -> Expr
+fill e = fst . fill' e
+  where
+  fill' :: Expr -> [Expr] -> (Expr,[Expr])
+  fill' (e1 :$ e2) es = let (e1',es')  = fill' e1 es
+                            (e2',es'') = fill' e2 es'
+                        in (e1' :$ e2', es'')
+  fill' eh (e:es) | isHole eh && typ eh == typ e = (e,es)
+  fill' e es = (e,es)
+-- TODO: copy tests from Speculate
+-- TODO: add examples on Haddock
+-- TODO: export? consider exporting 'fill' from the Hole module
diff --git a/src/Data/Express/Core.hs b/src/Data/Express/Core.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Core.hs
@@ -0,0 +1,1060 @@
+-- |
+-- Module      : Data.Express.Core
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- This module defines the 'Expr' type and basic utilities involving it.
+--
+-- This is the core of the Express library.
+-- As a user, you are probably better of importing "Data.Express".
+-- If you want to understand how the library works,
+-- this is the place to start.
+--
+-- 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
+module Data.Express.Core
+  (
+  -- * The Expr datatype
+    Expr (..)
+
+  -- * Smart constructors
+  , value
+  , val
+  , ($$)
+  , var
+
+  -- * Evaluating Exprs
+  , evaluate
+  , eval
+  , evl
+  , typ
+  , etyp
+  , mtyp
+  , toDynamic
+
+  -- * Boolean properties
+  , isValue
+  , isApp
+  , isVar
+  , isConst
+  , isIllTyped
+  , isWellTyped
+  , hasVar
+  , isGround
+
+  -- * Comparison
+  , compareComplexity
+
+  -- * Properties
+  , arity
+  , size
+  , depth
+  , height
+
+  -- * Listing subexpressions
+  , subexprs
+  , values
+  , vars
+  , consts
+  , nubSubexprs
+  , nubValues
+  , nubVars
+  , nubConsts
+
+  -- * Other utilities
+  , unfoldApp
+  , showExpr
+  , showOpExpr
+  , showPrecExpr
+  )
+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.Typeable
+
+-- |
+-- Values of type 'Expr' represent objects or applications between objects.
+-- Each object is encapsulated together with its type and string representation.
+-- Values encoded in 'Expr's are always monomorphic.
+--
+-- An 'Expr' can be constructed using:
+--
+-- * 'val',   for values that are 'Show' instances;
+-- * 'value', for values that are not 'Show' instances, like functions;
+-- * ':$',    for applications between 'Expr's.
+--
+-- > > val False
+-- > False :: Bool
+--
+-- > > value "not" not :$ val False
+-- > not False :: Bool
+--
+-- An 'Expr' can be evaluated using 'evaluate', 'eval' or 'evl'.
+--
+-- > > evl $ val (1 :: Int) :: Int
+-- > 1
+--
+-- > > evaluate $ val (1 :: Int) :: Maybe Bool
+-- > Nothing
+--
+-- > > eval 'a' (val 'b')
+-- > 'b'
+--
+-- 'Show'ing a value of type 'Expr' will return a pretty-printed representation
+-- of the expression together with its type.
+--
+-- > > show (value "not" not :$ val False)
+-- > "not False :: Bool"
+--
+-- 'Expr' is like 'Dynamic' but has support for applications and variables
+-- (':$', 'var').
+--
+-- /The 'var' underscore convention:/
+-- Functions that manipulate 'Expr's usually follow the convention
+-- where a 'value' whose 'String' representation starts with @'_'@
+-- 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
+
+-- | /O(1)/.
+-- It takes a string representation of a value and a value, returning an
+-- 'Expr' with that terminal value.
+-- For instances of 'Show', it is preferable to use 'val'.
+--
+-- > > value "0" (0 :: Integer)
+-- > 0 :: Integer
+--
+-- > > value "'a'" 'a'
+-- > 'a' :: Char
+--
+-- > > value "True" True
+-- > True :: Bool
+--
+-- > > value "id" (id :: Int -> Int)
+-- > id :: Int -> Int
+--
+-- > > value "(+)" ((+) :: Int -> Int -> Int)
+-- > (+) :: Int -> Int -> Int
+--
+-- > > value "sort" (sort :: [Bool] -> [Bool])
+-- > sort :: [Bool] -> [Bool]
+value :: Typeable a => String -> a -> Expr
+value s x = Value s (toDyn x)
+
+-- | /O(1)/.
+-- A shorthand for 'value' for values that are 'Show' instances.
+--
+-- > > val (0 :: Int)
+-- > 0 :: Int
+--
+-- > > val 'a'
+-- > 'a' :: Char
+--
+-- > > val True
+-- > True :: Bool
+--
+-- Example equivalences to 'value':
+--
+-- > val 0     =  value "0" 0
+-- > val 'a'   =  value "'a'" 'a'
+-- > val True  =  value "True" True
+val :: (Typeable a, Show a) => a -> Expr
+val x = value (show x) x
+
+-- | /O(n)/.
+-- Creates an 'Expr' representing a function application.
+-- 'Just' an 'Expr' application if the types match, 'Nothing' otherwise.
+-- (cf. ':$')
+--
+-- > > value "id" (id :: () -> ()) $$ val ()
+-- > Just (id () :: ())
+--
+-- > > value "abs" (abs :: Int -> Int) $$ val (1337 :: Int)
+-- > Just (abs 1337 :: Int)
+--
+-- > > value "abs" (abs :: Int -> Int) $$ val 'a'
+-- > Nothing
+--
+-- > > value "abs" (abs :: Int -> Int) $$ val ()
+-- > Nothing
+($$) :: Expr -> Expr -> Maybe Expr
+e1 $$ e2 | isIllTyped e  =  Nothing
+         | otherwise     =  Just e
+  where
+  e = e1 :$ e2
+
+-- | /O(1)/.
+-- Creates an 'Expr' representing a variable with the given name and argument
+-- type.
+--
+-- > > var "x" (undefined :: Int)
+-- > x :: Int
+--
+-- > > var "u" (undefined :: ())
+-- > u :: ()
+--
+-- > > var "xs" (undefined :: [Int])
+-- > xs :: [Int]
+--
+-- This function follows the /underscore convention/:
+-- a variable is just a 'value' whose string representation
+-- starts with underscore (@'_'@).
+var :: Typeable a => String -> a -> Expr
+var s a = value ('_':s) (undefined `asTypeOf` a)
+
+-- | /O(n)/.
+-- Computes the type of an expression.  This raises errors, but this should
+-- not happen if expressions are smart-constructed.
+--
+-- > > let one = val (1 :: Int)
+-- > > let bee = val 'b'
+-- > > let absE = value "abs" (abs :: Int -> Int)
+--
+-- > > typ one
+-- > Int
+--
+-- > > typ bee
+-- > Char
+--
+-- > > typ absE
+-- > Int -> Int
+--
+-- > > typ (absE :$ one)
+-- > Int
+--
+-- > > typ (absE :$ bee)
+-- > *** Exception: type mismatch, cannot apply `Int -> Int' to `Char'
+--
+-- > > typ ((absE :$ bee) :$ one)
+-- > *** Exception: type mismatch, cannot apply `Int -> Int' to `Char'
+typ :: Expr -> TypeRep
+typ  =  either err id . etyp
+  where
+  err (t1, t2)  =  error $ "type mismatch, cannot apply `"
+                ++ show t1 ++ "' to `" ++ show t2 ++ "'"
+
+-- | /O(n)/.
+-- Computes the type of an expression returning either the type of the given
+-- expression when possible or when there is a type error, the pair of types
+-- which produced the error.
+--
+-- > > let one = val (1 :: Int)
+-- > > let bee = val 'b'
+-- > > let absE = value "abs" (abs :: Int -> Int)
+--
+-- > > etyp one
+-- > Right Int
+--
+-- > > etyp bee
+-- > Right Char
+--
+-- > > etyp absE
+-- > Right (Int -> Int)
+--
+-- > > etyp (absE :$ one)
+-- > Right Int
+--
+-- > > etyp (absE :$ bee)
+-- > Left (Int -> Int, Char)
+--
+-- > > etyp ((absE :$ bee) :$ one)
+-- > Left (Int -> Int, Char)
+etyp :: Expr -> Either (TypeRep, TypeRep) TypeRep
+etyp (Value _ d) = Right $ dynTypeRep d
+etyp (e1 :$ e2) = case (etyp e1, etyp e2) of
+  (Right t1, Right t2) -> case t1 `funResultTy` t2 of
+                          Nothing -> Left (t1,t2)
+                          Just t  -> Right t
+  (Left e, _) -> Left e
+  (_, Left e) -> Left e
+
+-- | /O(n)/.
+-- Returns 'Just' the type of an expression
+-- or 'Nothing' when there is an error.
+--
+-- > > let one = val (1 :: Int)
+-- > > let bee = val 'b'
+-- > > let absE = value "abs" (abs :: Int -> Int)
+--
+-- > > mtyp one
+-- > Just Int
+--
+-- > > mtyp (absE :$ bee)
+-- > Nothing
+mtyp :: Expr -> Maybe TypeRep
+mtyp  =  either (const Nothing) Just . etyp
+
+-- | /O(n)/.
+-- Returns whether the given 'Expr' is ill typed.
+-- (cf. 'isWellTyped')
+--
+-- > > let one = val (1 :: Int)
+-- > > let bee = val 'b'
+-- > > let absE = value "abs" (abs :: Int -> Int)
+--
+-- > > isIllTyped (absE :$ val (1 :: Int))
+-- > False
+--
+-- > > isIllTyped (absE :$ val 'b')
+-- > True
+isIllTyped :: Expr -> Bool
+isIllTyped  =  isNothing . mtyp
+
+-- | /O(n)/.
+-- Returns whether the given 'Expr' is well typed.
+-- (cf. 'isIllTyped')
+--
+-- > > isWellTyped (absE :$ val (1 :: Int))
+-- > True
+--
+-- > > isWellTyped (absE :$ val 'b')
+-- > False
+isWellTyped :: Expr -> Bool
+isWellTyped  =  isJust . mtyp
+
+-- |  /O(n)/.
+-- 'Just' the value of an expression when possible (correct type),
+-- 'Nothing' otherwise.
+-- This does not catch errors from 'undefined' 'Dynamic' 'value's.
+--
+-- > > let one = val (1 :: Int)
+-- > > let bee = val 'b'
+-- > > let negateE = value "negate" (negate :: Int -> Int)
+--
+-- > > evaluate one :: Maybe Int
+-- > Just 1
+--
+-- > > evaluate one :: Maybe Char
+-- > Nothing
+--
+-- > > evaluate bee :: Maybe Int
+-- > Nothing
+--
+-- > > evaluate bee :: Maybe Char
+-- > Just 'b'
+--
+-- > > evaluate $ negateE :$ one :: Maybe Int
+-- > Just (-1)
+--
+-- > > evaluate $ negateE :$ bee :: Maybe Int
+-- > Nothing
+evaluate :: Typeable a => Expr -> Maybe a
+evaluate e = toDynamic e >>= fromDynamic
+
+-- | /O(n)/. 
+-- Evaluates an expression when possible (correct type).
+-- Returns a default value otherwise.
+--
+-- > > let two = val (2 :: Int)
+-- > > let three = val (3 :: Int)
+-- > > let e1 -+- e2 = value "+" ((+) :: Int->Int->Int) :$ e1 :$ e2
+--
+-- > > eval 0 $ two -+- three :: Int
+-- > 5
+--
+-- > > eval 'z' $ two -+- three :: Char
+-- > 'z'
+--
+-- > > eval 0 $ two -+- val '3' :: Int
+-- > 0
+eval :: Typeable a => a -> Expr -> a
+eval x e = fromMaybe x (evaluate e)
+
+-- | /O(n)/.
+-- Evaluates an expression when possible (correct type).
+-- Raises an error otherwise.
+--
+-- > > evl $ two -+- three :: Int
+-- > 5
+--
+-- > > evl $ two -+- three :: Bool
+-- > *** Exception: evl: cannot evaluate Expr `2 + 3 :: Int' at the Bool type
+--
+-- This may raise errors, please consider using 'eval' or 'evaluate'.
+evl :: Typeable a => Expr -> a
+evl e = r
+  where
+  r = eval err e
+  err = error $ "evl: cannot evaluate Expr `" ++ show e ++ "' at the " ++ show (typeOf r) ++ " type"
+
+-- | /O(n)/.
+-- Evaluates an expression to a terminal 'Dynamic' value when possible.
+-- Returns 'Nothing' otherwise.
+--
+-- > > toDynamic $ val (123 :: Int) :: Maybe Dynamic
+-- > Just <<Int>>
+--
+-- > > toDynamic $ value "abs" (abs :: Int -> Int) :$ val (-1 :: Int)
+-- > Just <<Int>>
+--
+-- > > toDynamic $ value "abs" (abs :: Int -> Int) :$ val 'a'
+-- > Nothing
+toDynamic :: Expr -> Maybe Dynamic
+toDynamic (Value _ x) = Just x
+toDynamic (e1 :$ e2)  = do v1 <- toDynamic e1
+                           v2 <- toDynamic e2
+                           dynApply v1 v2
+
+instance Show Expr where
+  showsPrec d e = showParen (d > 10)
+                $ showsPrecExpr 0 e
+                . showString " :: "
+                . showsTypeExpr e
+-- TODO: document Show Expr
+
+showsTypeExpr :: Expr -> String -> String
+showsTypeExpr e = case etyp e of
+  Left (t1,t2) -> showString "ill-typed # "
+                . shows t1
+                . showString " $ "
+                . shows t2
+                . showString " #"
+  Right t -> shows t
+
+showsPrecExpr :: Int -> Expr -> String -> String
+showsPrecExpr d (Value "_" _)     = showString "_" -- a hole
+showsPrecExpr d (Value ('_':s) _) = showParen (isInfix s) $ showString s -- a variable
+showsPrecExpr d (Value s _) | isInfixedPrefix s = showString $ toPrefix s
+showsPrecExpr d (Value s _) | isNegativeLiteral s = showParen (d > 0) $ showString s
+showsPrecExpr d (Value s _) = showParen sp $ showString s
+  where sp = if atomic s then isInfix s else maybe True (d >) $ outernmostPrec s
+showsPrecExpr d ((Value ":" _ :$ e1) :$ e2)
+  | isConst e1 && mtyp e1 == Just (typeOf (undefined :: Char)) =
+  case showsTailExpr e2 "" of
+    '\"':cs  -> showString ("\"" ++ (init . tail) (showsPrecExpr 0 e1 "") ++ cs)
+    cs -> showParen (d > prec ":")
+        $ showsOpExpr ":" e1 . showString ":" . showString cs
+showsPrecExpr d ((Value ":" _ :$ e1) :$ e2) =
+  case showsTailExpr e2 "" of
+    "[]" -> showString "[" . showsPrecExpr 0 e1 . showString "]"
+    '[':cs -> showString "[" . showsPrecExpr 0 e1 . showString "," . showString cs
+    cs -> showParen (d > prec ":")
+        $ showsOpExpr ":" e1 . showString ":" . showString cs
+showsPrecExpr d ee | isTuple ee = id
+    showString "("
+  . foldr1 (\s1 s2 -> s1 . showString "," . s2)
+           (showsPrecExpr 0 `map` unfoldTuple ee)
+  . showString ")"
+showsPrecExpr d ((Value f' _ :$ e1) :$ e2)
+  | isInfix f = showParen (d > prec f)
+              $ showsOpExpr f e1
+              . showString " " . showString f . showString " "
+              . showsOpExpr f e2
+  | otherwise = showParen (d > prec " ")
+              $ showString f
+              . showString " " . showsOpExpr " " e1
+              . showString " " . showsOpExpr " " e2
+  where
+  f = case f' of "_" -> "_"   -- holes are shown as _
+                 ('_':f) -> f -- on variables we drop the preceding _
+                 f -> f       -- constants as themselves
+showsPrecExpr d (Value f' _ :$ e1)
+  | isInfix f = showParen True
+              $ showsOpExpr f e1 . showString " " . showString f
+  where
+  f = case f' of "_" -> "_"   -- holes are shown as _
+                 ('_':f) -> f -- on variables we drop the preceding _
+                 f -> f       -- constants as themselves
+showsPrecExpr d (e1 :$ e2) = showParen (d > prec " ")
+                           $ showsPrecExpr (prec " ") e1
+                           . showString " "
+                           . showsPrecExpr (prec " " + 1) e2
+
+-- bad smell here, repeated code!
+showsTailExpr :: Expr -> String -> String
+showsTailExpr ((Value ":" _ :$ e1) :$ e2)
+  | isConst e1 && mtyp e1 == Just (typeOf (undefined :: Char)) =
+  case showsPrecExpr 0 e2 "" of
+    '\"':cs  -> showString ("\"" ++ (init . tail) (showsPrecExpr 0 e1 "") ++ cs)
+    cs -> showsOpExpr ":" e1 . showString ":" . showsTailExpr e2
+showsTailExpr ((Value ":" _ :$ e1) :$ e2) =
+  case showsPrecExpr 0 e2 "" of
+    "[]" -> showString "[" . showsPrecExpr 0 e1 . showString "]"
+    '[':cs -> showString "[" . showsPrecExpr 0 e1 . showString "," . showString cs
+    cs -> showsOpExpr ":" e1 . showString ":" . showsTailExpr e2
+showsTailExpr e = showsOpExpr ":" e
+
+showsOpExpr :: String -> Expr -> String -> String
+showsOpExpr op = showsPrecExpr (prec op + 1)
+
+showOpExpr :: String -> Expr -> String
+showOpExpr op = showPrecExpr (prec op + 1)
+
+showPrecExpr :: Int -> Expr -> String
+showPrecExpr n e = showsPrecExpr n e ""
+
+-- | /O(n)/.
+-- Returns a string representation of an expression.
+-- Differently from 'show' (@:: Expr -> String@)
+-- this function does not include the type in the output.
+--
+-- > > putStrLn $ showExpr (one -+- two)
+-- > 1 + 2
+--
+-- > > putStrLn $ showExpr $ (pp -||- true) -&&- (qq -||- false)
+-- > (p || True) && (q || False)
+showExpr :: Expr -> String
+showExpr = showPrecExpr 0
+
+-- | /O(n)/.
+--   Does not evaluate values when comparing, but rather uses their
+--   representation as strings and their types.
+--
+--   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
+
+instance Ord Expr where
+  compare = compareComplexity <> lexicompare
+-- TODO: document Ord Expr
+
+-- | /O(n)/.
+-- Compares the complexity of two 'Expr's.
+-- An expression /e1/ is /strictly simpler/ than another expression /e2/
+-- if the first of the following conditions to distingish between them is:
+--
+-- 1. /e1/ is smaller in size\/length than /e2/,
+--    e.g.: @x + y < x + (y + z)@;
+--
+-- 2. or, /e1/ has more distinct variables than /e2/,
+--    e.g.: @x + y < x + x@;
+--
+-- 3. or, /e1/ has more variable occurrences than /e2/,
+--    e.g.: @x + x < 1 + x@;
+--
+-- 4. or, /e1/ has fewer distinct constants than /e2/,
+--    e.g.: @1 + 1 < 0 + 1@.
+--
+-- They're otherwise considered of equal complexity,
+-- e.g.: @x + y@ and @y + z@.
+--
+-- > > (xx -+- yy) `compareComplexity` (xx -+- (yy -+- zz))
+-- > LT
+--
+-- > > (xx -+- yy) `compareComplexity` (xx -+- xx)
+-- > LT
+--
+-- > > (xx -+- xx) `compareComplexity` (one -+- xx)
+-- > LT
+--
+-- > > (one -+- one) `compareComplexity` (zero -+- one)
+-- > LT
+--
+-- > > (xx -+- yy) `compareComplexity` (yy -+- zz)
+-- > EQ
+--
+-- > > (zero -+- one) `compareComplexity` (one -+- zero)
+-- > EQ
+compareComplexity :: Expr -> Expr -> Ordering
+compareComplexity  =  (compare      `on` length . values)
+                   <> (flip compare `on` length . nubVars)
+                   <> (flip compare `on` length . vars)
+                   <> (compare      `on` length . nubConsts)
+
+lexicompareBy :: (Expr -> Expr -> Ordering) -> Expr -> Expr -> Ordering
+lexicompareBy compareConstants = cmp
+  where
+  e1@(Value ('_':s1) _) `cmp` e2@(Value ('_':s2) _)  =  typ e1 `compareTy` typ e2
+                                                     <> s1 `compare` s2
+  (f :$ x)        `cmp` (g :$ y)                     =  f  `cmp` g <> x `cmp` y
+  (_ :$ _)        `cmp` _                            =  GT
+  _               `cmp` (_ :$ _)                     =  LT
+  _               `cmp` Value ('_':_) _              =  GT
+  Value ('_':_) _ `cmp` _                            =  LT
+  e1@(Value _ _)        `cmp` e2@(Value _ _)         =  e1 `compareConstants` e2
+  -- Var < Constants < Apps
+
+lexicompareConstants :: Expr -> Expr -> Ordering
+lexicompareConstants = cmp
+  where
+  e1 `cmp` e2 | typ e1 /= typ e2 = typ e1 `compareTy` typ e2
+  Value s1 _ `cmp` Value s2 _ = s1 `compare` s2
+  _ `cmp` _ = error "lexicompareConstants can only compare constants"
+
+lexicompare :: Expr -> Expr -> Ordering
+lexicompare = lexicompareBy lexicompareConstants
+
+-- | /O(n)/.
+-- Unfold a function application 'Expr' into a list of function and
+-- arguments.
+--
+-- > unfoldApp $ e0                    =  [e0]
+-- > unfoldApp $ e0 :$ e1              =  [e0,e1]
+-- > unfoldApp $ e0 :$ e1 :$ e2        =  [e0,e1,e2]
+-- > unfoldApp $ e0 :$ e1 :$ e2 :$ e3  =  [e0,e1,e2,e3]
+--
+-- Remember ':$' is left-associative, so:
+--
+-- > unfoldApp e0                          =  [e0]
+-- > unfoldApp (e0 :$ e1)                  =  [e0,e1]
+-- > unfoldApp ((e0 :$ e1) :$ e2)          =  [e0,e1,e2]
+-- > unfoldApp (((e0 :$ e1) :$ e2) :$ e3)  =  [e0,e1,e2,e3]
+unfoldApp :: Expr -> [Expr]
+unfoldApp e  =  u e []
+  where
+  u (ef :$ ex) = u ef . (ex:)
+  u ex         = (ex:)
+
+-- | /O(n)/.
+-- Unfold a tuple 'Expr' into a list of values.
+--
+-- > > let pair' a b = value "," ((,) :: Bool->Char->(Bool,Char)) :$ a :$ b
+--
+-- > > pair' (val True) (val 'a')
+-- > (True,'a') :: (Bool,Char)
+--
+-- > > unfoldTuple $ pair' (val True) (val 'a')
+-- > [True :: Bool,'a' :: Char]
+--
+-- > > let trio' a b c = value ",," ((,,) :: Bool->Char->Int->(Bool,Char,Int)) :$ a :$ b :$ c
+--
+-- > > trio' (val False) (val 'b') (val (9 :: Int))
+-- > (False,'b',9) :: (Bool,Char,Int)
+--
+-- > > unfoldTuple $ trio' (val False) (val 'b') (val (9 :: Int))
+-- > [False :: Bool,'b' :: Char,9 :: Int]
+--
+-- NOTE: this function returns an empty list when the representation of the
+--       tupling function is @(,)@, @(,,)@, @(,,,)@ or @(,,,...)@.
+--       This is intentional, allowing the 'Show' 'Expr' instance
+--       to present @(,) 1 2@ differently than @(1,2)@.
+unfoldTuple :: Expr -> [Expr]
+unfoldTuple = u . unfoldApp
+  where
+  u (Value cs _:es) | not (null es) && cs == replicate (length es - 1) ',' = es
+  u _   = []
+
+isTuple :: Expr -> Bool
+isTuple = not . null . unfoldTuple
+
+-- | /O(n)/.
+-- Check if an 'Expr' has a variable.  (By convention, any value whose
+-- 'String' representation starts with @'_'@.)
+--
+-- > > hasVar $ value "not" not :$ val True
+-- > False
+--
+-- > > hasVar $ value "&&" (&&) :$ var "p" (undefined :: Bool) :$ val True
+-- > True
+hasVar :: Expr -> Bool
+hasVar (e1 :$ e2)  =  hasVar e1 || hasVar e2
+hasVar e           =  isVar e
+
+-- | /O(n)/.
+-- Returns whether a 'Expr' has /no/ variables.
+-- This is equivalent to "@not . hasVar@".
+--
+-- The name "ground" comes from term rewriting.
+--
+-- > > isGround $ value "not" not :$ val True
+-- > True
+--
+-- > > isGround $ value "&&" (&&) :$ var "p" (undefined :: Bool) :$ val True
+-- > False
+isGround :: Expr -> Bool
+isGround  =  not . hasVar
+
+-- | /O(1)/.
+-- Returns whether an 'Expr' is a terminal constant.
+-- (cf. 'isGround').
+--
+-- > > isConst $ var "x" (undefined :: Int)
+-- > False
+--
+-- > > isConst $ val False
+-- > True
+--
+-- > > isConst $ value "not" not :$ val False
+-- > False
+isConst :: Expr -> Bool
+isConst  (Value ('_':_) _)  =  False
+isConst  (Value _ _)        =  True
+isConst  _                  =  False
+
+-- | /O(1)/.
+-- Returns whether an 'Expr' is a terminal variable ('var').
+-- (cf. 'hasVar').
+--
+-- > > isVar $ var "x" (undefined :: Int)
+-- > True
+--
+-- > > isVar $ val False
+-- > False
+--
+-- > > isVar $ value "not" not :$ var "p" (undefined :: Bool)
+-- > False
+isVar :: Expr -> Bool
+isVar (Value ('_':_) _)  =  True
+isVar _                  =  False
+
+-- | /O(1)/.
+-- Returns whether an 'Expr' is a terminal value ('Value').
+--
+-- > > isValue $ var "x" (undefined :: Int)
+-- > True
+--
+-- > > isValue $ val False
+-- > True
+--
+-- > > isValue $ value "not" not :$ var "p" (undefined :: Bool)
+-- > False
+--
+-- This is equivalent to pattern matching the 'Value' constructor.
+--
+-- /Properties:/
+--
+-- * @ isValue (Value e)  =  True @
+--
+-- * @ isValue (e1 :$ e2)  =  False @
+--
+-- * @ isValue  =  not . isApp @
+--
+-- * @ isValue e  =  isVar e || isConst e @
+isValue :: Expr -> Bool
+isValue (Value _ _)  =  True
+isValue _            =  False
+
+-- | /O(1)/.
+-- Returns whether an 'Expr' is an application (':$').
+--
+-- > > isApp $ var "x" (undefined :: Int)
+-- > False
+--
+-- > > isApp $ val False
+-- > False
+--
+-- > > isApp $ value "not" not :$ var "p" (undefined :: Bool)
+-- > True 
+--
+-- This is equivalent to pattern matching the ':$' constructor.
+--
+-- /Properties:/
+--
+-- * @ isApp (e1 :$ e2)  =  True @
+--
+-- * @ isApp (Value e)  =  False @
+--
+-- * @ isApp  =  not . isValue @
+--
+-- * @ isApp e  =  not (isVar e) && not (isConst e) @
+isApp :: Expr -> Bool
+isApp (_ :$ _)  =  True
+isApp _         =  False
+
+-- | /O(n)/ for the spine, /O(n^2)/ for full evaluation.
+-- Lists subexpressions of a given expression in order and with repetitions.
+-- This includes the expression itself and partial function applications.
+-- (cf. 'nubSubexprs')
+--
+-- > > subexprs (xx -+- yy)
+-- > [ x + y :: Int
+-- > , (x +) :: Int -> Int
+-- > , (+) :: Int -> Int -> Int
+-- > , x :: Int
+-- > , y :: Int
+-- > ]
+--
+-- > > subexprs (pp -&&- (pp -&&- true))
+-- > [ p && (p && True) :: Bool
+-- > , (p &&) :: Bool -> Bool
+-- > , (&&) :: Bool -> Bool -> Bool
+-- > , p :: Bool
+-- > , p && True :: Bool
+-- > , (p &&) :: Bool -> Bool
+-- > , (&&) :: Bool -> Bool -> Bool
+-- > , p :: Bool
+-- > , True :: Bool
+-- > ]
+subexprs :: Expr -> [Expr]
+subexprs e  =  s e []
+  where
+  s :: Expr -> [Expr] -> [Expr]
+  s e@(e1 :$ e2)  =  (e:) . s e1 . s e2
+  s e             =  (e:)
+
+-- | /O(n log n)/ for the spine, /O(n^2)/ for full evaluation.
+-- Lists all subexpressions of a given expression without repetitions.
+-- This includes the expression itself and partial function applications.
+-- (cf. 'subexprs')
+--
+-- > > nubSubexprs (xx -+- yy)
+-- > [ x :: Int
+-- > , y :: Int
+-- > , (+) :: Int -> Int -> Int
+-- > , (x +) :: Int -> Int
+-- > , x + y :: Int
+-- > ]
+--
+-- > > nubSubexprs (pp -&&- (pp -&&- true))
+-- > [ p :: Bool
+-- > , True :: Bool
+-- > , (&&) :: Bool -> Bool -> Bool
+-- > , (p &&) :: Bool -> Bool
+-- > , p && True :: Bool
+-- > , p && (p && True) :: Bool
+-- > ]
+nubSubexprs :: Expr -> [Expr]
+nubSubexprs  =  s
+  where
+  s e@(e1 :$ e2)  =  [e] +++ s e1 +++ s e2
+  s e             =  [e]
+-- TODO: After deciding if I'll stick to this implementation or the old one,
+-- add note about complexity:
+--
+-- While this is /O(n log n)/ on average it is /O(n^2)/ in the worst-case.
+-- Worst case = f (g (h (i (j (k (l (m (n x))))))))
+
+-- | /O(n)/.
+-- Lists all terminal values in an expression in order and with repetitions.
+-- (cf. 'nubValues')
+--
+-- > > values (xx -+- yy)
+-- > [ (+) :: Int -> Int -> Int
+-- > , x :: Int
+-- > , y :: Int ]
+--
+-- > > values (xx -+- (yy -+- zz))
+-- > [ (+) :: Int -> Int -> Int
+-- > , x :: Int
+-- > , (+) :: Int -> Int -> Int
+-- > , y :: Int
+-- > , z :: Int ]
+--
+-- > > values (zero -+- (one -*- two))
+-- > [ (+) :: Int -> Int -> Int
+-- > , 0 :: Int
+-- > , (*) :: Int -> Int -> Int
+-- > , 1 :: Int
+-- > , 2 :: Int ]
+--
+-- > > values (pp -&&- true)
+-- > [ (&&) :: Bool -> Bool -> Bool
+-- > , p :: Bool
+-- > , True :: Bool ]
+values :: Expr -> [Expr]
+values e  =  v e []
+  where
+  v :: Expr -> [Expr] -> [Expr]
+  v (e1 :$ e2)  =  v e1 . v e2
+  v e           =  (e:)
+
+-- | /O(n log n)/.
+-- Lists all terminal values in an expression without repetitions.
+-- (cf. 'values')
+--
+-- > > nubValues (xx -+- yy)
+-- > [ x :: Int
+-- > , y :: Int
+-- > , (+) :: Int -> Int -> Int ]
+--
+-- > > nubValues (xx -+- (yy -+- zz))
+-- > [ x :: Int
+-- > , y :: Int
+-- > , z :: Int
+-- > , (+) :: Int -> Int -> Int ]
+--
+-- > > nubValues (zero -+- (one -*- two))
+-- > [ 0 :: Int
+-- > , 1 :: Int
+-- > , 2 :: Int
+-- > , (*) :: Int -> Int -> Int
+-- > , (+) :: Int -> Int -> Int ]
+--
+-- > > nubValues (pp -&&- pp)
+-- > [ p :: Bool
+-- > , (&&) :: Bool -> Bool -> Bool ]
+nubValues :: Expr -> [Expr]
+nubValues  =  v
+  where
+  v (e1 :$ e2)  =  v e1 +++ v e2
+  v e           =  [e]
+
+-- | /O(n)/.
+-- List terminal constants in an expression in order and with repetitions.
+-- (cf. 'nubConsts')
+--
+-- > > consts (xx -+- yy)
+-- > [ (+) :: Int -> Int -> Int ]
+--
+-- > > consts (xx -+- (yy -+- zz))
+-- > [ (+) :: Int -> Int -> Int
+-- > , (+) :: Int -> Int -> Int ]
+--
+-- > > consts (zero -+- (one -*- two))
+-- > [ (+) :: Int -> Int -> Int
+-- > , 0 :: Int
+-- > , (*) :: Int -> Int -> Int
+-- > , 1 :: Int
+-- > , 2 :: Int ]
+--
+-- > > consts (pp -&&- true)
+-- > [ (&&) :: Bool -> Bool -> Bool
+-- > , True :: Bool ]
+consts :: Expr -> [Expr]
+consts  =  filter isConst . values
+
+-- | /O(n log n)/.
+-- List terminal constants in an expression without repetitions.
+-- (cf. 'consts')
+--
+-- > > nubConsts (xx -+- yy)
+-- > [ (+) :: Int -> Int -> Int ]
+--
+-- > > nubConsts (xx -+- (yy -+- zz))
+-- > [ (+) :: Int -> Int -> Int ]
+--
+-- > > nubConsts (pp -&&- true)
+-- > [ True :: Bool
+-- > , (&&) :: Bool -> Bool -> Bool ]
+nubConsts :: Expr -> [Expr]
+nubConsts  =  c
+  where
+  c (e1 :$ e2)  =  c e1 +++ c e2
+  c e           =  [e | isConst e]
+
+-- | /O(n)/.
+-- Lists all variables in an expression in order and with repetitions.
+-- (cf. 'nubVars')
+--
+-- > > vars (xx -+- yy)
+-- > [ x :: Int
+-- > , y :: Int ]
+--
+-- > > vars (xx -+- (yy -+- xx))
+-- > [ x :: Int
+-- > , y :: Int
+-- > , x :: Int ]
+--
+-- > > vars (zero -+- (one -*- two))
+-- > []
+--
+-- > > vars (pp -&&- true)
+-- > [p :: Bool]
+vars :: Expr -> [Expr]
+vars  =  filter isVar . values
+
+-- | /O(n log n)/.
+-- Lists all variables in an expression without repetitions.
+-- (cf. 'vars')
+--
+-- > > nubVars (yy -+- xx)
+-- > [ x :: Int
+-- > , y :: Int ]
+--
+-- > > nubVars (xx -+- (yy -+- xx))
+-- > [ x :: Int
+-- > , y :: Int ]
+--
+-- > > nubVars (zero -+- (one -*- two))
+-- > []
+--
+-- > > nubVars (pp -&&- true)
+-- > [p :: Bool]
+nubVars :: Expr -> [Expr]
+nubVars  =  v
+  where
+  v (e1 :$ e2)  =  v e1 +++ v e2
+  v e           =  [e | isVar e]
+
+-- | /O(n)/.
+-- Return the arity of the given expression,
+-- i.e. the number of arguments that its type takes.
+--
+-- > > arity (val 0)
+-- > 0
+--
+-- > > arity (val False)
+-- > 0
+--
+-- > > arity (value "id" (id :: Int -> Int))
+-- > 1
+--
+-- > > arity (value "const" (const :: Int -> Int -> Int))
+-- > 2
+--
+-- > > arity (one -+- two)
+-- > 0
+arity :: Expr -> Int
+arity  =  tyArity . typ
+
+-- | /O(n)/.
+-- Returns the size of the given expression,
+-- i.e. the number of terminal values in it.
+--
+-- > > size zero
+-- > 1
+--
+-- > > size (one -+- two)
+-- > 3
+--
+-- > > size (abs' one)
+-- > 2
+size :: Expr -> Int
+size  =  length . values
+
+-- | /O(n)/.
+-- Returns the maximum depth of a given expression
+-- given by the maximum number of nested function applications.
+-- Curried function application is counted /only once/,
+-- i.e. the application of a two argument function
+-- increases the depth of both its arguments by one.
+-- (cf. 'height')
+--
+-- > > depth zero
+-- > 1
+--
+-- > > depth (one -+- two)
+-- > 2
+--
+-- > > depth (abs' one -+- two)
+-- > 3
+--
+-- Flipping arguments of applications in any of the subterms
+-- does not affect the result.
+depth :: Expr -> Int
+depth e@(_:$_)  =  1 + maximum (map depth $ unfoldApp e)
+depth _         =  1
+
+-- | /O(n)/.
+-- Returns the maximum height of a given expression
+-- given by the maximum number of nested function applications.
+-- Curried function application is counted /each time/,
+-- i.e. the application of a two argument function
+-- increases the depth of its first argument by two
+-- and of its second argument by one.
+-- (cf. 'depth')
+--
+-- > > height zero
+-- > 1
+--
+-- > > height (abs' one)
+-- > 2
+--
+-- > > height ((const' one) two)
+-- > 3
+--
+-- > > height ((const' (abs' one)) two)
+-- > 4
+--
+-- > > height ((const' one) (abs' two))
+-- > 3
+--
+-- Flipping arguments of applications in subterms
+-- may change the result of the function.
+height :: Expr -> Int
+height (e1 :$ e2)  =  1 + height e1 `max` height e2
+height _           =  1
diff --git a/src/Data/Express/Express.hs b/src/Data/Express/Express.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Express.hs
@@ -0,0 +1,243 @@
+-- |
+-- Module      : Data.Express.Express
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Defines the 'Express' type class.
+{-# LANGUAGE CPP #-}
+module Data.Express.Express (Express (..)) where
+
+import Data.Express.Core
+import Data.Typeable
+import Data.Ratio
+
+-- |
+-- 'Express' typeclass instances provide an 'expr' function
+-- that allows values to be deeply encoded as applications of 'Expr's.
+--
+-- > expr False  =  val False
+-- > expr (Just True)  =  value "Just" (Just :: Bool -> Maybe Bool) :$ val True
+--
+-- The function 'expr' can be contrasted with the function 'val':
+--
+-- * 'val' always encodes values as atomic 'Value' 'Expr's --
+--   shallow encoding.
+-- * 'expr' ideally encodes expressions as applications (':$')
+--   between 'Value' 'Expr's --
+--   deep encoding.
+--
+-- Depending on the situation, one or the other may be desirable.
+--
+-- Instances can be automatically derived using the TH function
+-- 'Data.Express.Express.Derive.deriveExpress'.
+--
+-- The following example shows a datatype and its instance:
+--
+-- > data Stack a = Stack a (Stack a) | Empty
+--
+-- > instance Express a => Express (Stack a) where
+-- >   expr s@(Stack x y) = value "Stack" (Stack ->>: s) :$ expr x :$ expr y
+-- >   expr s@Empty       = value "Empty" (Empty   -: s)
+--
+-- To declare 'expr' it may be useful to use auxiliary type binding operators:
+-- '-:', '->:', '->>:', ....
+--
+-- For types with atomic values, just declare @ expr = val @
+class Typeable a => Express a where
+  expr :: a -> Expr
+
+instance Express ()        where  expr  =  val
+instance Express Bool      where  expr  =  val
+instance Express Int       where  expr  =  val
+instance Express Integer   where  expr  =  val
+instance Express Char      where  expr  =  val
+instance Express Ordering  where  expr  =  val
+
+instance Express a => Express (Maybe a) where
+  expr mx@Nothing   =  value "Nothing" (Nothing -: mx)
+  expr mx@(Just x)  =  value "Just"    (Just   ->: mx) :$ expr x
+
+instance (Express a, Express b) => Express (Either a b) where
+  expr lx@(Left x)   =  value "Left"  (Left  ->: lx) :$ expr x
+  expr ry@(Right y)  =  value "Right" (Right ->: ry) :$ expr y
+
+instance (Express a, Express b) => Express (a,b) where
+  expr (x,y)  =  value "," ((,) ->>: (x,y))
+              :$ expr x :$ expr y
+
+instance (Express a, Express b, Express c) => Express (a,b,c) where
+  expr (x,y,z)  =  value ",," ((,,) ->>>: (x,y,z))
+                :$ expr x :$ expr y :$ expr z
+
+instance (Express a, Express b, Express c, Express d) => Express (a,b,c,d) where
+  expr (x,y,z,w)  =  value ",,," ((,,,) ->>>>: (x,y,z,w))
+                  :$ expr x :$ expr y :$ expr z :$ expr w
+
+instance Express a => Express [a] where
+  expr xs@[]
+    | typeOf xs == typeOf ""  =  value "\"\"" ([] -: xs)
+    | otherwise               =  value "[]" ([] -: xs)
+  expr xs@(y:ys)              =  value ":"  ((:) ->>: xs) :$ expr y :$ expr ys
+
+
+-- instances of further types and arities --
+
+instance (Integral a, Show a, Express a) => Express (Ratio a) where
+  expr  =  val
+-- 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
+
+instance (Express a, Express b, Express c, Express d, Express e)
+      => Express (a,b,c,d,e) where
+  expr (x,y,z,w,v)  =  value ",,,," ((,,,,) ->>>>>: (x,y,z,w,v))
+                    :$ expr x :$ expr y :$ expr z :$ expr w :$ expr v
+
+instance (Express a, Express b, Express c, Express d, Express e, Express f)
+      => Express (a,b,c,d,e,f) where
+  expr (x,y,z,w,v,u)  =  value ",,,,," ((,,,,,) ->>>>>>: (x,y,z,w,v,u))
+                    :$ expr x :$ expr y :$ expr z :$ expr w :$ expr v :$ expr u
+
+instance ( Express a, Express b, Express c, Express d, Express e, Express f
+         , Express g )
+      => Express (a,b,c,d,e,f,g) where
+  expr (x,y,z,w,v,u,t)  =  value ",,,,,," ((,,,,,,) ->>>>>>>: (x,y,z,w,v,u,t))
+                        :$ expr x :$ expr y :$ expr z :$ expr w
+                        :$ expr v :$ expr u :$ expr t
+
+#if __GLASGOW_HASKELL__ < 710
+-- No 8-tuples for you:
+-- On GHC 7.8, 8-tuples are not Typeable instances.  We could add a standalone
+-- deriving clause, but that may cause trouble if some other library does the
+-- same.  User should declare Generalizable 8-tuples manually when using GHC <=
+-- 7.8.
+#else
+instance ( Express a, Express b, Express c, Express d, Express e, Express f
+         , Express g, Express h )
+      => Express (a,b,c,d,e,f,g,h) where
+  expr (x,y,z,w,v,u,t,s)  =
+    value ",,,,,,," ((,,,,,,,) ->>>>>>>>: (x,y,z,w,v,u,t,s))
+      :$ expr x :$ expr y :$ expr z :$ expr w
+      :$ expr v :$ expr u :$ expr t :$ expr s
+
+instance ( Express a, Express b, Express c, Express d, Express e, Express f
+         , Express g, Express h, Express i )
+      => Express (a,b,c,d,e,f,g,h,i) where
+  expr (x,y,z,w,v,u,t,s,r)  =
+    value ",,,,,,,," ((,,,,,,,,) ->>>>>>>>>: (x,y,z,w,v,u,t,s,r))
+      :$ expr x :$ expr y :$ expr z :$ expr w
+      :$ expr v :$ expr u :$ expr t :$ expr s
+      :$ expr r
+
+instance ( Express a, Express b, Express c, Express d, Express e, Express f
+         , Express g, Express h, Express i, Express j )
+      => Express (a,b,c,d,e,f,g,h,i,j) where
+  expr (x,y,z,w,v,u,t,s,r,q)  =
+    value ",,,,,,,,," ((,,,,,,,,,) ->>>>>>>>>>: (x,y,z,w,v,u,t,s,r,q))
+      :$ expr x :$ expr y :$ expr z :$ expr w
+      :$ expr v :$ expr u :$ expr t :$ expr s
+      :$ expr r :$ expr q
+
+instance ( Express a, Express b, Express c, Express d, Express e, Express f
+         , Express g, Express h, Express i, Express j, Express k )
+      => Express (a,b,c,d,e,f,g,h,i,j,k) where
+  expr (x,y,z,w,v,u,t,s,r,q,p)  =
+    value ",,,,,,,,,," ((,,,,,,,,,,) ->>>>>>>>>>>: (x,y,z,w,v,u,t,s,r,q,p))
+      :$ expr x :$ expr y :$ expr z :$ expr w
+      :$ expr v :$ expr u :$ expr t :$ expr s
+      :$ expr r :$ expr q :$ expr p
+
+instance ( Express a, Express b, Express c, Express d, Express e, Express f
+         , Express g, Express h, Express i, Express j, Express k, Express l )
+      => Express (a,b,c,d,e,f,g,h,i,j,k,l) where
+  expr (x,y,z,w,v,u,t,s,r,q,p,o)  =
+    value ",,,,,,,,,,," ((,,,,,,,,,,,) ->>>>>>>>>>>>: (x,y,z,w,v,u,t,s,r,q,p,o))
+      :$ expr x :$ expr y :$ expr z :$ expr w
+      :$ expr v :$ expr u :$ expr t :$ expr s
+      :$ expr r :$ expr q :$ expr p :$ expr o
+#endif
+
+-- type binding utilities --
+
+(-:) :: a -> a -> a
+(-:) = asTypeOf -- const
+infixl 1 -:
+
+(-:>) :: (a -> b) -> a -> (a -> b)
+(-:>) = const
+infixl 1 -:>
+
+(->:) :: (a -> b) -> b -> (a -> b)
+(->:) = const
+infixl 1 ->:
+
+(->:>) :: (a -> b -> c) -> b -> (a -> b -> c)
+(->:>) = const
+infixl 1 ->:>
+
+(->>:) :: (a -> b -> c) -> c -> (a -> b -> c)
+(->>:) = const
+infixl 1 ->>:
+
+(->>:>) :: (a -> b -> c -> d) -> c -> (a -> b -> c -> d)
+(->>:>) = const
+infixl 1 ->>:>
+
+(->>>:) :: (a -> b -> c -> d) -> d -> (a -> b -> c -> d)
+(->>>:) = const
+infixl 1 ->>>:
+
+(->>>:>) :: (a -> b -> c -> d -> e) -> d -> (a -> b -> c -> d -> e)
+(->>>:>) = const
+infixl 1 ->>>:>
+
+(->>>>:) :: (a -> b -> c -> d -> e) -> e -> (a -> b -> c -> d -> e)
+(->>>>:) = const
+infixl 1 ->>>>:
+
+(->>>>:>) :: (a -> b -> c -> d -> e -> f) -> e -> (a -> b -> c -> d -> e -> f)
+(->>>>:>) = const
+infixl 1 ->>>>:>
+
+(->>>>>:) :: (a -> b -> c -> d -> e -> f) -> f -> (a -> b -> c -> d -> e -> f)
+(->>>>>:) = const
+infixl 1 ->>>>>:
+
+(->>>>>:>) :: (a->b->c->d->e->f->g) -> f -> (a->b->c->d->e->f->g)
+(->>>>>:>) = const
+infixl 1 ->>>>>:>
+
+(->>>>>>:) :: (a->b->c->d->e->f->g) -> g -> (a->b->c->d->e->f->g)
+(->>>>>>:) = const
+infixl 1 ->>>>>>:
+
+(->>>>>>>:) :: (a->b->c->d->e->f->g->h) -> h -> (a->b->c->d->e->f->g->h)
+(->>>>>>>:) = const
+infixl 1 ->>>>>>>:
+
+(->>>>>>>>:) :: (a->b->c->d->e->f->g->h->i) -> i -> (a->b->c->d->e->f->g->h->i)
+(->>>>>>>>:) = const
+infixl 1 ->>>>>>>>:
+
+(->>>>>>>>>:) :: (a->b->c->d->e->f->g->h->i->j) -> j
+              -> (a->b->c->d->e->f->g->h->i->j)
+(->>>>>>>>>:) = const
+infixl 1 ->>>>>>>>>:
+
+(->>>>>>>>>>:) :: (a->b->c->d->e->f->g->h->i->j->k) -> k
+               -> (a->b->c->d->e->f->g->h->i->j->k)
+(->>>>>>>>>>:) = const
+infixl 1 ->>>>>>>>>>:
+
+(->>>>>>>>>>>:) :: (a->b->c->d->e->f->g->h->i->j->k->l) -> l
+                -> (a->b->c->d->e->f->g->h->i->j->k->l)
+(->>>>>>>>>>>:) = const
+infixl 1 ->>>>>>>>>>>:
+
+(->>>>>>>>>>>>:) :: (a->b->c->d->e->f->g->h->i->j->k->l->m) -> m
+                 -> (a->b->c->d->e->f->g->h->i->j->k->l->m)
+(->>>>>>>>>>>>:) = const
+infixl 1 ->>>>>>>>>>>>:
diff --git a/src/Data/Express/Express/Derive.hs b/src/Data/Express/Express/Derive.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Express/Derive.hs
@@ -0,0 +1,70 @@
+{-# LANGUAGE TemplateHaskell, CPP #-}
+-- |
+-- Module      : Data.Express.Express.Derive
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Allows automatic derivation of 'Express' typeclass instances.
+module Data.Express.Express.Derive
+  ( deriveExpress
+  , deriveExpressCascading
+  , deriveExpressIfNeeded
+  )
+where
+
+import Data.Express.Core
+import Data.Express.Express
+
+import Control.Monad
+import Data.Char
+import Data.List
+import Data.Express.Utils.TH
+
+-- | Derives an 'Express' instance for the given type 'Name'.
+--
+-- This function needs the @TemplateHaskell@ extension.
+deriveExpress :: Name -> DecsQ
+deriveExpress  =  deriveWhenNeededOrWarn ''Express reallyDeriveExpress
+
+-- | Same as 'deriveExpress' but does not warn when instance already exists
+--   ('deriveExpress' is preferable).
+deriveExpressIfNeeded :: Name -> DecsQ
+deriveExpressIfNeeded  =  deriveWhenNeeded ''Express reallyDeriveExpress
+
+-- | Derives a 'Express' instance for a given type 'Name'
+--   cascading derivation of type arguments as well.
+deriveExpressCascading :: Name -> DecsQ
+deriveExpressCascading  =  deriveWhenNeeded ''Express reallyDeriveExpressCascading
+
+reallyDeriveExpress :: Name -> DecsQ
+reallyDeriveExpress t  =  do
+  isEq <- t `isInstanceOf` ''Eq
+  isOrd <- t `isInstanceOf` ''Ord
+  (nt,vs) <- normalizeType t
+#if __GLASGOW_HASKELL__ >= 710
+  cxt <- sequence [ [t| $(conT c) $(return v) |]
+#else
+  -- template-haskell <= 2.9.0.0:
+  cxt <- sequence [ classP c [return v]
+#endif
+                  | c <- ''Express:([''Eq | isEq] ++ [''Ord | isOrd])
+                  , v <- vs]
+  cs <- typeConstructorsArgNames t
+  asName <- newName "x"
+  let generalizableExpr = mergeIFns $ foldr1 mergeI
+        [ do retTypeOf <- lookupValN $ "-" ++ replicate (length ns) '>' ++ ":"
+             let exprs = [[| expr $(varE n) |] | n <- ns]
+             let conex = [| $(varE retTypeOf) $(conE c) $(varE asName) |]
+             let root = [| value $(stringE $ showJustName c) $(conex) |]
+             let rhs = foldl (\e1 e2 -> [| $e1 :$ $e2 |]) root exprs
+             [d| instance Express $(return nt) where
+                   expr $(asP asName $ conP c (map varP ns)) = $rhs |]
+        | (c,ns) <- cs
+        ]
+  cxt |=>| generalizableExpr
+
+-- Not only really derive Express instances,
+-- but cascade through argument types.
+reallyDeriveExpressCascading :: Name -> DecsQ
+reallyDeriveExpressCascading  =  reallyDeriveCascading ''Express reallyDeriveExpress
diff --git a/src/Data/Express/Fixtures.hs b/src/Data/Express/Fixtures.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Fixtures.hs
@@ -0,0 +1,1048 @@
+-- |
+-- Module      : Data.Express.Fixtures
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Defines some 'Expr' fixtures to facilitate testing and playing around on
+-- the REPL (GHCI).
+--
+-- * Instead of having to write:
+--
+--     > > value "&&" (&&) :$ (value "not" not :$ val True) :$ val False
+--     > not True && False :: Bool
+--
+--     Using this module, we can just write:
+--
+--     > > not' true -&&- false
+--     > not True && False :: Bool
+--
+-- * Instead of having to write:
+--
+--     > > value "+" ((+)::Int->Int->Int) :$ (value "*" ((*)::Int->Int->Int) :$ var "x" (undefined::Int) :$ var "y" (undefined::Int)) :$ (value "*" ((*)::Int->Int->Int) :$ val (1::Int) :$ val (2::Int))
+--     > x * y + 1 * 2 :: Int
+--
+--     Using this module, we can just write:
+--
+--     > > xx -*- yy -+- one -*- two
+--     > x * y + 1 * 2 :: Int
+--
+-- * Instead of having to write:
+--
+--     > > value "||" (||) :$ (value "==" ((==)::Int->Int->Bool) :$ val (3::Int) :$ (value "+" ((+)::Int->Int->Int) :$ var "y" (undefined::Int) :$ val (1::Int))) :$ (value "not" not :$ val False)
+--     > 3 == y + 1 || not False :: Bool
+--
+--     We can just write:
+--
+--     > > (three -==- yy -+- one) -||- not' false
+--     > x == y + 1 || not False :: Bool
+--
+-- This exports over a hundred symbols
+-- to be used mainly when writing unit tests
+-- or playing around on GHCi.
+--
+-- Since the 'Expr' type only allows monomorphic values,
+-- encoded polimorphic values are monomorphized
+-- usually to the 'Int' type.
+--
+-- /Beware:/ lifted 'Expr' functions sometimes work for different types.
+-- The current version does not have a rationale for types that are included:
+-- you have to either try around on the REPL or look at the source to really know.
+module Data.Express.Fixtures
+  (
+  -- * Convenience re-export
+    module Data.Express
+
+  -- * Functions and values encoded as Expr or functions of Exprs
+  -- | The naming rules are:
+  --
+  -- * 'Int's are encoded using their English names,
+  --   e.g.: 'zero', 'one', 'two';
+  --
+  -- * 'Char's are encoded using their English names,
+  --   e.g.: 'bee', 'cee', 'dee';
+  --
+  -- * 0-argument constructors are encoded in lowercase,
+  --   e.g.: 'false', 'true', 'nothing', 'just';
+  --
+  -- * lifted constructors are lowercased,
+  --   e.g.: 'just';
+  --
+  -- * lifted functions are primed
+  --   e.g.: 'id'', 'negate'', 'head'';
+  --
+  -- * lifted operators are surrounded by dashes,
+  --   e.g.: '-+-', '-*-', '-&&-', '-||-', '-:-'.
+  --
+  -- * operators are encoded using their English names,
+  --   e.g.: 'plus', 'times', 'cons';
+  --
+  -- * encoded functions are followed by @E@,
+  --   e.g.: 'idE', 'notE', 'absE';
+  --
+  -- * variables have the first character duplicated,
+  --   e.g.: 'xx', 'yy', 'xxs';
+  --
+  -- * encoded values may have the element type appended,
+  --   e.g.: 'idInt', 'idBool', 'justInt', 'nilChar'.
+  --
+  -- Unqualified polymorphic constructors and functions
+  -- have their element types bound to 'Int'.
+  --
+  -- There are exceptions to the above rules such as:
+  -- when a name would conflict with a Prelude function.
+  -- (e.g.: 'orE' and 'andE')
+
+  -- ** Booleans
+  , b_, pp, qq, rr, pp'
+  , false
+  , true
+  , notE
+  , orE
+  , andE
+  , implies
+  , not'
+  , (-||-)
+  , (-&&-)
+  , (-==>-)
+  , (-==-)
+  , (-/=-)
+  , (-<=-)
+  , (-<-)
+  , compare'
+
+  -- ** Integers
+  , i_, xx, yy, zz, xx'
+  , ii, jj, kk, ii'
+  , zero, one, two, three, minusOne, minusTwo
+  , idE, negateE, absE
+  , idInt
+  , idBool
+  , idChar
+  , idInts
+  , idBools
+  , idString
+  , id', const', negate', abs'
+  , plus, times
+  , (-+-), (-*-)
+  , ff, ffE
+  , gg, ggE
+  , (-?-)
+  , (-$-)
+  , odd'
+  , even'
+
+  -- ** Chars
+  , c_
+  , cc, dd, ccs
+  , ae, bee, cee, dee
+  , space, lineBreak
+  , ord'
+  , ordE
+
+  -- ** Lists
+  , is_
+  , xxs
+  , yys
+  , nil
+  , emptyString
+  , nilInt
+  , nilBool
+  , nilChar
+  , cons
+  , consInt
+  , consBool
+  , consChar
+  , (-:-)
+  , unit
+  , (-++-)
+  , head'
+  , tail'
+  , length'
+  , elem'
+  , sort'
+  , insert'
+
+  -- ** Maybes
+  , nothing
+  , nothingInt
+  , nothingBool
+  , just
+  , justInt
+  , justBool
+
+  -- ** Tuples
+  , comma
+  , pair
+  , (-|-)
+  , triple
+  , quadruple
+  , quintuple
+  , sixtuple
+  )
+where
+
+import Data.Express
+import Data.Maybe
+import Data.Typeable (Typeable, typeOf)
+import Data.Char
+import Data.List
+
+int :: Int
+int  =  undefined
+
+bool :: Bool
+bool  =  undefined
+
+char :: Char
+char  =  undefined
+
+string :: String
+string  =  undefined
+
+-- | 'Expr' representing a hole of 'Bool' type.
+--
+-- > > b_
+-- > _ :: Bool
+b_ :: Expr
+b_  =  hole bool
+
+-- | 'Expr' representing a variable @p :: `Bool`@.
+--
+-- > > pp
+-- > p :: Bool
+pp :: Expr
+pp  =  var "p" bool
+
+-- | 'Expr' representing a variable @q :: `Bool`@.
+--
+-- > > qq
+-- > q :: Bool
+qq :: Expr
+qq  =  var "q" bool
+
+-- | 'Expr' representing a variable @r :: `Bool`@.
+--
+-- > > rr
+-- > r :: Bool
+rr :: Expr
+rr  =  var "r" bool
+
+-- | 'Expr' representing a variable @p' :: `Bool`@.
+--
+-- > > pp'
+-- > p' :: Bool
+pp' :: Expr
+pp'  =  var "p'" bool
+
+
+-- | 'False' encoded as an 'Expr'.
+--
+-- > > false
+-- > False :: Bool
+false :: Expr
+false  =  val False
+
+-- | 'True' encoded as an 'Expr'.
+--
+-- > > true
+-- > True :: Bool
+true :: Expr
+true  =  val True
+
+-- | The function 'not' encoded as an 'Expr'.
+--
+-- > > notE
+-- > not :: Bool -> Bool
+notE :: Expr
+notE  =  value "not" not
+
+-- | The function 'and' encoded as an 'Expr'.
+--
+-- > > andE
+-- > (&&) :: Bool -> Bool -> Bool
+andE :: Expr
+andE  =  value "&&" (&&)
+
+-- | The function 'or' encoded as an 'Expr'.
+--
+-- > > orE
+-- > (||) :: Bool -> Bool -> Bool
+orE :: Expr
+orE  =  value "||" (||)
+
+(-==>-) :: Expr -> Expr -> Expr
+e1 -==>- e2  =  implies :$ e1 :$ e2
+
+implies :: Expr
+implies  =  value "==>" (==>)
+  where
+  False ==> _  =  True
+  True  ==> p  =  p
+
+-- | The function 'not' lifted over the 'Expr' type.
+--
+-- > > not' false
+-- > not False :: Bool
+--
+-- > > evalBool $ not' false
+-- > True
+--
+-- > > not' pp
+-- > not p :: Bool
+not' :: Expr -> Expr
+not' pp  =  notE :$ pp
+
+-- | The function '&&' lifted over the 'Expr' type.
+--
+-- > > pp -&&- qq
+-- > p && q :: Bool
+--
+-- > > false -&&- true
+-- > False && True :: Bool
+--
+-- > > evalBool $ false -&&- true
+-- > False
+(-&&-) :: Expr -> Expr -> Expr
+pp -&&- qq  =  andE :$ pp :$ qq
+
+-- | The function '||' lifted over the 'Expr' type.
+--
+-- > > pp -||- qq
+-- > p || q :: Bool
+--
+-- > > false -||- true
+-- > False || True :: Bool
+--
+-- > > evalBool $ false -||- true
+-- > True
+(-||-) :: Expr -> Expr -> Expr
+pp -||- qq  =  orE :$ pp :$ qq
+
+-- | A typed hole of 'Int' type.
+--
+-- > > i_
+-- > _ :: Int
+i_ :: Expr
+i_  =  hole int
+
+-- | A variable @x@ of 'Int' type.
+--
+-- > > xx
+-- > x :: Int
+xx :: Expr
+xx  =  var "x" int
+
+-- | A variable @y@ of 'Int' type.
+--
+-- > > yy
+-- > y :: Int
+yy :: Expr
+yy  =  var "y" int
+
+-- | A variable @z@ of 'Int' type.
+--
+-- > > zz
+-- > z :: Int
+zz :: Expr
+zz  =  var "z" int
+
+-- | A variable @x'@ of 'Int' type.
+--
+-- > > xx'
+-- > x' :: Int
+xx' :: Expr
+xx'  =  var "x'" int
+
+ii :: Expr
+ii  =  var "i" int
+
+jj :: Expr
+jj  =  var "j" int
+
+kk :: Expr
+kk  =  var "k" int
+
+ii' :: Expr
+ii'  =  var "i'" int
+
+-- | The value @0@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > zero
+-- > 0 :: Int
+zero :: Expr
+zero  =  val (0 :: Int)
+
+-- | The value @1@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > one
+-- > 1 :: Int
+one :: Expr
+one  =  val (1 :: Int)
+
+-- | The value @2@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > two
+-- > 2 :: Int
+two :: Expr
+two  =  val (2 :: Int)
+
+-- | The value @3@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > three
+-- > 3 :: Int
+three :: Expr
+three  =  val (3 :: Int)
+
+-- | The value @-1@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > minusOne
+-- > -1 :: Int
+minusOne :: Expr
+minusOne  =  val (-1 :: Int)
+
+-- | The value @-2@ bound to the 'Int' type encoded as an 'Expr'.
+--
+-- > > minusOne
+-- > -2 :: Int
+minusTwo :: Expr
+minusTwo  =  val (-2 :: Int)
+
+-- | A variable function @f@ of 'Int -> Int' type lifted over the 'Expr' type.
+--
+-- > > ff xx
+-- > f x :: Int
+--
+-- > > ff one
+-- > f 1 :: Int
+ff :: Expr -> Expr
+ff = (ffE :$)
+
+-- | A variable @f@ of 'Int -> Int' type encoded as an 'Expr'.
+--
+-- > > ffE
+-- > f :: Int -> Int
+ffE :: Expr
+ffE = var "f" (undefined :: Int -> Int)
+
+-- | A variable function @g@ of 'Int -> Int' type lifted over the 'Expr' type.
+--
+-- > > gg yy
+-- > g y :: Int
+--
+-- > > gg minusTwo
+-- > gg (-2) :: Int
+gg :: Expr -> Expr
+gg = (ggE :$)
+
+-- | A variable binary operator @?@ lifted over the 'Expr' type.
+--   Works for 'Int', 'Bool', 'Char', @[Int]@ and 'String'.
+--
+-- > > xx -?- yy
+-- > x ? y :: Int
+--
+-- > > pp -?- qq
+-- > p ? q :: Bool
+--
+-- > > xx -?- qq
+-- > *** Exception: (-?-): cannot apply `(?) :: * -> * -> *` to `x :: Int' and `q :: Bool'.  Unhandled types?
+(-?-) :: Expr -> Expr -> Expr
+ex -?- ey  =  fromMaybe err $ ($$ ey) $ headOr err $ mapMaybe ($$ ex)
+  [ var "?" (undefined :: Int -> Int -> Int)
+  , var "?" (undefined :: Bool -> Bool -> Bool)
+  , var "?" (undefined :: Char -> Char -> Char)
+  , var "?" (undefined :: [Int] -> [Int] -> [Int])
+  , var "?" (undefined :: String -> String -> String)
+  ]
+  where
+  err  =  error $ "(-?-): cannot apply `(?) :: * -> * -> *` to `"
+               ++ show ex ++ "' and `" ++ show ey ++ "'.  Unhandled types?"
+
+-- | A variable @g@ of 'Int -> Int' type encoded as an 'Expr'.
+--
+-- > > ggE
+-- > g :: Int -> Int
+ggE :: Expr
+ggE = var "g" (undefined :: Int -> Int)
+
+-- | The operator '+' for the 'Int' type for use on 'Expr's.  (See also 'plus'.)
+--
+-- > > two -+- three
+-- > 2 + 3 :: Int
+--
+-- > > minusOne -+- minusTwo -+- zero
+-- > ((-1) + (-2)) + 0 :: Int
+--
+-- > > xx -+- (yy -+- zz)
+-- > x + (y + z) :: Int
+(-+-) :: Expr -> Expr -> Expr
+e1 -+- e2 = plus :$ e1 :$ e2
+infixl 6 -+-
+
+-- | The operator '+' for the 'Int' type.  (See also '-+-'.)
+--
+-- > > plus
+-- > (+) :: Int -> Int -> Int
+--
+-- > > plus :$ one
+-- > (1 +) :: Int -> Int
+--
+-- > > plus :$ xx :$ yy
+-- > x + y :: Int
+plus :: Expr
+plus = value "+" ((+) :: Int -> Int -> Int)
+
+-- | The operator '*' for the 'Int' type lifted over the 'Expr' type.  (See also 'times'.)
+--
+-- > > three -*- three
+-- > 9 :: Int
+--
+-- > > one -*- two -*- three
+-- > (1 * 2) * 3 :: Int
+--
+-- > > two -*- xx
+-- > 2 * x :: Int
+(-*-) :: Expr -> Expr -> Expr
+e1 -*- e2 = times :$ e1 :$ e2
+
+-- | The operator '*' for the 'Int' type.  (See also '-*-'.)
+--
+-- > > times
+-- > (*) :: Int -> Int -> Int
+--
+-- > > times :$ two
+-- > (2 *) :: Int -> Int
+--
+-- > > times :$ xx :$ yy
+-- > x * y :: Int
+times :: Expr
+times  =  value "*" ((*) :: Int -> Int -> Int)
+
+minus :: Expr
+minus  =  value "-" ((-) :: Int -> Int -> Int)
+
+-- | Constructs an application of 'id' as an 'Expr'.
+--   Only works for 'Int', 'Bool', 'Char', 'String', @[Int]@, @[Bool]@.
+--
+-- > > id' yy
+-- > id yy :: Int
+--
+-- > > id' one
+-- > id 1 :: Int
+--
+-- > > evl (id' one) :: Int
+-- > 1
+--
+-- > > id' pp
+-- > id p :: Bool
+--
+-- > > id' false
+-- > id' False :: Bool
+--
+-- > > evl (id' true) :: Bool
+-- > True :: Bool
+id' :: Expr -> Expr
+id' e  =  headOr err $ mapMaybe ($$ e)
+  [ idInt
+  , idBool
+  , idChar
+  , idInts
+  , idBools
+  , idString
+  ]
+  where
+  err  =  error $ "id': unhandled type " ++ show (typ e)
+
+-- | The function 'id' for the 'Int' type encoded as an 'Expr'.  (See also 'id''.)
+--
+-- > > idE :$ xx
+-- > id x :: Int
+--
+-- > > idE :$ zero
+-- > id 0 :: Int
+--
+-- > > evaluate $ idE :$ zero :: Maybe Int
+-- > Just 0
+idE :: Expr
+idE  =  idInt
+
+-- | The function 'id' encoded as an 'Expr'.  (cf. 'id'')
+idInt,idBool,idChar,idInts,idBools,idString :: Expr
+idInt     =  value "id" (id :: Id Int)
+idBool    =  value "id" (id :: Id Bool)
+idChar    =  value "id" (id :: Id Char)
+idInts    =  value "id" (id :: Id [Int])
+idBools   =  value "id" (id :: Id [Bool])
+idString  =  value "id" (id :: Id String)
+type Id a = a -> a
+
+const' :: Expr -> Expr -> Expr
+const' e1 e2  =  (:$ e2) . headOr err $ mapMaybe ($$ e1)
+  [ value "const" (const :: Int -> Int -> Int)
+  , value "const" (const :: Bool -> Bool -> Bool)
+  , value "const" (const :: Char -> Char -> Char)
+  , value "const" (const :: [Int] -> [Int] -> [Int])
+  , value "const" (const :: [Bool] -> [Bool] -> [Bool])
+  , value "const" (const :: String -> String -> String)
+  ]
+  where
+  err  =  error $ "const': unhandled type " ++ show (typ e1)
+
+-- | 'negate' over the 'Int' type lifted over the 'Expr' type.
+--
+-- > > negate' xx
+-- > negate x :: Int
+--
+-- > > evl (negate' one) :: Int
+-- > -1
+negate' :: Expr -> Expr
+negate' e  =  negateE :$ e
+
+-- | 'negate' over the 'Int' type encoded as an 'Expr'
+--
+-- > > negateE
+-- > negate :: Int -> Int
+negateE :: Expr
+negateE  =  value "negate" (negate :: Int -> Int)
+
+-- | 'abs' over the 'Int' type lifted over the 'Expr' type.
+--
+-- > > abs' xx'
+-- > abs x' :: Int
+--
+-- > > evl (abs' minusTwo) :: Int
+-- > 2
+abs' :: Expr -> Expr
+abs' e  =  absE :$ e
+
+-- | 'abs' over the 'Int' type encoded as an 'Expr'.
+--
+-- > > absE
+-- > abs :: Int -> Int
+absE :: Expr
+absE  =  value "abs" (abs :: Int -> Int)
+
+odd' :: Expr -> Expr
+odd' = (oddE :$) where oddE = value "odd" (odd :: Int -> Bool)
+
+even' :: Expr -> Expr
+even' = (evenE :$) where evenE = value "even" (even :: Int -> Bool)
+
+-- | A hole of 'Char' type encoded as an 'Expr'.
+--
+-- > > c_
+-- > _ :: Char
+c_ :: Expr
+c_  =  hole char
+
+cc :: Expr
+cc  =  var "c" char
+
+dd :: Expr
+dd  =  var "d" char
+
+ccs :: Expr
+ccs  =  var "cs" [char]
+
+-- The English name for letter 'a' is not really 'ae', but simply 'a'.
+ae :: Expr
+ae  =  val 'a'
+
+-- | The character @\'b\'@ encoded as an 'Expr'
+--
+-- > > bee
+-- > 'b' :: Char
+--
+-- > > evl bee :: Char
+-- > 'b'
+bee :: Expr
+bee  =  val 'b'
+
+-- | The character @\'c\'@ encoded as an 'Expr'
+--
+-- > > cee
+-- > 'c' :: Char
+--
+-- > > evl cee :: Char
+-- > 'c'
+cee :: Expr
+cee  =  val 'c'
+
+-- | The character @\'d\'@ encoded as an 'Expr'
+--
+-- > > dee
+-- > 'd' :: Char
+--
+-- > > evl dee :: Char
+-- > 'd'
+dee :: Expr
+dee  =  val 'd'
+
+space :: Expr
+space = val ' '
+
+lineBreak :: Expr
+lineBreak = val '\n'
+
+ord' :: Expr -> Expr
+ord' = (ordE :$)
+
+ordE :: Expr
+ordE = value "ord" ord
+
+-- | A typed hole of @[Int]@ type encoded as an 'Expr'.
+--
+-- > > is_
+-- > _ :: [Int]
+is_ :: Expr
+is_  =  hole [int]
+
+-- | A variable named @xs@ of type @[Int]@ encoded as an 'Expr'.
+--
+-- > > xxs
+-- > xs :: [Int]
+xxs :: Expr
+xxs  =  var "xs" [int]
+
+-- | A variable named @ys@ of type @[Int]@ encoded as an 'Expr'.
+--
+-- > > yys
+-- > ys :: [Int]
+yys :: Expr
+yys  =  var "ys" [int]
+
+-- | An empty list of type @[Int]@ encoded as an 'Expr'.
+--
+-- > > nil
+-- > [] :: [Int]
+nil :: Expr
+nil  =  nilInt
+
+-- | An empty 'String' encoded as an 'Expr'.
+--
+-- > > emptyString
+-- > "" :: String
+emptyString :: Expr
+emptyString  =  val ""
+
+-- | The empty list '[]' encoded as an 'Expr'.
+nilInt, nilBool, nilChar :: Expr
+nilInt   =  val ([] :: [Int])
+nilBool  =  val ([] :: [Bool])
+nilChar  =  value "[]" ([] :: [Char])
+
+-- | The list constructor with 'Int' as element type encoded as an 'Expr'.
+--
+-- > > cons
+-- > (:) :: Int -> [Int] -> [Int]
+--
+-- > > cons :$ one :$ nil
+-- > [1] :: [Int]
+--
+-- Consider using '-:-' and 'unit' when building lists of 'Expr'.
+cons :: Expr
+cons  =  consInt
+
+-- | The list constructor @ : @ encoded as an 'Expr'.
+consInt, consBool, consChar :: Expr
+consInt   =  value ":" ((:) :: Cons Int)
+consBool  =  value ":" ((:) :: Cons Bool)
+consChar  =  value ":" ((:) :: Cons Char)
+type Cons a = a -> [a] -> [a]
+
+-- | 'unit' constructs a list with a single element.
+--   This works for elements of type 'Int', 'Char' and 'Bool'.
+--
+-- > > unit one
+-- > [1]
+--
+-- > > unit false
+-- > [False]
+unit :: Expr -> Expr
+unit e  =  e -:- nil'
+  where
+  nil' | typ e == typ i_  =  nil
+       | typ e == typ c_  =  emptyString
+       | typ e == typ b_  =  nilBool
+
+-- | The list constructor lifted over the 'Expr' type.
+--   Works for the element types 'Int', 'Char' and 'Bool'.
+--
+-- > > zero -:- one -:- unit two
+-- > [0,1,2] :: [Int]
+--
+-- > > zero -:- one -:- two -:- nil
+-- > [0,1,2] :: [Int]
+--
+-- > > bee -:- unit cee
+-- > "bc" :: [Char]
+(-:-) :: Expr -> Expr -> Expr
+e1 -:- e2  =  (:$ e2) . headOr err $ mapMaybe ($$ e1)
+  [ consInt
+  , consBool
+  , consChar
+  , value ":" ((:) :: Cons (Maybe Int))
+  ]
+  where
+  err  =  error $ "(-:-): unhandled type " ++ show (typ e1)
+infixr 5 -:-
+
+-- | List concatenation lifted over the 'Expr' type.
+--   Works for the element types 'Int', 'Char' and 'Bool'.
+--
+-- > > (zero -:- one -:- nil) -:- (two -:- three -:- nil)
+-- > [0,1] -++- [2,3] :: [Int]
+--
+-- > > (bee -:- unit cee) -:- unit dee
+-- > "bc" -++- "c" :: [Char]
+(-++-) :: Expr -> Expr -> Expr
+e1 -++- e2 = (:$ e2) . headOr err $ mapMaybe ($$ e1)
+  [ value "++" ((++) :: [Int] -> [Int] -> [Int])
+  , value "++" ((++) :: String -> String -> String)
+  , value "++" ((++) :: [Bool] -> [Bool] -> [Bool])
+  ]
+  where
+  err  =  error $ "(-++-): unhandled type " ++ show (typ e1)
+infixr 5 -++-
+
+-- | List 'head' lifted over the 'Expr' type.
+--   Works for the element types 'Int', 'Char' and 'Bool'.
+--
+-- > > head' $ unit one
+-- > head [1] :: Int
+--
+-- > > head' $ unit bee
+-- > head "b" :: Char
+--
+-- > > head' $ zero -:- unit two
+-- > head [0,2] :: Int
+--
+-- > > evl $ head' $ unit one :: Int
+-- > 1
+head' :: Expr -> Expr
+head' exs = headOr err $ mapMaybe ($$ exs)
+  [ value "head" (head :: [Int] -> Int)
+  , value "head" (head :: [Char] -> Char)
+  , value "head" (head :: [Bool] -> Bool)
+  ]
+  where
+  err  =  error $ "head': cannot apply `head :: [a] -> a` to `" ++ show exs ++ "'."
+
+-- | List 'tail' lifted over the 'Expr' type.
+--   Works for the element types 'Int', 'Char' and 'Bool'.
+--
+-- > > tail' $ unit one
+-- > tail [1] :: [Int]
+--
+-- > > tail' $ unit bee
+-- > tail "b" :: [Char]
+--
+-- > > tail' $ zero -:- unit two
+-- > tail [0,2] :: [Int]
+--
+-- > > evl $ tail' $ zero -:- unit two :: [Int]
+-- > [2]
+tail' :: Expr -> Expr
+tail' exs = headOr err $ mapMaybe ($$ exs)
+  [ value "tail" (tail :: [Int] -> [Int])
+  , value "tail" (tail :: [Char] -> [Char])
+  , value "tail" (tail :: [Bool] -> [Bool])
+  ]
+  where
+  err  =  error $ "tail': unhandled type " ++ show (typ exs)
+
+-- | List 'length' lifted over the 'Expr' type.
+--   Works for the element types 'Int', 'Char' and 'Bool'.
+--
+-- > > length' $ unit one
+-- > length [1] :: Int
+--
+-- > > length' $ unit bee
+-- > length "b" :: Int
+--
+-- > > length' $ zero -:- unit two
+-- > length [0,2] :: Int
+--
+-- > > evl $ length' $ unit one :: Int
+-- > 1
+length' :: Expr -> Expr
+length' exs = headOr err $ mapMaybe ($$ exs)
+  [ value "length" (length :: [Int] -> Int)
+  , value "length" (length :: [Char] -> Int)
+  , value "length" (length :: [Bool] -> Int)
+  ]
+  where
+  err  =  error $ "length': cannot apply `length :: [a] -> a` to `" ++ show exs ++ "'."
+
+sort' :: Expr -> Expr
+sort' exs = headOr err $ mapMaybe ($$ exs)
+  [ value "sort" (sort :: [Int] -> [Int])
+  , value "sort" (sort :: [Char] -> [Char])
+  , value "sort" (sort :: [Bool] -> [Bool])
+  ]
+  where
+  err  =  error $ "sort': unhandled type " ++ show (typ exs)
+
+insert' :: Expr -> Expr -> Expr
+insert' ex exs  =  (:$ exs) . headOr err $ mapMaybe ($$ ex)
+  [ value "insert" (insert :: Int -> [Int] -> [Int])
+  , value "insert" (insert :: Bool -> [Bool] -> [Bool])
+  , value "insert" (insert :: Char -> String -> String)
+  ]
+  where
+  err  =  error $ "insert': unhandled type " ++ show (typ ex)
+
+elem' :: Expr -> Expr -> Expr
+elem' ex exs  =  (:$ exs) . headOr err $ mapMaybe ($$ ex)
+  [ value "elem" (elem :: Int -> [Int] -> Bool)
+  , value "elem" (elem :: Bool -> [Bool] -> Bool)
+  , value "elem" (elem :: Char -> String -> Bool)
+  ]
+  where
+  err  =  error $ "elem': unhandled type " ++ show (typ ex)
+
+(-$-) :: Expr -> Expr -> Expr
+ef -$- ex = (:$ ex) . headOr err $ mapMaybe ($$ ef)
+  [ value "$" (($) :: Apply Int)
+  , value "$" (($) :: Apply Bool)
+  , value "$" (($) :: Apply Char)
+  , value "$" (($) :: Apply [Int])
+  , value "$" (($) :: Apply [Bool])
+  , value "$" (($) :: Apply [Char])
+  ]
+  where
+  err  =  error $ "(-$-): unhandled type " ++ show (typ ef)
+infixl 6 -$-
+type Apply a = (a -> a) -> a -> a
+
+(-==-) :: Expr -> Expr -> Expr
+ex -==- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
+  [ value "==" ((==) :: Comparison ())
+  , value "==" ((==) :: Comparison Int)
+  , value "==" ((==) :: Comparison Bool)
+  , value "==" ((==) :: Comparison Char)
+  , value "==" ((==) :: Comparison [Int])
+  , value "==" ((==) :: Comparison [Bool])
+  , value "==" ((==) :: Comparison [Char])
+  ]
+  where
+  err  =  error $ "(-==-): unhandled type " ++ show (typ ex)
+infix 4 -==-
+type Comparison a = a -> a -> Bool
+
+(-/=-) :: Expr -> Expr -> Expr
+ex -/=- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
+  [ value "/=" ((/=) :: Comparison ())
+  , value "/=" ((/=) :: Comparison Int)
+  , value "/=" ((/=) :: Comparison Bool)
+  , value "/=" ((/=) :: Comparison Char)
+  , value "/=" ((/=) :: Comparison [Int])
+  , value "/=" ((/=) :: Comparison [Bool])
+  , value "/=" ((/=) :: Comparison [Char])
+  ]
+  where
+  err  =  error $ "(-/=-): unhandled type " ++ show (typ ex)
+infix 4 -/=-
+
+(-<=-) :: Expr -> Expr -> Expr
+ex -<=- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
+  [ value "<=" ((<=) :: Comparison ())
+  , value "<=" ((<=) :: Comparison Int)
+  , value "<=" ((<=) :: Comparison Bool)
+  , value "<=" ((<=) :: Comparison Char)
+  , value "<=" ((<=) :: Comparison [Int])
+  , value "<=" ((<=) :: Comparison [Bool])
+  , value "<=" ((<=) :: Comparison [Char])
+  ]
+  where
+  err  =  error $ "(-<=-): unhandled type " ++ show (typ ex)
+infix 4 -<=-
+
+(-<-) :: Expr -> Expr -> Expr
+ex -<- ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
+  [ value "<" ((<) :: Comparison ())
+  , value "<" ((<) :: Comparison Int)
+  , value "<" ((<) :: Comparison Bool)
+  , value "<" ((<) :: Comparison Char)
+  , value "<" ((<) :: Comparison [Int])
+  , value "<" ((<) :: Comparison [Bool])
+  , value "<" ((<) :: Comparison [Char])
+  ]
+  where
+  err  =  error $ "(-<-): unhandled type " ++ show (typ ex)
+infix 4 -<-
+
+compare' :: Expr -> Expr -> Expr
+compare' ex ey  =  (:$ ey) . headOr err $ mapMaybe ($$ ex)
+  [ value "compare" (compare :: Compare ())
+  , value "compare" (compare :: Compare Int)
+  , value "compare" (compare :: Compare Bool)
+  , value "compare" (compare :: Compare Char)
+  , value "compare" (compare :: Compare [Int])
+  , value "compare" (compare :: Compare [Bool])
+  , value "compare" (compare :: Compare [Char])
+  ]
+  where
+  err  =  error $ "(-<-): unhandled type " ++ show (typ ex)
+type Compare a = a -> a -> Ordering
+
+nothing :: Expr
+nothing  =  nothingInt
+
+nothingInt, nothingBool :: Expr
+nothingInt   =  val (Nothing :: Maybe Int)
+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
+
+just :: Expr -> Expr
+just ex  =  headOr err $ mapMaybe ($$ ex)
+  [ justInt
+  , justBool
+  ]
+  where
+  err  =  error $ "just: unhandled type " ++ show (typ ex)
+
+(-|-) :: Expr -> Expr -> Expr
+(-|-) = pair
+
+pair :: Expr -> Expr -> Expr
+pair x y  =  comma :$ x :$ y
+  where
+  comma  =  case (show $ typ x, show $ typ y) of
+            ("Int", "Int")  -> value "," ((,) :: Pair Int Int)
+            ("Int", "Bool") -> value "," ((,) :: Pair Int Bool)
+            ("Bool","Int")  -> value "," ((,) :: Pair Bool Int)
+            ("Bool","Bool") -> value "," ((,) :: Pair Bool Bool)
+            (t,t')          -> error $ "(-:-): unhandled types " ++ t ++ " " ++ t'
+type Pair a b = a -> b -> (a,b)
+
+comma :: Expr
+comma = value "," ((,) :: Pair Int Int)
+
+triple :: Expr -> Expr -> Expr -> Expr
+triple e1 e2 e3 = ccE :$ e1 :$ e2 :$ e3
+  where
+  ccE = value ",," ((,,) :: Int -> Int -> Int -> (Int,Int,Int))
+
+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))
+
+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))
+
+sixtuple :: Expr -> Expr -> Expr -> Expr -> Expr -> Expr -> Expr
+sixtuple e1 e2 e3 e4 e5 e6 = cccccE :$ e1 :$ e2 :$ e3 :$ e4 :$ e5 :$ e6
+  where
+  cccccE = value ",,,,," ((,,,,,) :: Int -> Int -> Int -> Int -> Int -> Int -> (Int,Int,Int,Int,Int,Int))
+
+headOr :: a -> [a] -> a
+headOr x []     =  x
+headOr _ (x:_)  =  x
diff --git a/src/Data/Express/Fold.hs b/src/Data/Express/Fold.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Fold.hs
@@ -0,0 +1,133 @@
+-- |
+-- Module      : Data.Express.Fold
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Defines utilities for folding and unfolding 'Expr's.
+{-# LANGUAGE DeriveDataTypeable #-} -- for GHC <= 7.8
+module Data.Express.Fold
+  ( fold
+  , unfold
+  , foldPair
+  , unfoldPair
+  , foldTrio
+  , unfoldTrio
+  , foldApp
+  , unfoldApp
+  )
+where
+
+import Data.Express.Core
+import Data.Typeable (Typeable) -- for GHC <= 7.8
+
+data ExprPair = ExprPair
+  deriving Typeable -- for GHC <= 7.8
+
+-- | /O(n)/.
+-- Folds a list of 'Expr' with function application (':$').
+-- This reverses the effect of 'unfoldApp'.
+--
+-- > foldApp [e0]           =  e0
+-- > foldApp [e0,e1]        =  e0 :$ e1
+-- > foldApp [e0,e1,e2]     =  e0 :$ e1 :$ e2
+-- > foldApp [e0,e1,e2,e3]  =  e0 :$ e1 :$ e2 :$ e3
+--
+-- Remember ':$' is left-associative, so:
+--
+-- > foldApp [e0]           =    e0
+-- > foldApp [e0,e1]        =   (e0 :$ e1)
+-- > foldApp [e0,e1,e2]     =  ((e0 :$ e1) :$ e2)
+-- > foldApp [e0,e1,e2,e3]  = (((e0 :$ e1) :$ e2) :$ e3)
+--
+-- This function /may/ produce an ill-typed expression.
+foldApp :: [Expr] -> Expr
+foldApp = foldl1 (:$)
+
+-- | /O(1)/.
+-- Folds a pair of 'Expr' values into a single 'Expr'.
+-- (cf. 'unfoldPair')
+--
+-- This /always/ generates an ill-typed expression.
+--
+-- > > foldPair (val False, val (1::Int))
+-- > (False,1) :: ill-typed # ExprPair $ Bool #
+--
+-- > > foldPair (val (0::Int), val True)
+-- > (0,True) :: ill-typed # ExprPair $ 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)
+-- > > unfoldPair $ canonicalize $ foldPair (ii,kk)
+-- > (x :: Int,y :: Int)
+foldPair :: (Expr,Expr) -> Expr
+foldPair (e1,e2)  =  value "," (undefined :: ExprPair) :$ e1 :$ e2
+
+-- | /O(1)/.
+-- Unfolds an 'Expr' representing a pair.
+-- This reverses the effect of 'foldPair'.
+--
+-- > > value "," ((,) :: Bool->Bool->(Bool,Bool)) :$ val True :$ val False
+-- > (True,False) :: (Bool,Bool)
+-- > > unfoldPair $ value "," ((,) :: Bool->Bool->(Bool,Bool)) :$ val True :$ val False
+-- > (True :: Bool,False :: Bool)
+unfoldPair :: Expr -> (Expr,Expr)
+unfoldPair (Value "," _ :$ e1 :$ e2) = (e1,e2)
+unfoldPair (Value "(,)" _ :$ e1 :$ e2) = (e1,e2)
+unfoldPair _  =  error "unfoldPair: not an Expr pair"
+
+data ExprTrio = ExprTrio
+  deriving Typeable -- for GHC <= 7.8
+
+foldTrio :: (Expr,Expr,Expr) -> Expr
+foldTrio (e1,e2,e3)  =  value ",," (undefined :: ExprTrio) :$ e1 :$ e2 :$ e3
+
+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'.
+-- (cf. 'unfold')
+--
+-- This /always/ generates an ill-typed expression.
+--
+-- > fold [val False, val True, val (1::Int)]
+-- > [False,True,1] :: ill-typed # ExprList $ Bool #
+--
+-- This is useful when applying transformations on lists 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 qq = var "q" (undefined::Bool)
+-- > > let notE = value "not" not
+-- > > unfold . canonicalize . fold $ [ii,kk,notE :$ qq, notE :$ val False]
+-- > [x :: Int,y :: Int,not p :: Bool,not False :: Bool]
+fold :: [Expr] -> Expr
+fold []      =  value "[]" ExprList
+fold (e:es)  =  value ":"  ExprList :$ e :$ fold es
+
+-- | /O(n)/.
+-- Unfolds an 'Expr' representing a list into a list of 'Expr's.
+-- This reverses the effect of 'fold'.
+--
+-- > > expr [1,2,3::Int]
+-- > [1,2,3] :: [Int]
+-- > > unfold $ expr [1,2,3::Int]
+-- > [1 :: Int,2 :: Int,3 :: Int]
+unfold :: Expr -> [Expr]
+unfold   (Value "[]" _)               =  []
+unfold (((Value ":"  _) :$ e) :$ es)  =  e : unfold es
+unfold e  =  error $ "unfold: cannot unfold expression: " ++ show e
diff --git a/src/Data/Express/Hole.hs b/src/Data/Express/Hole.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Hole.hs
@@ -0,0 +1,180 @@
+-- |
+-- Module      : Data.Express.Hole
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Utilities for manipulating variables and typed holes encoded as 'Expr's.
+{-# LANGUAGE CPP #-}
+module Data.Express.Hole
+  (
+  -- * Creating variables
+    varAsTypeOf
+  , listVars
+  , listVarsAsTypeOf
+
+  -- * Typed holes
+  , hole
+  , isHole
+  , holes
+  , nubHoles
+  , holeAsTypeOf
+  )
+where
+
+import Data.Express.Core
+
+import Data.Dynamic
+import Data.Maybe (fromMaybe)
+import Data.Express.Utils.Typeable (tyArity)
+import Data.Express.Utils.List (nubSort)
+import Data.Express.Utils.String (variableNamesFromTemplate)
+
+-- | /O(1)/.
+-- Creates a 'var'iable with the same type as the given 'Expr'.
+--
+-- > > let one = val (1::Int)
+-- > > "x" `varAsTypeOf` one
+-- > x :: Int
+--
+-- > > "p" `varAsTypeOf` val False
+-- > p :: Bool
+varAsTypeOf :: String -> Expr -> Expr
+varAsTypeOf n = Value ('_':n) . undefine . fromMaybe err . toDynamic
+  where
+  err = error "varAsTypeOf: could not compile Dynamic value, type error?"
+  undefine :: Dynamic -> Dynamic
+#if __GLASGOW_HASKELL__ >= 806
+  undefine (Dynamic t v) = (Dynamic t undefined)
+#else
+  undefine = id -- there's no way to do this using the old Data.Dynamic API.
+#endif
+
+-- | /O(1)/.
+-- Creates an 'Expr' representing a typed hole with the type of the given
+-- 'Expr'. (cf. 'hole')
+--
+-- > > val (1::Int)
+-- > 1 :: Int
+-- > > holeAsTypeOf $ val (1::Int)
+-- > _ :: Int
+holeAsTypeOf :: Expr -> Expr
+holeAsTypeOf = ("" `varAsTypeOf`)
+
+-- | /O(1)/.
+-- Creates an 'Expr' representing a typed hole of the given argument type.
+--
+-- > > hole (undefined :: Int)
+-- > _ :: Int
+--
+-- > > hole (undefined :: Maybe String)
+-- > _ :: Maybe [Char]
+--
+-- A hole is represented as a variable with no name or
+-- a value named @"_"@:
+--
+-- > hole x = var "" x
+-- > hole x = value "_" x
+hole :: Typeable a => a -> Expr
+hole a = var "" (undefined `asTypeOf` a)
+
+-- | /O(1)/.
+-- Checks if an 'Expr' represents a typed hole.
+-- (cf. 'hole')
+--
+-- > > isHole $ hole (undefined :: Int)
+-- > True
+--
+-- > > isHole $ value "not" not :$ val True
+-- > False
+--
+-- > > isHole $ val 'a'
+-- > False
+isHole :: Expr -> Bool
+isHole (Value "_" _)  = True
+isHole _              = False
+
+-- | /O(n)/.
+-- Lists all holes in an expression, in order and with repetitions.
+-- (cf. 'nubHoles')
+--
+-- > > holes $ hole (undefined :: Bool)
+-- > [_ :: Bool]
+--
+-- > > holes $ value "&&" (&&) :$ hole (undefined :: Bool) :$ hole (undefined :: Bool)
+-- > [_ :: Bool,_ :: Bool]
+--
+-- > > holes $ hole (undefined :: Bool->Bool) :$ hole (undefined::Bool)
+-- > [_ :: Bool -> Bool,_ :: Bool]
+holes :: Expr -> [Expr]
+holes  =  filter isHole . values
+
+-- | /O(n log n)/.
+-- Lists all holes in an expression without repetitions.
+-- (cf. 'holes')
+--
+-- > > nubHoles $ hole (undefined :: Bool)
+-- > [_ :: Bool]
+--
+-- > > nubHoles $ value "&&" (&&) :$ hole (undefined :: Bool) :$ hole (undefined :: Bool)
+-- > [_ :: Bool]
+--
+-- > > nubHoles $ hole (undefined :: Bool->Bool) :$ hole (undefined::Bool)
+-- > [_ :: Bool,_ :: Bool -> Bool]
+nubHoles :: Expr -> [Expr]
+nubHoles  =  nubSort . holes
+
+-- |
+-- Generate an infinite list of variables
+-- based on a template and a given type.
+-- (cf. 'listVarsAsTypeOf')
+--
+-- > > putL 10 $ listVars "x" (undefined :: Int)
+-- > [ x :: Int
+-- > , y :: Int
+-- > , z :: Int
+-- > , x' :: Int
+-- > , y' :: Int
+-- > , z' :: Int
+-- > , x'' :: Int
+-- > , ...
+-- > ]
+--
+-- > > putL 10 $ listVars "p" (undefined :: Bool)
+-- > [ p :: Bool
+-- > , q :: Bool
+-- > , r :: Bool
+-- > , p' :: Bool
+-- > , q' :: Bool
+-- > , r' :: Bool
+-- > , p'' :: Bool
+-- > , ...
+-- > ]
+listVars :: Typeable a => String -> a -> [Expr]
+listVars s a  =  map (`var` a) (variableNamesFromTemplate s)
+
+-- |
+-- Generate an infinite list of variables
+-- based on a template
+-- and the type of a given 'Expr'.
+-- (cf. 'listVars')
+--
+-- > > let one = val (1::Int)
+-- > > putL 10 $ "x" `listVarsAsTypeOf` one
+-- > [ x :: Int
+-- > , y :: Int
+-- > , z :: Int
+-- > , x' :: Int
+-- > , ...
+-- > ]
+--
+-- > > let false = val False
+-- > > putL 10 $ "p" `listVarsAsTypeOf` false
+-- > [ p :: Bool
+-- > , q :: Bool
+-- > , r :: Bool
+-- > , p' :: Bool
+-- > , ...
+-- > ]
+listVarsAsTypeOf :: String -> Expr -> [Expr]
+listVarsAsTypeOf s e  =  map (`varAsTypeOf` e) (variableNamesFromTemplate s)
diff --git a/src/Data/Express/Instances.hs b/src/Data/Express/Instances.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Instances.hs
@@ -0,0 +1,359 @@
+-- |
+-- Module      : Data.Express.Instances
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Defines utilities do deal with instances of typeclasses
+module Data.Express.Instances
+  ( reifyEq
+  , reifyOrd
+  , reifyEqOrd
+  , reifyName
+
+  , mkEq
+  , mkOrd
+  , mkOrdLessEqual
+  , mkName
+  , mkNameWith
+
+  , isEq
+  , isOrd
+  , isEqOrd
+  , isEqT
+  , isOrdT
+  , isEqOrdT
+
+  , mkEquation
+  , mkComparisonLE
+  , mkComparisonLT
+  , mkComparison
+  , lookupComparison
+
+  , listVarsWith
+  , lookupName
+  , lookupNames
+
+  , validApps
+  , findValidApp
+
+  , preludeNameInstances
+  )
+where
+
+-- TODO: document and test functions of the Instances module
+
+import Data.Express.Basic
+import Data.Express.Name
+import Data.Express.Express
+import Data.Express.Utils.Typeable
+import Data.Express.Utils.List
+import Data.Maybe
+import Control.Applicative ((<$>)) -- for GHC <= 7.8
+
+
+-- reifying instances --
+
+-- | /O(1)./
+-- Reifies an 'Eq' instance into a list of 'Expr's.
+-- The list will contain '==' and '/=' for the given type.
+-- (cf. 'mkEq', 'mkEquation')
+--
+-- > > reifyEq (undefined :: Int)
+-- > [ (==) :: Int -> Int -> Bool
+-- > , (/=) :: Int -> Int -> Bool ]
+--
+-- > > reifyEq (undefined :: Bool)
+-- > [ (==) :: Bool -> Bool -> Bool
+-- > , (/=) :: Bool -> Bool -> Bool ]
+--
+-- > > reifyEq (undefined :: String)
+-- > [ (==) :: [Char] -> [Char] -> Bool
+-- > , (/=) :: [Char] -> [Char] -> Bool ]
+reifyEq :: (Typeable a, Eq a) => a -> [Expr]
+reifyEq a  =  mkEq  ((==) -:> a)
+
+-- | /O(1)./
+-- Reifies an 'Ord' instance into a list of 'Expr's.
+-- The list will contain 'compare', '<=' and '<' for the given type.
+-- (cf. 'mkOrd', 'mkOrdLessEqual', 'mkComparisonLE', 'mkComparisonLT')
+--
+-- > > reifyOrd (undefined :: Int)
+-- > [ (<=) :: Int -> Int -> Bool
+-- > , (<) :: Int -> Int -> Bool ]
+--
+-- > > reifyOrd (undefined :: Bool)
+-- > [ (<=) :: Bool -> Bool -> Bool
+-- > , (<) :: Bool -> Bool -> Bool ]
+--
+-- > > reifyOrd (undefined :: [Bool])
+-- > [ (<=) :: [Bool] -> [Bool] -> Bool
+-- > , (<) :: [Bool] -> [Bool] -> Bool ]
+reifyOrd :: (Typeable a, Ord a) => a -> [Expr]
+reifyOrd a  =  mkOrd (compare -:> a)
+
+-- | /O(1)./
+-- Reifies 'Eq' and 'Ord' instances into a list of 'Expr'.
+reifyEqOrd :: (Typeable a, Ord a) => a -> [Expr]
+reifyEqOrd a  =  reifyEq a ++ reifyOrd a
+
+-- | /O(1)./
+-- Reifies a 'Name' instance into a list of 'Expr's.
+-- The list will contain 'name' for the given type.
+-- (cf. 'mkName', 'lookupName', 'lookupNames')
+--
+-- > > reifyName (undefined :: Int)
+-- > [name :: Int -> [Char]]
+--
+-- > > reifyName (undefined :: Bool)
+-- > [name :: Bool -> [Char]]
+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')
+--
+-- > > mkEq ((==) :: Int -> Int -> Bool)
+-- > [ (==) :: Int -> Int -> Bool
+-- > , (/=) :: Int -> Int -> Bool ]
+mkEq :: Typeable a => (a -> a -> Bool) -> [Expr]
+mkEq (==)  =
+  [ value "==" (==)
+  , value "/=" (/=)
+  ]
+  where
+  x /= y = not (x == y)
+
+-- | /O(1)/.
+-- Builds a reified 'Ord' instance from the given 'compare' function.
+-- (cf. 'reifyOrd', 'mkOrdLessEqual')
+mkOrd :: Typeable a => (a -> a -> Ordering) -> [Expr]
+mkOrd compare  =
+  [ value "<=" (<=)
+  , value "<" (<)
+-- we don't include other Ord functions, at least for now
+--, value "compare" compare
+  ]
+  where
+  x <  y  =  x `compare` y == LT
+  x <= y  =  x `compare` y /= GT
+
+-- | /O(1)/.
+-- Builds a reified 'Ord' instance from the given '<=' function.
+-- (cf. 'reifyOrd', 'mkOrd')
+mkOrdLessEqual :: Typeable a => (a -> a -> Bool) -> [Expr]
+mkOrdLessEqual (<=)  =
+  [ value "<=" (<=)
+  , value "<" (<)
+  ]
+  where
+  x < y  =  not (y <= x)
+
+-- | /O(1)/.
+-- Builds a reified 'Name' instance from the given 'name' function.
+-- (cf. 'reifyName', 'mkNameWith')
+mkName :: Typeable a => (a -> String) -> [Expr]
+mkName name  =  [value "name" name]
+
+-- | /O(1)/.
+-- Builds a reified 'Name' instance from the given 'String' and type.
+-- (cf. 'reifyName', 'mkName')
+mkNameWith :: Typeable a => String -> a -> [Expr]
+mkNameWith n a  =  [value "name" (const n -:> a)]
+
+
+-- searching for functions --
+
+lookupComparison :: String -> TypeRep -> [Expr] -> Maybe Expr
+lookupComparison n' t  =  find (\i@(Value n _) -> n == n' && typ i == mkComparisonTy t)
+
+-- | /O(n)./
+-- Returns whether an 'Eq' instance exists in the given instances list
+-- for the given 'TypeRep'.
+--
+-- > > isEqT (reifyEqOrd (undefined :: Int)) (typeOf (undefined :: Int))
+-- > True
+--
+-- > > isEqT (reifyEqOrd (undefined :: Int)) (typeOf (undefined :: [[[Int]]]))
+-- > False
+--
+-- Given that the instances list has length /n/, this function is /O(n)/.
+isEqT :: [Expr] -> TypeRep -> Bool
+isEqT is t  =  isJust $ lookupComparison "==" t is
+
+-- | /O(n)./
+-- Returns whether an 'Ord' instance exists in the given instances list
+-- for the given 'TypeRep'.
+--
+-- > > isOrdT (reifyEqOrd (undefined :: Int)) (typeOf (undefined :: Int))
+-- > True
+--
+-- > > isOrdT (reifyEqOrd (undefined :: Int)) (typeOf (undefined :: [[[Int]]]))
+-- > False
+--
+-- Given that the instances list has length /n/, this function is /O(n)/.
+isOrdT :: [Expr] -> TypeRep -> Bool
+isOrdT is t  =  isJust $ lookupComparison "<=" t is
+
+-- | /O(n)./
+-- Returns whether both 'Eq' and 'Ord' instance exist in the given list
+-- for the given 'TypeRep'.
+--
+-- Given that the instances list has length /n/, this function is /O(n)/.
+isEqOrdT :: [Expr] -> TypeRep -> Bool
+isEqOrdT is t  =  isEqT is t && isOrdT is t
+
+-- | /O(n+m)./
+-- Returns whether an 'Eq' instance exists in the given instances list
+-- for the given 'Expr'.
+--
+-- > > isEq (reifyEqOrd (undefined :: Int)) (val (0::Int))
+-- > True
+--
+-- > > isEq (reifyEqOrd (undefined :: Int)) (val ([[[0::Int]]]))
+-- > False
+--
+-- Given that the instances list has length /m/
+-- and that the given 'Expr' has size /n/,
+-- this function is /O(n+m)/.
+isEq :: [Expr] -> Expr -> Bool
+isEq is  =  isEqT is . typ
+
+-- | /O(n+m)./
+-- Returns whether an 'Ord' instance exists in the given instances list
+-- for the given 'Expr'.
+--
+-- > > isOrd (reifyEqOrd (undefined :: Int)) (val (0::Int))
+-- > True
+--
+-- > > isOrd (reifyEqOrd (undefined :: Int)) (val ([[[0::Int]]]))
+-- > False
+--
+-- Given that the instances list has length /m/
+-- and that the given 'Expr' has size /n/,
+-- this function is /O(n+m)/.
+isOrd :: [Expr] -> Expr -> Bool
+isOrd is  =  isOrdT is . typ
+
+-- | /O(n+m)./
+-- Returns whether both 'Eq' and 'Ord' instance exist in the given list
+-- for the given 'Expr'.
+--
+-- Given that the instances list has length /m/
+-- and that the given 'Expr' has size /n/,
+-- this function is /O(n+m)/.
+isEqOrd :: [Expr] -> Expr -> Bool
+isEqOrd is e  =  isEq is e && isOrd is e
+
+mkComparison :: String -> [Expr] -> Expr -> Expr -> Expr
+mkComparison n' is e1 e2  =  fromMaybe (val False) $ do
+  e1e <- findValidApp os e1
+  e1e $$ e2
+  where
+  os = [eq | eq@(Value n _) <- is, n == n']
+
+mkEquation :: [Expr] -> Expr -> Expr -> Expr
+mkEquation  =  mkComparison "=="
+
+mkComparisonLT :: [Expr] -> Expr -> Expr -> Expr
+mkComparisonLT  =  mkComparison "<"
+
+mkComparisonLE :: [Expr] -> Expr -> Expr -> Expr
+mkComparisonLE  =  mkComparison "<="
+
+lookupName :: [Expr] -> Expr -> String
+lookupName is e  =  fromMaybe "x" $ eval "x" <$> findValidApp es e
+  where
+  es = [e | e@(Value "name" _) <- is]
+
+lookupNames :: [Expr] -> Expr -> [String]
+lookupNames is  =  variableNamesFromTemplate . lookupName is
+
+listVarsWith :: [Expr] -> Expr -> [Expr]
+listVarsWith is e  =  lookupName is e `listVarsAsTypeOf` e
+
+
+-- helpers --
+
+validApps :: [Expr] -> Expr -> [Expr]
+validApps es e  =  mapMaybe ($$ e) es
+
+findValidApp :: [Expr] -> Expr -> Maybe Expr
+findValidApp es  =  listToMaybe . validApps es
+
+(-:>) :: (a -> b) -> a -> (a -> b)
+(-:>)  =  const
+infixl 1 -:>
+
+
+-- reified instances --
+
+preludeNameInstances :: [Expr]
+preludeNameInstances = concat
+  [ reifyName (u :: ())
+  , reifyName (u :: Bool)
+  , reifyName (u :: Int)
+  , reifyName (u :: Integer)
+  , reifyName (u :: Char)
+  , reifyName (u :: Ordering)
+  , reifyName (u :: Rational)
+  , reifyName (u :: Float)
+  , reifyName (u :: Double)
+
+  , reifyName (u :: [()])
+  , reifyName (u :: [Bool])
+  , reifyName (u :: [Int])
+  , reifyName (u :: [Integer])
+  , reifyName (u :: [Char])
+  , reifyName (u :: [Ordering])
+  , reifyName (u :: [Rational])
+  , reifyName (u :: [Float])
+  , reifyName (u :: [Double])
+
+  , reifyName (u :: Maybe ())
+  , reifyName (u :: Maybe Bool)
+  , reifyName (u :: Maybe Int)
+  , reifyName (u :: Maybe Integer)
+  , reifyName (u :: Maybe Char)
+  , reifyName (u :: Maybe Ordering)
+  , reifyName (u :: Maybe Rational)
+  , reifyName (u :: Maybe Float)
+  , reifyName (u :: Maybe Double)
+
+  , reifyName (u :: ((),()))
+  , reifyName (u :: (Bool,Bool))
+  , reifyName (u :: (Int,Int))
+  , reifyName (u :: (Integer,Integer))
+  , reifyName (u :: (Char,Char))
+  , reifyName (u :: (Ordering,Ordering))
+  , reifyName (u :: (Rational,Rational))
+  , reifyName (u :: (Float,Float))
+  , reifyName (u :: (Double,Double))
+
+  , reifyName (u :: () -> ())
+  , reifyName (u :: Bool -> Bool)
+  , reifyName (u :: Int -> Int)
+  , reifyName (u :: Integer -> Integer)
+  , reifyName (u :: Char -> Char)
+  , reifyName (u :: Ordering -> Ordering)
+  , reifyName (u :: Rational -> Rational)
+  , reifyName (u :: Float -> Float)
+  , reifyName (u :: Double -> Double)
+
+  , reifyName (u :: () -> () -> ())
+  , reifyName (u :: Bool -> Bool -> Bool)
+  , reifyName (u :: Int -> Int -> Int)
+  , reifyName (u :: Integer -> Integer -> Integer)
+  , reifyName (u :: Char -> Char -> Char)
+  , reifyName (u :: Ordering -> Ordering -> Ordering)
+  , reifyName (u :: Rational -> Rational -> Rational)
+  , reifyName (u :: Float -> Float -> Float)
+  , reifyName (u :: Double -> Double -> Double)
+  ]
+  where
+  u :: a
+  u  =  undefined
+
diff --git a/src/Data/Express/Map.hs b/src/Data/Express/Map.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Map.hs
@@ -0,0 +1,245 @@
+-- |
+-- Module      : Data.Express.Map
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Utilities for mapping or transforming 'Expr's.
+module Data.Express.Map
+  ( mapValues
+  , mapVars
+  , mapConsts
+  , mapSubexprs
+  , (//-)
+  , (//)
+  , renameVarsBy
+  )
+where
+
+import Data.Express.Core
+import Data.Express.Utils.List
+import Data.Maybe (fromMaybe)
+
+-- | /O(n*m)/.
+-- Applies a function to all terminal values in an expression.
+-- (cf. '//-')
+--
+-- Given that:
+--
+-- > > let zero  = val (0 :: Int)
+-- > > let one   = val (1 :: Int)
+-- > > let two   = val (2 :: Int)
+-- > > let three = val (3 :: Int)
+-- > > let xx -+- yy = value "+" ((+) :: Int->Int->Int) :$ xx :$ yy
+-- > > let intToZero e = if typ e == typ zero then zero else e
+--
+-- Then:
+--
+-- > > one -+- (two -+- three)
+-- > 1 + (2 + 3) :: Int
+--
+-- > > mapValues intToZero $ one -+- (two -+- three)
+-- > 0 + (0 + 0) :: Integer
+--
+-- Given that the argument function is /O(m)/, this function is /O(n*m)/.
+mapValues :: (Expr -> Expr) -> Expr -> Expr
+mapValues f  =  m
+  where
+  m (e1 :$ e2)  =  m e1 :$ m e2
+  m e           =  f e
+
+-- | /O(n*m)/.
+-- Applies a function to all variables in an expression.
+--
+-- Given that:
+--
+-- > > let primeify e = if isVar e
+-- > |                  then case e of (Value n d) -> Value (n ++ "'") d
+-- > |                  else e
+-- > > let xx = var "x" (undefined :: Int)
+-- > > let yy = var "y" (undefined :: Int)
+-- > > let xx -+- yy = value "+" ((+) :: Int->Int->Int) :$ xx :$ yy
+--
+-- Then:
+--
+-- > > xx -+- yy
+-- > x + y :: Int
+--
+-- > > primeify xx
+-- > x' :: Int
+--
+-- > > mapVars primeify $ xx -+- yy
+-- > x' + y' :: Int
+--
+-- > > mapVars (primeify . primeify) $ xx -+- yy
+-- > x'' + y'' :: Int
+--
+-- Given that the argument function is /O(m)/, this function is /O(n*m)/.
+mapVars :: (Expr -> Expr) -> Expr -> Expr
+mapVars f  =  mapValues f'
+  where
+  f' e  =  if isVar e
+           then f e
+           else e
+
+-- | /O(n*m)/.
+-- Applies a function to all terminal constants in an expression.
+--
+-- Given that:
+--
+-- > > let one   = val (1 :: Int)
+-- > > let two   = val (2 :: Int)
+-- > > let xx -+- yy = value "+" ((+) :: Int->Int->Int) :$ xx :$ yy
+-- > > let intToZero e = if typ e == typ zero then zero else e
+--
+-- Then:
+--
+-- > > one -+- (two -+- xx)
+-- > 1 + (2 + x) :: Int
+--
+-- > > mapConsts intToZero (one -+- (two -+- xx))
+-- > 0 + (0 + x) :: Integer
+--
+-- Given that the argument function is /O(m)/, this function is /O(n*m)/.
+mapConsts :: (Expr -> Expr) -> Expr -> Expr
+mapConsts f  =  mapValues f'
+  where
+  f' e  =  if isConst e
+           then f e
+           else e
+
+-- | /O(n*m)/.
+-- Substitute subexpressions of an expression using the given function.
+-- Outer expressions have more precedence than inner expressions.
+-- (cf. '//')
+--
+-- With:
+--
+-- > > let xx = var "x" (undefined :: Int)
+-- > > let yy = var "y" (undefined :: Int)
+-- > > let zz = var "z" (undefined :: Int)
+-- > > let plus = value "+" ((+) :: Int->Int->Int)
+-- > > let times = value "*" ((*) :: Int->Int->Int)
+-- > > let xx -+- yy = plus :$ xx :$ yy
+-- > > let xx -*- yy = times :$ xx :$ yy
+--
+-- > > let pluswap (o :$ xx :$ yy) | o == plus = Just $ o :$ yy :$ xx
+-- > |     pluswap _                           = Nothing
+--
+-- Then:
+--
+-- > > mapSubexprs pluswap $ (xx -*- yy) -+- (yy -*- zz)
+-- > y * z + x * y :: Int
+--
+-- > > mapSubexprs pluswap $ (xx -+- yy) -*- (yy -+- zz)
+-- > (y + x) * (z + y) :: Int
+--
+-- Substitutions do not stack, in other words
+-- a replaced expression or its subexpressions are not further replaced:
+--
+-- > > mapSubexprs pluswap $ (xx -+- yy) -+- (yy -+- zz)
+-- > (y + z) + (x + y) :: Int
+--
+-- Given that the argument function is /O(m)/, this function is /O(n*m)/.
+mapSubexprs :: (Expr -> Maybe Expr) -> Expr -> Expr
+mapSubexprs f  =  m
+  where
+  m e  =  fromMaybe e' (f e)
+    where
+    e'  =  case e of
+           e1 :$ e2 -> m e1 :$ m e2
+           e -> e
+
+-- | /O(n*m)/.
+-- Substitute occurrences of values in an expression
+-- from the given list of substitutions.
+-- (cf. 'mapValues')
+--
+-- Given that:
+--
+-- > > let xx = var "x" (undefined :: Int)
+-- > > let yy = var "y" (undefined :: Int)
+-- > > let zz = var "z" (undefined :: Int)
+-- > > let xx -+- yy = value "+" ((+) :: Int->Int->Int) :$ xx :$ yy
+--
+-- Then:
+--
+-- > > ((xx -+- yy) -+- (yy -+- zz)) //- [(xx, yy), (zz, yy)]
+-- > (y + y) + (y + y) :: Int
+--
+-- > > ((xx -+- yy) -+- (yy -+- zz)) //- [(yy, yy -+- zz)]
+-- > (x + (y + z)) + ((y + z) + z) :: Int
+--
+-- This function does not work for substituting non-terminal subexpressions:
+--
+-- > > (xx -+- yy) //- [(xx -+- yy, zz)]
+-- > x + y :: Int
+--
+-- Please use the slower '//' if you want the above replacement to work.
+--
+-- Replacement happens only once:
+--
+-- > > xx //- [(xx,yy), (yy,zz)]
+-- > y :: Int
+--
+-- Given that the argument list has length /m/,
+-- this function is /O(n*m)/.
+(//-) :: Expr -> [(Expr,Expr)] -> Expr
+e //- s  =  mapValues (flip lookupId s) e
+
+-- | /O(n*n*m)/.
+-- Substitute subexpressions in an expression
+-- from the given list of substitutions.
+-- (cf. 'mapSubexprs').
+--
+-- Please consider using '//-' if you are replacing just terminal values
+-- as it is faster.
+--
+-- Given that:
+--
+-- > > let xx = var "x" (undefined :: Int)
+-- > > let yy = var "y" (undefined :: Int)
+-- > > let zz = var "z" (undefined :: Int)
+-- > > let xx -+- yy = value "+" ((+) :: Int->Int->Int) :$ xx :$ yy
+--
+-- Then:
+--
+-- > > ((xx -+- yy) -+- (yy -+- zz)) // [(xx -+- yy, yy), (yy -+- zz, yy)]
+-- > y + y :: Int
+--
+-- > > ((xx -+- yy) -+- zz) // [(xx -+- yy, zz), (zz, xx -+- yy)]
+-- > z + (x + y) :: Int
+--
+-- Replacement happens only once with outer expressions
+-- having more precedence than inner expressions.
+--
+-- > > (xx -+- yy) // [(yy,xx), (xx -+- yy,zz), (zz,xx)]
+-- > z :: Int
+--
+-- Given that the argument list has length /m/, this function is /O(n*n*m)/.
+-- Remember that since /n/ is the size of an expression,
+-- comparing two expressions is /O(n)/ in the worst case,
+-- and we may need to compare with /n/ subexpressions in the worst case.
+(//) :: Expr -> [(Expr,Expr)] -> Expr
+e // s  =  mapSubexprs (flip lookup s) e
+
+-- | Rename variables in an 'Expr'.
+--
+-- > > renameVarsBy (++ "'") (xx -+- yy)
+-- > x' + y' :: Int
+--
+-- > > renameVarsBy (++ "'") (yy -+- (zz -+- xx))
+-- > (y' + (z' + x')) :: Int
+--
+-- > > renameVarsBy (++ "1") (abs' xx)
+-- > abs x1 :: Int
+--
+-- > > renameVarsBy (++ "2") $ abs' (xx -+- yy)
+-- > abs (x2 + y2) :: Int
+--
+-- NOTE: this will affect holes!
+renameVarsBy :: (String -> String) -> Expr -> Expr
+renameVarsBy f = mapValues f'
+  where
+  f' (Value ('_':n) t) = Value ('_':f n) t
+  f' e = e
diff --git a/src/Data/Express/Match.hs b/src/Data/Express/Match.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Match.hs
@@ -0,0 +1,118 @@
+-- |
+-- Module      : Data.Express.Match
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Utilities for matching 'Expr's with 'var'iables.
+module Data.Express.Match
+  ( match
+  , matchWith
+  , isInstanceOf
+  , hasInstanceOf
+  , isSubexprOf
+  )
+where
+
+import Data.Express.Basic
+import Data.Maybe
+import Data.Functor ((<$>))
+import Control.Monad ((>=>))
+
+-- |
+-- Given two expressions, returns a 'Just' list of matches
+-- of subexpressions of the first expressions
+-- to variables in the second expression.
+-- Returns 'Nothing' when there is no match.
+--
+-- > > let zero = val (0::Int)
+-- > > let one  = val (1::Int)
+-- > > let xx   = var "x" (undefined :: Int)
+-- > > let yy   = var "y" (undefined :: Int)
+-- > > let e1 -+- e2  =  value "+" ((+)::Int->Int->Int) :$ e1 :$ e2
+--
+-- > > (zero -+- one) `match` (xx -+- yy)
+-- > Just [(y :: Int,1 :: Int),(x :: Int,0 :: Int)]
+--
+-- > > (zero -+- (one -+- two)) `match` (xx -+- yy)
+-- > Just [(y :: Int,1 + 2 :: Int),(x :: Int,0 :: Int)]
+--
+-- > > (zero -+- (one -+- two)) `match` (xx -+- (yy -+- yy))
+-- > Nothing
+--
+-- In short:
+--
+-- >           (zero -+- one) `match` (xx -+- yy)           =  Just [(xx,zero), (yy,one)]
+-- > (zero -+- (one -+- two)) `match` (xx -+- yy)           =  Just [(xx,zero), (yy,one-+-two)]
+-- > (zero -+- (one -+- two)) `match` (xx -+- (yy -+- yy))  =  Nothing
+match :: Expr -> Expr -> Maybe [(Expr,Expr)]
+match = matchWith []
+
+-- |
+-- Like 'match' but allowing predefined bindings.
+--
+-- > matchWith [(xx,zero)] (zero -+- one) (xx -+- yy)  =  Just [(xx,zero), (yy,one)]
+-- > matchWith [(xx,one)]  (zero -+- one) (xx -+- yy)  =  Nothing
+matchWith :: [(Expr,Expr)] -> Expr -> Expr -> Maybe [(Expr,Expr)]
+matchWith bs e1' e2' = m e1' e2' bs
+  where
+  m :: Expr -> Expr -> [(Expr,Expr)] -> Maybe [(Expr,Expr)]
+  m (f1 :$ x1) (f2 :$ x2)             =  m f1 f2 >=> m x1 x2
+  m e1 e2
+    | isVar e2 && mtyp e1 == mtyp e2  =  updateAssignments (e2,e1)
+    | e1 == e2                        =  Just
+    | otherwise                       =  const Nothing
+
+updateAssignments :: (Expr,Expr) -> [(Expr,Expr)] -> Maybe [(Expr,Expr)]
+updateAssignments (e,e') = \bs ->
+  case lookup e bs of
+    Nothing  -> Just ((e,e'):bs)
+    Just e'' -> if e'' == e'
+                then Just bs
+                else Nothing
+
+-- |
+-- Given two 'Expr's,
+-- checks if the first expression
+-- is an instance of the second
+-- in terms of variables.
+-- (cf. 'hasInstanceOf')
+--
+-- > > let zero = val (0::Int)
+-- > > let one  = val (1::Int)
+-- > > let xx   = var "x" (undefined :: Int)
+-- > > let yy   = var "y" (undefined :: Int)
+-- > > let e1 -+- e2  =  value "+" ((+)::Int->Int->Int) :$ e1 :$ e2
+--
+-- >  one `isInstanceOf` one   =  True
+-- >   xx `isInstanceOf` xx    =  True
+-- >   yy `isInstanceOf` xx    =  True
+-- > zero `isInstanceOf` xx    =  True
+-- >   xx `isInstanceOf` zero  =  False
+-- >  one `isInstanceOf` zero  =  False
+-- >   (xx -+- (yy -+- xx)) `isInstanceOf`   (xx -+- yy)  =  True
+-- >   (yy -+- (yy -+- xx)) `isInstanceOf`   (xx -+- yy)  =  True
+-- > (zero -+- (yy -+- xx)) `isInstanceOf` (zero -+- yy)  =  True
+-- >  (one -+- (yy -+- xx)) `isInstanceOf` (zero -+- yy)  =  False
+isInstanceOf :: Expr -> Expr -> Bool
+e1 `isInstanceOf` e2 = isJust $ e1 `match` e2
+
+-- |
+-- Checks if any of the subexpressions of the first argument 'Expr'
+-- is an instance of the second argument 'Expr'.
+hasInstanceOf :: Expr -> Expr -> Bool
+e1 `hasInstanceOf` e2  =  any (`isInstanceOf` e2) (subexprs e1)
+
+-- | /O(n^2)/.
+-- Checks if an 'Expr' is a subexpression of another.
+--
+-- > > (xx -+- yy) `isSubexprOf` (zz -+- (xx -+- yy))
+-- > True
+--
+-- > > (xx -+- yy) `isSubexprOf` abs' (yy -+- xx)
+-- > False
+--
+-- > > xx `isSubexprOf` yy
+-- > False
+isSubexprOf :: Expr -> Expr -> Bool
+isSubexprOf e = (e `elem`) . subexprs
diff --git a/src/Data/Express/Name.hs b/src/Data/Express/Name.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Name.hs
@@ -0,0 +1,248 @@
+-- |
+-- Module      : Data.Express.Name
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Defines the 'Name' type class.
+module Data.Express.Name
+  ( Name (..)
+  , names
+  , variableNamesFromTemplate
+  )
+where
+
+import Data.Express.Utils.String
+
+import Data.Char
+import Data.List
+import Data.Ratio (Ratio)
+import Data.Word (Word) -- for GHC <= 7.8
+
+-- |
+-- If we were to come up with a variable name for the given type
+-- what 'name' would it be?
+--
+-- An instance for a given type @ Ty @ is simply given by:
+--
+-- > instance Name Ty where name _ = "x"
+--
+-- Examples:
+--
+-- > > name (undefined :: Int)
+-- > "x"
+--
+-- > > name (undefined :: Bool)
+-- > "p"
+--
+-- > > name (undefined :: [Int])
+-- > "xs"
+--
+-- This is then used to generate an infinite list of variable 'names':
+--
+-- > > names (undefined :: Int)
+-- > ["x", "y", "z", "x'", "y'", "z'", "x''", "y''", "z''", ...]
+--
+-- > > names (undefined :: Bool)
+-- > ["p", "q", "r", "p'", "q'", "r'", "p''", "q''", "r''", ...]
+--
+-- > > names (undefined :: [Int])
+-- > ["xs", "ys", "zs", "xs'", "ys'", "zs'", "xs''", "ys''", ...]
+class Name a where
+  -- | /O(1)./
+  --
+  -- Returns a name for a variable of the given argument's type.
+  --
+  -- > > name (undefined :: Int)
+  -- > "x"
+  --
+  -- > > name (undefined :: [Bool])
+  -- > "ps"
+  --
+  -- > > name (undefined :: [Maybe Integer])
+  -- > "mxs"
+  --
+  -- The default definition is:
+  --
+  -- > name _ = "x"
+  name :: a -> String
+  name _ = "x"
+
+-- |
+-- > name (undefined :: ()) = "u"
+-- > names (undefined :: ()) = ["u", "v", "w", "u'", "v'", ...]
+instance Name ()        where  name _  =  "u"
+
+-- |
+-- > name (undefined :: Bool) = "p"
+-- > names (undefined :: Bool) = ["p", "q", "r", "p'", "q'", ...]
+instance Name Bool      where  name _  =  "p"
+
+-- |
+-- > name (undefined :: Int) = "x"
+-- > names (undefined :: Int) = ["x", "y", "z", "x'", "y'", ...]
+instance Name Int       where  name _  =  "x"
+
+-- |
+-- > name (undefined :: Integer) = "x"
+-- > names (undefined :: Integer) = ["x", "y", "z", "x'", ...]
+instance Name Integer   where  name _  =  "x"
+
+-- |
+-- > name (undefined :: Char) = "c"
+-- > names (undefined :: Char) = ["c", "d", "e", "c'", "d'", ...]
+instance Name Char      where  name _  =  "c"
+
+-- |
+-- > name (undefined :: Ordering) = "o"
+-- > names (undefined :: Ordering) = ["o", "p", "q", "o'", ...]
+instance Name Ordering  where  name _  =  "o"
+
+-- |
+-- > name (undefined :: Rational) = "q"
+-- > names (undefined :: Rational) = ["q", "r", "s", "q'", ...]
+instance Name (Ratio a) where  name _  =  "q"
+
+-- |
+-- > name (undefined :: Float) = "x"
+-- > names (undefined :: Float) = ["x", "y", "z", "x'", ...]
+instance Name Float     where  name _  =  "x"
+
+-- |
+-- > name (undefined :: Double) = "x"
+-- > names (undefined :: Double) = ["x", "y", "z", "x'", ...]
+instance Name Double    where  name _  =  "x"
+
+-- |
+-- > names (undefined :: ()->()) = ["f", "g", "h", "f'", ...]
+-- > names (undefined :: Int->Int) = ["f", "g", "h", ...]
+instance Name (a -> b)  where  name _  =  "f"
+
+-- |
+-- > names (undefined :: Maybe Int) = ["mx", "mx1", "mx2", ...]
+-- > nemes (undefined :: Maybe Bool) = ["mp", "mp1", "mp2", ...]
+instance Name a => Name (Maybe a) where
+  name mx  =  "m" ++ name x
+    where
+    Just x = mx
+
+-- |
+-- > names (undefined :: Either Int Int) = ["exy", "exy1", ...]
+-- > names (undefined :: Either Int Bool) = ["exp", "exp1", ...]
+instance (Name a, Name b) => Name (Either a b) where
+  name exy  =  "e" ++ n ++ m
+    where
+    Left x  = exy
+    Right y = exy
+    n = name x
+    m = head $ names y \\ [n]
+
+-- |
+-- > names (undefined :: (Int,Int)) = ["xy", "zw", "xy'", ...]
+-- > names (undefined :: (Bool,Bool)) = ["pq", "rs", "pq'", ...]
+instance (Name a, Name b) => Name (a,b) where
+  name xy  =  n ++ m
+    where
+    (x,y)  =  xy
+    n  =  name x
+    m  =  head $ names y \\ [n]
+
+-- |
+-- > names (undefined :: (Int,Int,Int)) = ["xyz","uvw", ...]
+-- > names (undefined :: (Int,Bool,Char)) = ["xpc", "xpc1", ...]
+instance (Name a, Name b, Name c) => Name (a,b,c) where
+  name xyz  =  n ++ m ++ o
+    where
+    (x,y,z)  =  xyz
+    n  =  name x
+    m  =  head $ names y \\ [n]
+    o  =  head $ names z \\ [n,m]
+
+-- |
+-- > names (undefined :: ((),(),(),())) = ["uuuu", "uuuu1", ...]
+-- > names (undefined :: (Int,Int,Int,Int)) = ["xxxx", ...]
+instance (Name a, Name b, Name c, Name d) => Name (a,b,c,d) where
+  name xyzw  =  name x ++ name y ++ name z ++ name w  where  (x,y,z,w)  =  xyzw
+
+-- |
+-- > names (undefined :: [Int]) = ["xs", "ys", "zs", "xs'", ...]
+-- > names (undefined :: [Bool]) = ["ps", "qs", "rs", "ps'", ...]
+instance Name a => Name [a] where
+  name xs  =  name (head xs) ++ "s"
+
+-- |
+-- Returns na infinite list of variable names from the given type:
+-- the result of 'variableNamesFromTemplate' after 'name'.
+--
+-- > > names (undefined :: Int)
+-- > ["x", "y", "z", "x'", "y'", "z'", "x''", "y''", "z''", ...]
+--
+-- > > names (undefined :: Bool)
+-- > ["p", "q", "r", "p'", "q'", "r'", "p''", "q''", "r''", ...]
+--
+-- > > names (undefined :: [Int])
+-- > ["xs", "ys", "zs", "xs'", "ys'", "zs'", "xs''", "ys''", ...]
+names :: Name a => a -> [String]
+names  =  variableNamesFromTemplate . name
+
+
+-- instances of further types and arities --
+
+instance Name Word where  name _  =  "x"
+
+instance (Name a, Name b, Name c, Name d, Name e) => Name (a,b,c,d,e) where
+  name xyzwv  =  name x ++ name y ++ name z ++ name w ++ name v
+    where  (x,y,z,w,v)  =  xyzwv
+
+instance (Name a, Name b, Name c, Name d, Name e, Name f)
+      => Name (a,b,c,d,e,f) where
+  name xyzwvu  =  name x ++ name y ++ name z ++ name w ++ name v ++ name u
+    where  (x,y,z,w,v,u)  =  xyzwvu
+
+instance (Name a, Name b, Name c, Name d, Name e, Name f, Name g)
+      => Name (a,b,c,d,e,f,g) where
+  name xyzwvut  =  name x ++ name y ++ name z ++ name w
+                ++ name v ++ name u ++ name t
+    where  (x,y,z,w,v,u,t)  =  xyzwvut
+
+instance (Name a, Name b, Name c, Name d, Name e, Name f, Name g, Name h)
+      => Name (a,b,c,d,e,f,g,h) where
+  name xyzwvuts  =  name x ++ name y ++ name z ++ name w
+                 ++ name v ++ name u ++ name t ++ name s
+    where  (x,y,z,w,v,u,t,s)  =  xyzwvuts
+
+instance ( Name a, Name b, Name c, Name d
+         , Name e, Name f, Name g, Name h
+         , Name i)
+      => Name (a,b,c,d,e,f,g,h,i) where
+  name xyzwvutsr  =  name x ++ name y ++ name z ++ name w
+                  ++ name v ++ name u ++ name t ++ name s
+                  ++ name r
+    where  (x,y,z,w,v,u,t,s,r)  =  xyzwvutsr
+
+instance ( Name a, Name b, Name c, Name d
+         , Name e, Name f, Name g, Name h
+         , Name i, Name j )
+      => Name (a,b,c,d,e,f,g,h,i,j) where
+  name xyzwvutsrq  =  name x ++ name y ++ name z ++ name w
+                   ++ name v ++ name u ++ name t ++ name s
+                   ++ name r ++ name q
+    where  (x,y,z,w,v,u,t,s,r,q)  =  xyzwvutsrq
+
+instance ( Name a, Name b, Name c, Name d
+         , Name e, Name f, Name g, Name h
+         , Name i, Name j, Name k )
+      => Name (a,b,c,d,e,f,g,h,i,j,k) where
+  name xyzwvutsrqp  =  name x ++ name y ++ name z ++ name w
+                    ++ name v ++ name u ++ name t ++ name s
+                    ++ name r ++ name q ++ name p
+    where  (x,y,z,w,v,u,t,s,r,q,p)  =  xyzwvutsrqp
+
+instance ( Name a, Name b, Name c, Name d
+         , Name e, Name f, Name g, Name h
+         , Name i, Name j, Name k, Name l )
+      => Name (a,b,c,d,e,f,g,h,i,j,k,l) where
+  name xyzwvutsrqpo  =  name x ++ name y ++ name z ++ name w
+                     ++ name v ++ name u ++ name t ++ name s
+                     ++ name r ++ name q ++ name p ++ name o
+    where  (x,y,z,w,v,u,t,s,r,q,p,o)  =  xyzwvutsrqpo
diff --git a/src/Data/Express/Name/Derive.hs b/src/Data/Express/Name/Derive.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Name/Derive.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE TemplateHaskell, CPP #-}
+-- |
+-- Module      : Data.Express.Name.Derive
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Allows automatic derivation of 'Name' typeclass instances.
+module Data.Express.Name.Derive
+  ( deriveName
+  , deriveNameCascading
+  , deriveNameIfNeeded
+  )
+where
+
+import qualified Data.Express.Name as N
+
+import Control.Monad
+import Data.Char
+import Data.List
+import Data.Express.Utils.TH
+
+-- | Derives a 'N.Name' instance
+--   for the given type 'Name'.
+--
+-- This function needs the @TemplateHaskell@ extension.
+deriveName :: Name -> DecsQ
+deriveName  =  deriveWhenNeededOrWarn ''N.Name reallyDeriveName
+
+-- | Same as 'deriveName' but does not warn when instance already exists
+--   ('deriveName' is preferable).
+deriveNameIfNeeded :: Name -> DecsQ
+deriveNameIfNeeded  =  deriveWhenNeeded ''N.Name reallyDeriveName
+
+-- | Derives a 'N.Name' instance for a given type 'Name'
+--   cascading derivation of type arguments as well.
+deriveNameCascading :: Name -> DecsQ
+deriveNameCascading  =  deriveWhenNeeded ''N.Name reallyDeriveNameCascading
+
+reallyDeriveName :: Name -> DecsQ
+reallyDeriveName t  =  do
+  (nt,vs) <- normalizeType t
+  [d| instance N.Name $(return nt) where
+        name _ = $(stringE vname) |]
+  where
+  showJustName = reverse . takeWhile (/= '.') . reverse . show
+  vname = map toLower . take 1 $ showJustName t
+-- TODO: on deriveName, use full camelCase name?
+-- TODO: on deriveName, use x for Num instances?
+
+-- Not only really derive Name instances,
+-- but cascade through argument types.
+reallyDeriveNameCascading :: Name -> DecsQ
+reallyDeriveNameCascading  =  reallyDeriveCascading ''N.Name reallyDeriveName
diff --git a/src/Data/Express/Utils/List.hs b/src/Data/Express/Utils/List.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Utils/List.hs
@@ -0,0 +1,105 @@
+-- |
+-- Module      : Data.Express.Utils.List
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Re-exports the "Data.List" module along with additional functions over
+-- lists.
+{-# LANGUAGE CPP #-}
+module Data.Express.Utils.List
+  ( nubSort
+  , isPermutationOf
+  , isSubsetOf
+  , isNub
+  , lookupId
+  , (+++)
+  , module Data.List
+#if __GLASGOW_HASKELL__ < 710
+  , isSubsequenceOf
+#endif
+  )
+where
+
+import Data.Function (on)
+import Data.List
+import Data.Maybe (fromMaybe)
+
+-- | /O(n log n)/.
+-- Sorts and remove repetitions.
+-- Equivalent to @nub . sort@.
+--
+-- > > nubSort [1,2,3]
+-- > [1,2,3]
+-- > > nubSort [3,2,1]
+-- > [1,2,3]
+-- > > nubSort [3,2,1,3,2,1]
+-- > [1,2,3]
+-- > > nubSort [3,3,1,1,2,2]
+-- > [1,2,3]
+nubSort :: Ord a => [a] -> [a]
+nubSort  =  nnub . sort
+  where
+  -- linear nub of adjacent values
+  nnub [] = []
+  nnub [x] = [x]
+  nnub (x:xs) = x : nnub (dropWhile (==x) xs)
+
+-- | /O(n log n)/.
+-- Checks that all elements of the first list are elements of the second.
+isSubsetOf :: Ord a => [a] -> [a] -> Bool
+xs `isSubsetOf` ys  =  nubSort xs `isSubsequenceOf` nubSort ys
+
+
+#if __GLASGOW_HASKELL__ < 710
+-- only exported from Data.List since base 4.8.0.0
+isSubsequenceOf :: Eq a => [a] -> [a] -> Bool
+isSubsequenceOf []    _                    =  True
+isSubsequenceOf (_:_) []                   =  False
+isSubsequenceOf (x:xs) (y:ys) | x == y     =     xs  `isSubsequenceOf` ys
+                              | otherwise  =  (x:xs) `isSubsequenceOf` ys
+#endif
+
+-- | /O(n log n)/.
+-- Checks that all elements of the first list are elements of the second.
+isPermutationOf :: Ord a => [a] -> [a] -> Bool
+isPermutationOf  =  (==) `on` sort
+
+-- | /O(n log n)/.
+-- Checks that all elements are unique.
+-- This function is a faster equivalent to the following:
+--
+-- > isNub xs  =  nub xs == xs
+--
+-- Examples:
+--
+-- > isNub []       =  True
+-- > isNub [1,2,3]  =  True
+-- > isNub [2,1,2]  =  False
+isNub :: Ord a => [a] -> Bool
+isNub xs  =  length (nubSort xs) == length xs
+
+-- | /O(n)/.
+-- Like 'lookup' but returns the key itself if nothing is found.
+--
+-- > > lookupId 5 [(1,2),(3,4)]
+-- > 5
+--
+-- > > lookupId 5 [(1,2),(3,4),(5,6)]
+-- > 6
+lookupId :: Eq a => a -> [(a,a)] -> a
+lookupId x = fromMaybe x . lookup x
+
+(+++) :: Ord a => [a] -> [a] -> [a]
+(+++)  =  nubMerge
+infixr 5 +++
+
+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)
+                                 GT -> y:nubMergeBy cmp (x:xs) ys
+                                 EQ -> x:nubMergeBy cmp xs ys
+nubMergeBy _ xs ys  =  xs ++ ys
+
+nubMerge :: Ord a => [a] -> [a] -> [a]
+nubMerge  =  nubMergeBy compare
diff --git a/src/Data/Express/Utils/String.hs b/src/Data/Express/Utils/String.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Utils/String.hs
@@ -0,0 +1,171 @@
+-- |
+-- Module      : Data.Express.Utils.String
+-- Copyright   : (c) 2016-2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Utilities for manipulating strings.
+--
+-- At some point, this file was part of the Speculate tool.
+module Data.Express.Utils.String
+  ( module Data.String
+  , module Data.Char
+  , unquote
+  , atomic
+  , outernmostPrec
+  , isNegativeLiteral
+  , isInfix, isPrefix, isInfixedPrefix
+  , toPrefix
+  , prec
+  , variableNamesFromTemplate
+  , primeCycle
+  )
+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
+unquote ('"':s) | last s == '"' = init s
+unquote s = s
+
+-- | Checks if a string-encoded Haskell expression is atomic.
+--
+-- > > atomic "123"
+-- > True
+-- > > atomic "42 + 1337"
+-- > False
+-- > > atomic "'a'"
+-- > True
+-- > > atomic "[1,2,3,4,5]"
+-- > True
+-- > > atomic "(1,2,3,4,5)"
+-- > True
+--
+-- FIXME: The current implementation may produce false positives:
+--
+-- > > atomic "'a' < 'b'"
+-- > True
+-- > > atomic "\"asdf\" ++ \"qwer\""
+-- > True
+-- > > atomic "[1,2,3] ++ [4,5,6]"
+-- > True
+--
+-- but this does not cause problems for (all?) most cases.
+atomic :: String -> Bool
+atomic s | all (not . isSpace) s = True
+atomic ('\'':s) | last s == '\'' = True
+atomic ('"':s)  | last s == '"'  = True
+atomic ('[':s)  | last s == ']'  = True
+atomic ('(':s)  | last s == ')'  = True
+atomic _ = False
+
+outernmostPrec :: String -> Maybe Int
+outernmostPrec s =
+  case words s of
+    [l,o,r] | isInfix o -> Just (prec o)
+    _                   -> Nothing
+
+isNegativeLiteral :: String -> Bool
+isNegativeLiteral s | not (atomic s) = False
+isNegativeLiteral "-"                = False
+isNegativeLiteral ('-':cs)           = all isDigit cs
+isNegativeLiteral _                  = False
+
+-- | Check if a function / operator is infix
+--
+-- > isInfix "foo"   == False
+-- > isInfix "(+)"   == False
+-- > isInfix "`foo`" == True
+-- > isInfix "+"     == True
+isInfix :: String -> Bool
+isInfix (c:_) = c `notElem` "()'\"[_" && not (isAlphaNum c)
+isInfix "" = error "isInfix: empty string"
+
+-- | Returns the precedence of default Haskell operators
+prec :: String -> Int
+prec " "  = 10
+prec "!!" = 9
+prec "."  = 9
+prec "^"  = 8
+prec "^^" = 8
+prec "**" = 8
+prec "*"  = 7
+prec "/"  = 7
+prec "%"  = 7
+prec "+"  = 6
+prec "-"  = 6
+prec ":"  = 5
+prec "++" = 5
+prec "\\" = 5
+prec ">"  = 4
+prec "<"  = 4
+prec ">=" = 4
+prec "<=" = 4
+prec "==" = 4
+prec "/=" = 4
+prec "`elem`" = 4
+prec "&&" = 3
+prec "||" = 2
+prec ">>=" = 1
+prec ">>" = 1
+prec ">=>" = 1
+prec "<=<" = 1
+prec "$"  = 0
+prec "`seq`" = 0
+prec "==>" = 0
+prec "<==>" = 0
+prec _ = 9
+
+isPrefix :: String -> Bool
+isPrefix = not . isInfix
+
+-- | Is the string of the form @\`string\`@
+isInfixedPrefix :: String -> Bool
+isInfixedPrefix s | not (atomic s) = False
+isInfixedPrefix ('`':cs)           = last cs == '`'
+isInfixedPrefix _                  = False
+
+-- | Transform an infix operator into an infix function:
+--
+-- > toPrefix "`foo`" == "foo"
+-- > toPrefix "+"     == "(+)"
+toPrefix :: String -> String
+toPrefix ('`':cs) = init cs
+toPrefix cs = '(':cs ++ ")"
+
+primeCycle :: [String] -> [String]
+primeCycle []  =  []
+primeCycle ss  =  ss ++ map (++ "'") (primeCycle ss)
+
+-- |
+-- Returns an infinite list of variable names based on the given template.
+--
+-- > > variableNamesFromTemplate "x"
+-- > ["x", "y", "z", "x'", "y'", ...]
+--
+-- > > variableNamesFromTemplate "p"
+-- > ["p", "q", "r", "p'", "q'", ...]
+--
+-- > > variableNamesFromTemplate "xy"
+-- > ["xy", "zw", "xy'", "zw'", "xy''", ...]
+variableNamesFromTemplate :: String -> [String]
+variableNamesFromTemplate  =  primeCycle . f
+  where
+  f ""                           =  f "x"
+  f "x"                          =  ["x", "y", "z"] -- redundant, for clarity
+  f "xy"                         =  ["xy", "zw"]
+  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 [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
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Utils/TH.hs
@@ -0,0 +1,290 @@
+{-# LANGUAGE TemplateHaskell, CPP #-}
+-- |
+-- Module      : Data.Express.Name.Derive
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- Template Haskell utilities.
+module Data.Express.Utils.TH
+  ( reallyDeriveCascading
+  , deriveWhenNeeded
+  , deriveWhenNeededOrWarn
+  , typeConArgs
+  , typeConArgsThat
+  , typeConCascadingArgsThat
+  , normalizeType
+  , normalizeTypeUnits
+  , isInstanceOf
+  , isntInstanceOf
+  , typeArity
+  , typeConstructors
+  , isTypeSynonym
+  , typeSynonymType
+  , mergeIFns
+  , mergeI
+  , lookupValN
+  , showJustName
+  , typeConstructorsArgNames
+  , (|=>|)
+  , whereI
+  , module Language.Haskell.TH
+  )
+where
+
+import Control.Monad
+import Data.List
+import Language.Haskell.TH
+
+deriveWhenNeeded :: Name -> (Name -> DecsQ) -> Name -> DecsQ
+deriveWhenNeeded  =  deriveWhenNeededX False
+
+deriveWhenNeededOrWarn :: Name -> (Name -> DecsQ) -> Name -> DecsQ
+deriveWhenNeededOrWarn  =  deriveWhenNeededX True
+
+deriveWhenNeededX :: Bool -> Name -> (Name -> DecsQ) -> Name -> DecsQ
+deriveWhenNeededX warnExisting cls reallyDerive t  =  do
+  is <- t `isInstanceOf` cls
+  if is
+  then do
+    unless (not warnExisting)
+      (reportWarning $ "Instance " ++ showJustName cls ++ " " ++ showJustName t
+                    ++ " already exists, skipping derivation")
+    return []
+  else
+    reallyDerive t
+
+showJustName :: Name -> String
+showJustName = reverse . takeWhile (/= '.') . reverse . show
+
+reallyDeriveCascading :: Name -> (Name -> DecsQ) -> Name -> DecsQ
+reallyDeriveCascading cls reallyDerive t =
+      return . concat
+  =<< mapM reallyDerive
+  =<< filterM (liftM not . isTypeSynonym)
+  =<< return . (t:) . delete t
+  =<< t `typeConCascadingArgsThat` (`isntInstanceOf` cls)
+
+typeConArgs :: Name -> Q [Name]
+typeConArgs t = do
+  is <- isTypeSynonym t
+  if is
+    then liftM typeConTs $ typeSynonymType t
+    else liftM (nubMerges . map typeConTs . concat . map snd) $ typeConstructors t
+  where
+  typeConTs :: Type -> [Name]
+  typeConTs (AppT t1 t2) = typeConTs t1 `nubMerge` typeConTs t2
+  typeConTs (SigT t _) = typeConTs t
+  typeConTs (VarT _) = []
+  typeConTs (ConT n) = [n]
+#if __GLASGOW_HASKELL__ >= 800
+  -- typeConTs (PromotedT n) = [n] ?
+  typeConTs (InfixT  t1 n t2) = typeConTs t1 `nubMerge` typeConTs t2
+  typeConTs (UInfixT t1 n t2) = typeConTs t1 `nubMerge` typeConTs t2
+  typeConTs (ParensT t) = typeConTs t
+#endif
+  typeConTs _ = []
+
+typeConArgsThat :: Name -> (Name -> Q Bool) -> Q [Name]
+typeConArgsThat t p = do
+  targs <- typeConArgs t
+  tbs   <- mapM (\t' -> do is <- p t'; return (t',is)) targs
+  return [t' | (t',p) <- tbs, p]
+
+typeConCascadingArgsThat :: Name -> (Name -> Q Bool) -> Q [Name]
+t `typeConCascadingArgsThat` p = do
+  ts <- t `typeConArgsThat` p
+  let p' t' = do is <- p t'; return $ t' `notElem` (t:ts) && is
+  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.
+--
+-- Suppose:
+--
+-- > data DT a b c ... = ...
+--
+-- Then, in pseudo-TH:
+--
+-- > normalizeType [t|DT|] == Q (DT a b c ..., [a, b, c, ...])
+normalizeType :: Name -> Q (Type, [Type])
+normalizeType t = do
+  ar <- typeArity t
+  vs <- newVarTs ar
+  return (foldl AppT (ConT t) vs, vs)
+  where
+    newNames :: [String] -> Q [Name]
+    newNames = mapM newName
+    newVarTs :: Int -> Q [Type]
+    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 () () |]
+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.
+isInstanceOf :: Name -> Name -> Q Bool
+isInstanceOf tn cl = do
+  ty <- normalizeTypeUnits tn
+  isInstance cl [ty]
+
+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
+--
+-- This works for Data's and Newtype's and it is useful when generating
+-- typeclass instances.
+typeArity :: Name -> Q Int
+typeArity t = do
+  ti <- reify t
+  return . length $ case ti of
+#if __GLASGOW_HASKELL__ < 800
+    TyConI (DataD    _ _ ks _ _) -> ks
+    TyConI (NewtypeD _ _ ks _ _) -> ks
+#else
+    TyConI (DataD    _ _ ks _ _ _) -> ks
+    TyConI (NewtypeD _ _ ks _ _ _) -> ks
+#endif
+    TyConI (TySynD _ ks _) -> ks
+    _ -> 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
+-- the type arguments they take.
+--
+-- > typeConstructors ''()    === Q [('(),[])]
+--
+-- > typeConstructors ''(,)   === Q [('(,),[VarT a, VarT b])]
+--
+-- > typeConstructors ''[]    === Q [('[],[]),('(:),[VarT a,AppT ListT (VarT a)])]
+--
+-- > data Pair a = P a a
+-- > typeConstructors ''Pair  === Q [('P,[VarT a, VarT a])]
+--
+-- > data Point = Pt Int Int
+-- > typeConstructors ''Point === Q [('Pt,[ConT Int, ConT Int])]
+typeConstructors :: Name -> Q [(Name,[Type])]
+typeConstructors t = do
+  ti <- reify t
+  return . map simplify $ case ti of
+#if __GLASGOW_HASKELL__ < 800
+    TyConI (DataD    _ _ _ cs _) -> cs
+    TyConI (NewtypeD _ _ _ c  _) -> [c]
+#else
+    TyConI (DataD    _ _ _ _ cs _) -> cs
+    TyConI (NewtypeD _ _ _ _ c  _) -> [c]
+#endif
+    _ -> error $ "error (typeConstructors): symbol " ++ show t
+              ++ " is neither newtype nor data"
+  where
+  simplify (NormalC n ts)  = (n,map snd ts)
+  simplify (RecC    n ts)  = (n,map trd ts)
+  simplify (InfixC  t1 n t2) = (n,[snd t1,snd t2])
+  trd (x,y,z) = z
+
+isTypeSynonym :: Name -> Q Bool
+isTypeSynonym t = do
+  ti <- reify t
+  return $ case ti of
+    TyConI (TySynD _ _ _) -> True
+    _                     -> False
+
+typeSynonymType :: Name -> Q Type
+typeSynonymType t = do
+  ti <- reify t
+  return $ case ti of
+    TyConI (TySynD _ _ t') -> t'
+    _ -> error $ "error (typeSynonymType): symbol " ++ show t
+              ++ " is not a type synonym"
+
+-- Append to instance contexts in a declaration.
+--
+-- > sequence [[|Eq b|],[|Eq c|]] |=>| [t|instance Eq a => Cl (Ty a) where f=g|]
+-- > == [t| instance (Eq a, Eq b, Eq c) => Cl (Ty a) where f = g |]
+(|=>|) :: Cxt -> DecsQ -> DecsQ
+c |=>| qds = do ds <- qds
+                return $ map (`ac` c) ds
+#if __GLASGOW_HASKELL__ < 800
+  where ac (InstanceD c ts ds) c' = InstanceD (c++c') ts ds
+        ac d                   _  = d
+#else
+  where ac (InstanceD o c ts ds) c' = InstanceD o (c++c') ts ds
+        ac d                     _  = d
+#endif
+
+mergeIFns :: DecsQ -> DecsQ
+mergeIFns qds = do ds <- qds
+                   return $ map m' ds
+  where
+#if __GLASGOW_HASKELL__ < 800
+  m' (InstanceD   c ts ds) = InstanceD   c ts [foldr1 m ds]
+#else
+  m' (InstanceD o c ts ds) = InstanceD o c ts [foldr1 m ds]
+#endif
+  FunD n cs1 `m` FunD _ cs2 = FunD n (cs1 ++ cs2)
+
+mergeI :: DecsQ -> DecsQ -> DecsQ
+qds1 `mergeI` qds2 = do ds1 <- qds1
+                        ds2 <- qds2
+                        return $ ds1 `m` ds2
+  where
+#if __GLASGOW_HASKELL__ < 800
+  [InstanceD   c ts ds1] `m` [InstanceD   _ _ ds2] = [InstanceD   c ts (ds1 ++ ds2)]
+#else
+  [InstanceD o c ts ds1] `m` [InstanceD _ _ _ ds2] = [InstanceD o c ts (ds1 ++ ds2)]
+#endif
+
+whereI :: DecsQ -> [Dec] -> DecsQ
+qds `whereI` w = do ds <- qds
+                    return $ map (`aw` w) ds
+#if __GLASGOW_HASKELL__ < 800
+  where aw (InstanceD   c ts ds) w' = InstanceD   c ts (ds++w')
+        aw d                     _  = d
+#else
+  where aw (InstanceD o c ts ds) w' = InstanceD o c ts (ds++w')
+        aw d                     _  = d
+#endif
+
+-- > nubMerge xs ys == nub (merge xs ys)
+-- > nubMerge xs ys == nub (sort (xs ++ ys))
+nubMerge :: Ord a => [a] -> [a] -> [a]
+nubMerge [] ys = ys
+nubMerge xs [] = xs
+nubMerge (x:xs) (y:ys) | x < y     = x :    xs  `nubMerge` (y:ys)
+                       | x > y     = y : (x:xs) `nubMerge`    ys
+                       | otherwise = x :    xs  `nubMerge`    ys
+
+nubMerges :: Ord a => [[a]] -> [a]
+nubMerges = foldr nubMerge []
+
+typeConstructorsArgNames :: Name -> Q [(Name,[Name])]
+typeConstructorsArgNames t = do
+  cs <- typeConstructors t
+  sequence [ do ns <- sequence [newName "x" | _ <- ts]
+                return (c,ns)
+           | (c,ts) <- cs ]
+
+lookupValN :: String -> Q Name
+lookupValN s = do
+  mn <- lookupValueName s
+  case mn of
+    Just n -> return n
+    Nothing -> fail $ "lookupValN: cannot find " ++ s
diff --git a/src/Data/Express/Utils/Typeable.hs b/src/Data/Express/Utils/Typeable.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Express/Utils/Typeable.hs
@@ -0,0 +1,121 @@
+-- |
+-- Module      : Data.Express.Utils.Typeable
+-- Copyright   : (c) 2016-2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- This module is part of Express.
+--
+-- Utilities to manipulate 'TypeRep's (of 'Typeable' values).
+module Data.Express.Utils.Typeable
+  ( tyArity
+  , unFunTy
+  , isFunTy
+  , argumentTy
+  , resultTy
+  , finalResultTy
+  , boolTy
+  , intTy
+  , orderingTy
+  , mkComparisonTy
+  , mkCompareTy
+  , funTyCon
+  , compareTy
+  , elementTy
+  , (->::)
+  , module Data.Typeable
+  )
+where
+
+import Data.Typeable
+import Data.Monoid ((<>))
+
+-- Different versions of Typeable/GHC provide different orderings for TypeReps.
+-- 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.
+compareTy :: TypeRep -> TypeRep -> Ordering
+compareTy t1 t2 | t1 == t2 = EQ -- optional optimization
+compareTy t1 t2 = tyArity t1 `compare` tyArity t2
+               <> length ts1 `compare` length ts2
+               <> show c1 `compare` show c2
+               <> foldr (<>) EQ (zipWith compareTy ts1 ts2)
+  where
+  (c1,ts1) = splitTyConApp t1
+  (c2,ts2) = splitTyConApp t2
+
+tyArity :: TypeRep -> Int
+tyArity t
+  | isFunTy t = 1 + tyArity (resultTy t)
+  | otherwise = 0
+
+finalResultTy :: TypeRep -> TypeRep
+finalResultTy t
+  | isFunTy t = finalResultTy (resultTy t)
+  | otherwise = t
+
+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"
+
+argumentTy :: TypeRep -> TypeRep
+argumentTy = fst . unFunTy
+
+resultTy :: TypeRep -> TypeRep
+resultTy = snd . unFunTy
+
+-- | This function returns the type of the element of a list.
+--   It will throw an error when not given the list type.
+--
+--   > > > elementTy $ typeOf (undefined :: [Int])
+--   > Int
+--   > > > elementTy $ typeOf (undefined :: [[Int]])
+--   > [Int]
+--   > > > elementTy $ typeOf (undefined :: [Bool])
+--   > Bool
+--   > > > elementTy $ typeOf (undefined :: Bool)
+--   > *** Exception: error (elementTy): `Bool' is not a list type
+elementTy :: TypeRep -> TypeRep
+elementTy t
+  | isListTy t = let (_,[a]) = splitTyConApp t in a
+  | otherwise = error $ "error (elementTy): `" ++ show t ++ "' is not a list type"
+
+boolTy :: TypeRep
+boolTy = typeOf (undefined :: Bool)
+
+intTy :: TypeRep
+intTy = typeOf (undefined :: Int)
+
+orderingTy :: TypeRep
+orderingTy = typeOf (undefined :: Ordering)
+
+funTyCon :: TyCon
+funTyCon = typeRepTyCon $ typeOf (undefined :: () -> ())
+
+listTyCon :: TyCon
+listTyCon = typeRepTyCon $ typeOf (undefined :: [()])
+
+isFunTy :: TypeRep -> Bool
+isFunTy t =
+  case splitTyConApp t of
+    (con,[_,_]) | con == funTyCon -> True
+    _ -> False
+
+isListTy :: TypeRep -> Bool
+isListTy t  =  case splitTyConApp t of
+  (con,[_]) | con == listTyCon -> True
+  _ -> False
+
+mkComparisonTy :: TypeRep -> TypeRep
+mkComparisonTy a = a ->:: a ->:: boolTy
+
+mkCompareTy :: TypeRep -> TypeRep
+mkCompareTy a = a ->:: a ->:: orderingTy
+
+-- | An infix alias for 'mkFunTy'.  It is right associative.
+(->::) :: TypeRep -> TypeRep -> TypeRep
+(->::) = mkFunTy
+infixr 9 ->::
diff --git a/stack-lts-11.yaml b/stack-lts-11.yaml
new file mode 100644
--- /dev/null
+++ b/stack-lts-11.yaml
@@ -0,0 +1,17 @@
+# stack.yaml docs:
+# https://docs.haskellstack.org/en/stable/yaml_configuration/
+
+# resolver: nightly-2015-09-21
+# resolver: ghc-7.10.2
+resolver: lts-11.22
+
+packages:
+- .
+
+extra-deps:
+- leancheck-0.9.1
+- speculate-0.3.5
+
+flags: {}
+
+extra-package-dbs: []
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,15 @@
+# stack.yaml docs:
+# https://docs.haskellstack.org/en/stable/yaml_configuration/
+
+# resolver: nightly-2015-09-21
+# resolver: ghc-7.10.2
+resolver: lts-13.6
+
+packages:
+- .
+
+extra-deps: []
+
+flags: {}
+
+extra-package-dbs: []
diff --git a/test/Test.hs b/test/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Test.hs
@@ -0,0 +1,77 @@
+-- |
+-- Module      : Test
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- This module defines utilities used to test "Data.Express".
+--
+-- It should never be exported in @ express.cabal @.
+module Test
+  ( module Test.LeanCheck
+  , module Test.LeanCheck.Utils
+  , module Test.LeanCheck.Derive
+  , module Data.Express.Fixtures
+  , module Data.Express.Utils.List
+  , module Data.Express.Utils.Typeable
+  , module Test.ListableExpr
+  , module Data.Maybe
+  , module Data.Either
+  , mainTest
+
+  , tyBool
+  , tyInt
+  , tyChar
+  , tyInts
+  , tyIntToInt
+  )
+where
+
+import System.Environment (getArgs)
+import System.Exit (exitFailure)
+import Data.List (elemIndices)
+import Data.Typeable (TypeRep, typeOf)
+
+import Data.Maybe
+import Data.Either
+import Test.LeanCheck
+import Test.LeanCheck.Utils
+import Test.LeanCheck.Derive
+import Test.ListableExpr
+import Data.Express.Fixtures
+import Data.Express.Utils.List
+import Data.Express.Utils.Typeable
+
+reportTests :: [Bool] -> IO ()
+reportTests tests =
+  case elemIndices False tests of
+    [] -> putStrLn "+++ Tests passed!"
+    is -> do putStrLn ("*** Failed tests:" ++ show is)
+             exitFailure
+
+getMaxTestsFromArgs :: Int -> IO Int
+getMaxTestsFromArgs n = do
+  as <- getArgs
+  return $ case as of
+             (s:_) -> read s
+             _     -> n
+
+mainTest :: (Int -> [Bool]) -> Int -> IO ()
+mainTest tests n' = do
+  n <- getMaxTestsFromArgs n'
+  reportTests (tests n)
+
+tyBool :: TypeRep
+tyBool  =  typeOf (undefined :: Bool)
+
+tyInt :: TypeRep
+tyInt  =  typeOf (undefined :: Int)
+
+tyChar  :: TypeRep
+tyChar  =  typeOf (undefined :: Char)
+
+tyInts :: TypeRep
+tyInts =  typeOf (undefined :: [Int])
+
+tyIntToInt :: TypeRep
+tyIntToInt  =  typeOf (undefined :: Int -> Int)
diff --git a/test/Test/ListableExpr.hs b/test/Test/ListableExpr.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/ListableExpr.hs
@@ -0,0 +1,285 @@
+-- |
+-- Module      : Test.ListableExpr
+-- Copyright   : (c) 2019 Rudy Matela
+-- License     : 3-Clause BSD  (see the file LICENSE)
+-- Maintainer  : Rudy Matela <rudy@matela.com.br>
+--
+-- This module exports a 'Listable' 'Expr' instance.
+-- This instance does not, by any means, list all possible expressions.
+-- It lists expressions based on a subset of the symbols exported by
+-- "Data.Express.Fixtures".
+--
+-- This is only intended to be used for testing,
+-- so this will not be exported on @ express.cabal @.
+module Test.ListableExpr
+  (
+  -- * The Expr type
+    Expr
+
+  -- * Expressions of a type
+  , IntE (..)
+  , BoolE (..)
+  , IntsE (..)
+  , CharE (..)
+
+  , IntE0 (..)
+  , IntEV (..)
+  , BoolE0 (..)
+  , BoolEV (..)
+  , IntsE0 (..)
+  , IntsEV (..)
+  , CharE0 (..)
+  , CharEV (..)
+
+  -- ** Functional values
+  , IntToIntE (..)
+  , IntToIntToIntE (..)
+  , BoolToBoolE (..)
+  , BoolToBoolToBoolE (..)
+
+  , SameTypeE (..)
+  , unSameTypeE
+  , SameTypedPairsE (..)
+
+  -- * Terminal expressions
+  , E0 (..)
+  , EV (..)
+
+  -- * Ill typed expressions
+  , Ill (..)
+  )
+where
+
+-- TODO: StringE
+
+import Test.LeanCheck
+import Test.LeanCheck.Function.ShowFunction
+import Data.Express.Fixtures
+import Data.Function (on)
+
+-- | Terminal constants.
+newtype E0  =  E0 { unE0 :: Expr }
+
+-- | Variables.
+newtype EV  =  EV { unEV :: Expr }
+
+-- | Expression of 'Int' type.
+newtype IntE  =  IntE { unIntE :: Expr }
+
+-- | Constant terminal value of 'Int' type.
+newtype IntE0  =  IntE0 { unIntE0 :: Expr }
+
+-- | Varialbe of 'Int' type.
+newtype IntEV  =  IntEV { unIntEV :: Expr }
+
+-- | Functions from Int to Int
+newtype IntToIntE  =  IntToIntE { unIntToIntE :: Expr }
+newtype IntToIntToIntE  =  IntToIntToIntE { unIntToIntToIntE :: Expr }
+
+-- | Expression of 'Bool' type.
+newtype BoolE  =  BoolE { unBoolE :: Expr }
+
+-- | Constant terminal value of 'Bool' type.
+newtype BoolE0  =  BoolE0 { unBoolE0 :: Expr }
+
+-- | Varialbe of 'Bool' type.
+newtype BoolEV  =  BoolEV { unBoolEV :: Expr }
+
+-- | Functions from Bool to Bool
+newtype BoolToBoolE  =  BoolToBoolE { unBoolToBoolE :: Expr }
+newtype BoolToBoolToBoolE  =  BoolToBoolToBoolE { unBoolToBoolToBoolE :: Expr }
+
+newtype CharE  =  CharE { unCharE :: Expr }
+
+newtype CharE0  =  CharE0 { unCharE0 :: Expr }
+
+newtype CharEV  =  CharEV { unCharEV :: Expr }
+
+data SameTypeE  =  SameTypeE Expr Expr
+
+unSameTypeE :: SameTypeE -> (Expr,Expr)
+unSameTypeE (SameTypeE e1 e2)  =  (e1,e2)
+
+data SameTypedPairsE  =  SameTypedPairsE { unSameTypedPairsE :: [(Expr,Expr)] }
+
+-- | Ill typed expressions.
+newtype Ill  =  Ill { unIll :: Expr }
+
+
+instance Show E0  where  show (E0 e) = show e
+instance Show EV  where  show (EV e) = show e
+
+instance Show IntE  where  show (IntE e) = show e
+
+instance Show IntE0  where  show (IntE0 e) = show e
+instance Show IntEV  where  show (IntEV e) = show e
+
+instance Show IntToIntE  where  show (IntToIntE e) = show e
+instance Show IntToIntToIntE  where  show (IntToIntToIntE e) = show e
+
+instance Show BoolE  where  show (BoolE e) = show e
+
+instance Show BoolE0  where  show (BoolE0 e) = show e
+instance Show BoolEV  where  show (BoolEV e) = show e
+
+instance Show BoolToBoolE  where  show (BoolToBoolE e) = show e
+instance Show BoolToBoolToBoolE  where  show (BoolToBoolToBoolE e) = show e
+
+instance Show IntsE  where  show (IntsE e) = show e
+
+instance Show IntsE0  where  show (IntsE0 e) = show e
+instance Show IntsEV  where  show (IntsEV e) = show e
+
+instance Show CharE  where  show (CharE e) = show e
+
+instance Show CharE0  where  show (CharE0 e) = show e
+instance Show CharEV  where  show (CharEV e) = show e
+
+instance Show SameTypeE  where  show (SameTypeE e1 e2) = show (e1,e2)
+
+instance Show SameTypedPairsE  where  show (SameTypedPairsE ees)  =  show ees
+
+-- | Expression of 'Ints' type.
+newtype IntsE  =  IntsE { unIntsE :: Expr }
+
+-- | Constant terminal value of 'Ints' type.
+newtype IntsE0  =  IntsE0 { unIntsE0 :: Expr }
+
+-- | Varialbe of 'Ints' type.
+newtype IntsEV  =  IntsEV { unIntsEV :: Expr }
+
+instance Show Ill where  show (Ill e) = show e
+
+instance Listable IntE  where
+  tiers  =  mapT IntE
+         $  cons0 i_
+         \/ cons1 unIntEV
+         \/ cons1 unIntE0
+         \/ cons2 (\(IntToIntE f) (IntE xx) -> f :$ xx)
+         \/ cons1 (head' . unIntsE) `ofWeight` 2
+         \/ cons1 (ord' . unCharE) `ofWeight` 2
+
+instance Listable IntE0 where
+  tiers  =  (IntE0 . val) `mapT` (tiers :: [[Int]])
+
+instance Listable IntEV where
+  list  =  map IntEV $ listVars "x" (undefined :: Int)
+
+instance Listable IntToIntE where
+  tiers  =  mapT IntToIntE
+         $  cons0 idE
+         \/ cons0 negateE `addWeight` 1
+         \/ cons0 absE    `addWeight` 1
+         \/ cons2 (\(IntToIntToIntE ef) (IntE ex) -> ef :$ ex)
+         \/ toTiers (listVars "f" (undefined :: Int -> Int)) `addWeight` 2
+
+instance Listable IntToIntToIntE where
+  list  =  map IntToIntToIntE [plus, times]
+
+instance Listable IntsE  where
+  tiers  =  mapT IntsE
+         $  cons0 is_
+         \/ cons1 unIntsEV
+         \/ cons1 unIntsE0
+         \/ cons2 (\(IntE ex) (IntsE exs) -> ex -:- exs)
+         \/ cons1 (tail' . unIntsE) `ofWeight` 2
+         \/ cons2 (\(IntsE exs) (IntsE eys) -> exs -++- eys) `ofWeight` 2
+         \/ cons1 (\(IntsE exs) -> sort' exs) `ofWeight` 3
+         \/ cons2 (\(IntE ex) (IntsE exs) -> insert' ex exs) `ofWeight` 3
+
+instance Listable IntsE0 where
+  tiers  =  (IntsE0 . val) `mapT` (tiers :: [[ [Int] ]])
+
+instance Listable IntsEV where
+  list  =  map IntsEV $ listVars "xs" (undefined :: [Int])
+
+instance Listable BoolE  where
+  tiers  =  mapT BoolE
+         $  cons0 b_
+         \/ cons1 unBoolEV
+         \/ cons1 unBoolE0
+         \/ cons2 (\(BoolToBoolE ef) (BoolE ep) -> ef :$ ep)
+         \/ cons2 ((-==-) `on` unIntE)  `addWeight` 2
+         \/ cons2 ((-==-) `on` unBoolE) `addWeight` 2
+         \/ cons2 ((-<=-) `on` unIntE)  `addWeight` 3
+         \/ cons2 ((-<=-) `on` unBoolE) `addWeight` 3
+         \/ cons2 ((-<-)  `on` unIntE)  `addWeight` 4
+         \/ cons2 ((-<-)  `on` unBoolE) `addWeight` 4
+         \/ cons2 ((-/=-) `on` unIntE)  `addWeight` 5
+         \/ cons2 ((-/=-) `on` unBoolE) `addWeight` 5
+         \/ cons1 (odd'  . unIntE) `addWeight` 2
+         \/ cons1 (even' . unIntE) `addWeight` 2
+         \/ cons2 (\(IntE ex) (IntsE exs) -> elem' ex exs) `addWeight` 2
+
+instance Listable BoolE0 where
+  tiers  =  (BoolE0 . val) `mapT` (tiers :: [[Bool]])
+
+instance Listable BoolEV where
+  list  =  map BoolEV $ listVars "p" (undefined :: Bool)
+
+instance Listable BoolToBoolE where
+  tiers  =  mapT BoolToBoolE
+         $  cons0 notE
+         \/ cons2 (\(BoolToBoolToBoolE ef) (BoolE ex) -> ef :$ ex)
+
+instance Listable BoolToBoolToBoolE where
+  list  =  map BoolToBoolToBoolE [orE, andE, implies]
+
+instance Listable CharE where
+  tiers  =  mapT CharE $ cons0 c_
+                      \/ cons1 unCharEV
+                      \/ cons1 unCharE0
+
+instance Listable CharEV where
+  list  =  map CharEV $ listVars "c" (undefined :: Char)
+
+instance Listable CharE0 where
+  tiers  =  (CharE0 . val) `mapT` (tiers :: [[Char]])
+
+instance Listable SameTypeE where
+  tiers = cons1 (\(IntE  e1, IntE  e2) -> SameTypeE e1 e2) `ofWeight` 0
+       \/ cons1 (\(BoolE e1, BoolE e2) -> SameTypeE e1 e2) `ofWeight` 1
+       \/ cons1 (\(IntsE e1, IntsE e2) -> SameTypeE e1 e2) `ofWeight` 1
+       \/ cons1 (\(CharE e1, CharE e2) -> SameTypeE e1 e2) `ofWeight` 2
+       \/ cons1 (\(IntToIntE e1, IntToIntE e2)     -> SameTypeE e1 e2) `ofWeight` 2
+       \/ cons1 (\(BoolToBoolE e1, BoolToBoolE e2) -> SameTypeE e1 e2) `ofWeight` 2
+       \/ cons1 (\(BoolToBoolToBoolE e1, BoolToBoolToBoolE e2) -> SameTypeE e1 e2) `ofWeight` 2
+       \/ cons1 (\(IntToIntToIntE e1, IntToIntToIntE e2)       -> SameTypeE e1 e2) `ofWeight` 2
+
+instance Listable SameTypedPairsE where
+  tiers = cons1 (SameTypedPairsE . map unSameTypeE) `ofWeight` 0
+
+instance Listable E0 where
+  tiers  =  mapT E0
+         $  cons1 unIntE0  `ofWeight` 0
+         \/ cons1 unBoolE0 `ofWeight` 1
+         \/ cons1 unIntsE0 `ofWeight` 1
+
+instance Listable EV where
+  tiers  =  mapT EV
+         $  cons1 unIntEV  `ofWeight` 0
+         \/ cons1 unBoolEV `ofWeight` 1
+         \/ cons1 unIntsEV `ofWeight` 1
+
+
+instance Listable Expr where
+  tiers  =  reset (cons1 unIntE)
+         \/ cons1 unBoolE
+         \/ cons1 unCharE
+         \/ cons1 unIntsE
+         \/ cons1 unIntToIntE         `addWeight` 1
+         \/ cons1 unIntToIntToIntE    `addWeight` 1
+         \/ cons1 unBoolToBoolE       `addWeight` 2
+         \/ cons1 unBoolToBoolToBoolE `addWeight` 2
+
+
+-- | This listable instance only produces Ill typed expressions
+instance Listable Ill where
+  tiers  =  mapT Ill
+         $  cons2 (\(IntE ef) (IntE ex) -> ef :$ ex) `ofWeight` 0
+         \/ cons2 (\(IntToIntE ef) (IntToIntE ex) -> ef :$ ex)
+         \/ cons2 (\(Ill ef) ex -> ef :$ ex)
+         \/ cons2 (\ef (Ill ex)-> ef :$ ex)
+
+
+instance ShowFunction Expr where bindtiers  =  bindtiersShow
diff --git a/test/canon.hs b/test/canon.hs
new file mode 100644
--- /dev/null
+++ b/test/canon.hs
@@ -0,0 +1,119 @@
+-- Copyright (c) 2017-2018 Rudy Matela.  -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , canonicalize (xx -+- yy)
+              == (xx -+- yy)
+  , canonicalize (jj -+- (ii -+- ii))
+              == (xx -+- (yy -+- yy))
+  , canonicalize ((jj -+- ii) -+- (xx -+- xx))
+              == ((xx -+- yy) -+- (zz -+- zz))
+
+  -- these are just tests:
+  -- canonicalizeWith expects the resulting list of the arg function to be infinite
+  , canonicalizeWith (const ["i","j","k","l"]) (xx -+- yy)
+                                            == (ii -+- jj)
+  , canonicalizeWith (const ["i","j","k","l"]) (jj -+- (ii -+- ii))
+                                            == (ii -+- (jj -+- jj))
+  , canonicalizeWith (const ["i","j","k","l"]) ((jj -+- ii) -+- (xx -+- xx))
+                                            == ((ii -+- jj) -+- (kk -+- kk))
+
+  , canonicalize (xx -+- ord' cc) == (xx -+- ord' cc)
+  , canonicalize (yy -+- ord' dd) == (xx -+- ord' cc)
+  , canonicalize (ff xx -+- gg yy -+- ff yy) == (ff xx -+- gg yy -+- ff yy)
+  , canonicalize (gg yy -+- ff xx -+- gg xx) == (ff xx -+- gg yy -+- ff yy)
+  , canonicalizeWith (lookupNames $ reifyName (undefined :: Int -> Int -> Int) ++ preludeNameInstances)
+      (zz -?- kk) == (var "f" (undefined :: Int -> Int -> Int) :$ xx :$ yy)
+
+  -- canonicalizing holes --
+  , canonicalize (hole (undefined :: Int       )) == xx
+  , canonicalize (hole (undefined :: Bool      )) == pp
+  , canonicalize (hole (undefined :: Char      )) == cc
+  , canonicalize (hole (undefined :: [Int]     )) == xxs
+  , canonicalize (hole (undefined :: [Char]    )) == ccs
+  , canonicalize (hole (undefined :: ()        )) == var "u" ()
+  , canonicalize (hole (undefined :: Integer   )) == var "x" (undefined :: Integer)
+  , canonicalize (hole (undefined :: [Integer] )) == var "xs" (undefined :: [Integer])
+  , canonicalize (hole (undefined :: Maybe Int )) == var "mx" (undefined :: Maybe Int)
+  , canonicalize (hole (undefined :: (Int,Int) )) == var "xy" (undefined :: (Int,Int))
+
+  , canonicalVariations (zero -+- xx) == [zero -+- xx]
+  , canonicalVariations (zero -+- i_) == [zero -+- xx]
+  , canonicalVariations (i_ -+- i_) == [xx -+- yy, xx -+- xx]
+  , canonicalVariations (i_ -+- (i_ -+- ord' c_))
+    == [ xx -+- (yy -+- ord' cc)
+       , xx -+- (xx -+- ord' cc) ]
+
+  , fastCanonicalVariations (ii -+- i_) == [ii -+- xx]
+  , canonicalVariations ((i_ -+- i_) -+- (ord' c_ -+- ord' c_))
+    == [ (xx -+- yy) -+- (ord' cc -+- ord' dd)
+       , (xx -+- yy) -+- (ord' cc -+- ord' cc)
+       , (xx -+- xx) -+- (ord' cc -+- ord' dd)
+       , (xx -+- xx) -+- (ord' cc -+- ord' cc) ]
+
+  , canonicalVariations (i_)
+    == [ xx ]
+  , canonicalVariations (i_ -+- i_)
+    == [ xx -+- yy
+       , xx -+- xx ]
+  , canonicalVariations (i_ -+- i_ -+- i_)
+    == [ xx -+- yy -+- zz
+       , xx -+- yy -+- xx
+       , xx -+- yy -+- yy
+       , xx -+- xx -+- yy
+       , xx -+- xx -+- xx
+       ]
+  , canonicalVariations (i_ -+- i_ -+- i_ -+- i_)
+    == [ xx -+- yy -+- zz -+- xx'
+       , xx -+- yy -+- zz -+- xx
+       , xx -+- yy -+- zz -+- yy
+       , xx -+- yy -+- zz -+- zz
+       , xx -+- yy -+- xx -+- zz
+       , xx -+- yy -+- xx -+- xx
+       , xx -+- yy -+- xx -+- yy
+       , xx -+- yy -+- yy -+- zz
+       , xx -+- yy -+- yy -+- xx
+       , xx -+- yy -+- yy -+- yy
+       , xx -+- xx -+- yy -+- zz
+       , xx -+- xx -+- yy -+- xx
+       , xx -+- xx -+- yy -+- yy
+       , xx -+- xx -+- xx -+- yy
+       , xx -+- xx -+- xx -+- xx
+       ]
+
+  , holds n $ \e -> all isHole (vars e)
+                ==> let xs = map (length . nubVars) $ canonicalVariations e
+                    in (head xs >) `all` tail xs
+                    && (last xs <) `all` init xs
+  , holds n $ \e -> all isHole (vars e)
+                ==> isNub (vars (head (canonicalVariations e)))
+  , holds n $ \e -> all isHole (vars e)
+                ==> let es = canonicalVariations e
+                    in (`isInstanceOf` head es) `all` tail es
+                    && (last es `isInstanceOf`) `all` init es
+  , holds n $ \e -> let es = canonicalVariations e
+                    in length (nub (sort es)) == length es
+  , holds n $ \e -> length (canonicalVariations e)
+                 == product (map (bell . snd) . counts $ holes e)
+  ]
+
+-- O(1) bell number implementation
+-- only works up to 8
+-- but this is enough for testing.
+bell :: Int -> Int
+bell 0 = 1
+bell 1 = 1
+bell 2 = 2
+bell 3 = 5
+bell 4 = 15
+bell 5 = 52
+bell 6 = 203
+bell 7 = 877
+bell 8 = 4140
+bell _ = error "bell: argument > 8, implement me!"
diff --git a/test/core.hs b/test/core.hs
new file mode 100644
--- /dev/null
+++ b/test/core.hs
@@ -0,0 +1,254 @@
+-- Copyright (c) 2019 Rudy Matela.  -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+import Data.Express.Utils.List
+import Test.LeanCheck.Error (errorToNothing)
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  -- smart constructors and evaluation
+
+  , holds n $ \x -> eval (undefined :: Int -> Int) (value "abs" (abs :: Int -> Int)) x == abs (x :: Int)
+  , evl (val (10 :: Int)) == (10 :: Int)
+  , evl (val (1337 :: Int)) == (1337 :: Int)
+  , evl (val False) == False
+  , holds n $ \x y -> evl (value "+" ((+) :: Int -> Int -> Int) :$ val x :$ val y) == (x + y :: Int)
+  , holds n $ \x y -> evl (value "+" ((*) :: Int -> Int -> Int) :$ val x :$ val y) == (x * y :: Int)
+  , holds n $ \i -> evl (val i) == (i :: Int)
+  , show (one -+- one) == "1 + 1 :: Int"
+  , show absE == "abs :: Int -> Int"
+  , show notE == "not :: Bool -> Bool"
+  , show andE == "(&&) :: Bool -> Bool -> Bool"
+  , show (pp -&&- (not' false)) == "p && not False :: Bool"
+  , show (one :$ one) == "1 1 :: ill-typed # Int $ Int #"
+  , holds n $ \(IntE xx, IntE yy) -> isJust (toDynamic $ xx -+- yy)
+  , holds n $ \(IntE xx, IntE yy) -> isGround xx && isGround yy
+                                 ==> evl (xx -+- yy) =$ errorToNothing $= (evl (yy -+- xx) :: Int)
+
+  -- valid applications
+  , holds n $ \(IntToIntE   ef) (IntE  ex) -> isJust (ef $$ ex)
+  , holds n $ \(BoolToBoolE ef) (BoolE ep) -> isJust (ef $$ ep)
+
+  -- invalid applications
+  , holds n $ \(IntE ex)  (IntE  ey) -> isNothing (ex $$ ey)
+  , holds n $ \(BoolE ep) (BoolE eq) -> isNothing (ep $$ eq)
+  , holds n $ \(BoolToBoolE ef) (IntE  ex) -> isNothing (ef $$ ex)
+  , holds n $ \(IntToIntE   ef) (BoolE ep) -> isNothing (ef $$ ep)
+  , holds n $ \(IntE ex) (IntE ey) (IntE ez) -> isNothing (ex $$ (ey :$ ez))
+  , holds n $ \(IntE ex) (IntE ey) (IntE ez) -> isNothing ((ex :$ ey) $$ ez)
+
+
+  -- typing
+  , typ zero       == tyInt
+  , typ one        == tyInt
+  , typ xx         == tyInt
+  , typ bee        == tyChar
+  , typ xxs        == tyInts
+  , typ (ff xx)    == tyInt
+  , typ (abs' one) == tyInt
+  , typ true       == tyBool
+  , typ pp         == tyBool
+
+  , etyp zero       == Right tyInt
+  , etyp (abs' one) == Right tyInt
+  , etyp (abs' bee) == Left (tyIntToInt, tyChar)
+  , etyp (abs' bee :$ zero) == Left (tyIntToInt, tyChar)
+  , etyp ((zero :$ one) :$ (bee :$ cee)) == Left (tyInt, tyInt)
+
+  , etyp (xx :$ yy) == Left (tyInt, tyInt)
+  , etyp (xx :$ (cc :$ yy)) == Left (tyChar, tyInt)
+  , etyp (abs' xx :$ (ord' cc :$ negate' yy)) == Left (tyInt, tyInt)
+  , holds n $ \(SameTypeE ef eg) (SameTypeE ex ey) -> (etyp (ef :$ ex) == etyp (eg :$ ey))
+  , holds n $ \ef eg ex ey -> (etyp ef == etyp eg && etyp ex == etyp ey)
+                           == (etyp (ef :$ ex) == etyp (eg :$ ey))
+
+  , isIllTyped (abs' zero) == False
+  , isIllTyped (zero :$ one) == True
+  , isWellTyped (abs' zero) == True
+  , isWellTyped (zero :$ one) == False
+
+  -- eq instance
+  , xx -+- yy == xx -+- yy
+  , xx -+- yy /= yy -+- xx
+
+
+  -- our Listable Expr enumeration does not produce ill typed Exprs
+  , holds n $ isRight . etyp
+  , holds n $ isJust  . mtyp
+  , holds n $ isWellTyped
+  , holds n $ not . isIllTyped
+
+  -- our Listable Ill enumeration only produces ill typed Exprs
+  , holds n $ isLeft    . etyp . unIll
+  , holds n $ isNothing . mtyp . unIll
+  , holds n $ isIllTyped . unIll
+  , holds n $ not . isWellTyped . unIll
+
+  -- we don't need the precondition here given the above
+  -- but it's added just in case
+  , holds n $ \e -> isRight (etyp e) ==> etyp e == Right (typ e)
+  , holds n $ \e -> isJust  (mtyp e) ==> mtyp e == Just  (typ e)
+
+  -- we prefer returning errors to the left
+  , holds n $ \(Ill ef) (Ill ex) -> etyp (ef :$ ex) == etyp ef
+  , holds n $ \ef (Ill ex) -> etyp (ef :$ ex) == etyp ex
+
+
+  -- boolean properties
+
+  , hasVar (zero -+- one) == False
+  , hasVar (xx -+- yy) == True
+
+  , isGround (zero -+- (one -*- two)) == True
+  , isGround (xx -+- (one -*- three)) == False
+
+  , holds n $ isGround === not . hasVar
+
+  -- isValue and isApp
+  , holds n $ \e1 e2  ->  isValue (e1 :$ e2)  ==  False
+  , holds n $ \e1 e2  ->  isApp   (e1 :$ e2)  ==  True
+  , holds n $ isValue === not . isApp
+  , holds n $ isApp   === not . isValue
+  , holds n $ \e  ->  isValue e  ==  (isVar e || isConst e)
+  , holds n $ \e  ->  isApp e    ==  (not (isVar e) && not (isConst e))
+
+  , isVar xx == True
+  , isVar yy == True
+  , isVar ffE == True
+  , isVar (xx -+- yy) == False
+  , isVar (ff xx) == False
+  , isVar one == False
+  , isVar (one -+- two) == False
+
+  , isHole i_ == True
+  , isHole b_ == True
+  , isHole xx == False
+
+  , isConst xx == False
+  , isConst yy == False
+  , isConst (xx -+- yy) == False
+  , isConst (ff xx) == False
+  , isConst one == True
+  , isConst two == True
+  , isConst absE == True
+  , isConst (one -+- two) == False
+
+  , values (xx -+- yy) == [plus, xx, yy]
+  , values (xx -+- (yy -+- zz)) == [plus, xx, plus, yy, zz]
+  , values ((xx -+- yy) -+- zz) == [plus, plus, xx, yy, zz]
+  , values (zero -+- (one -*- two)) == [plus, zero, times, one, two]
+  , values (pp -&&- true) == [andE, pp, true]
+
+  , subexprs (xx -+- yy) ==
+      [ xx -+- yy
+      , plus :$ xx
+      , plus
+      , xx
+      , yy
+      ]
+  , subexprs (pp -&&- (pp -&&- true)) ==
+      [ pp -&&- (pp -&&- true)
+      , andE :$ pp
+      , andE
+      , pp
+      , pp -&&- true
+      , andE :$ pp
+      , andE
+      , pp
+      , true
+      ]
+  , nubSubexprs (xx -+- yy) ==
+      [ xx
+      , yy
+      , plus
+      , plus :$ xx
+      , xx -+- yy
+      ]
+  , nubSubexprs (pp -&&- (pp -&&- true)) ==
+      [ pp
+      , true
+      , andE
+      , andE :$ pp
+      , pp -&&- true
+      , pp -&&- (pp -&&- true)
+      ]
+
+
+  -- boolean properties
+  , holds n $ \e -> isHole e ==> isVar e
+
+  -- listing subexpressions
+  , holds n $ \e -> isGround e ==> consts e == values e
+
+  , holds n $ \e -> nubSubexprs e `isSubsetOf` subexprs e
+  , holds n $ \e -> nubValues e `isSubsetOf` values e
+  , holds n $ \e -> nubVars   e `isSubsetOf` vars   e
+  , holds n $ \e -> nubConsts e `isSubsetOf` consts e
+  , holds n $ \e -> values e    `isSubsetOf` subexprs e
+  , holds n $ \e -> vars      e `isSubsetOf` values e
+  , holds n $ \e -> consts    e `isSubsetOf` values e
+  , holds n $ \e -> (vars e ++ consts e) `isPermutationOf` values e
+  , holds n $ \e -> (nubVars e ++ nubConsts e) `isPermutationOf` nubValues e
+
+  -- in case implementation changes
+  , holds n $ \e -> nubSubexprs e == nubSort (subexprs e)
+  , holds n $ \e -> nubValues   e == nubSort (values e)
+  , holds n $ \e -> nubVars     e == nubSort (vars e)
+  , holds n $ \e -> nubConsts   e == nubSort (consts e)
+
+  , arity zero == 0
+  , arity xx == 0
+  , arity absE == 1
+  , arity plus == 2
+  , arity times == 2
+
+  , size zero == 1
+  , size (one -+- two) == 3
+  , size (abs' one) == 2
+
+  , depth zero == 1
+  , depth (one -+- two) == 2
+  , depth (abs' one -+- two) == 3
+
+  , height zero == 1
+  , height (abs' one) == 2
+  , height ((const' one) two) == 3
+  , height ((const' (abs' one)) two) == 4
+  , height ((const' one) (abs' two)) == 3
+
+  , holds n $ \e -> depth e  <= height e
+  , holds n $ \e -> depth e  <= size e
+  , holds n $ \e -> height e <= size e
+
+  , size  zero == 1
+  , depth zero == 1
+  , size  one  == 1
+  , depth one  == 1
+  , size  (zero -+- one) == 3
+  , depth (zero -+- one) == 2
+  , size  (zero -+- (xx -+- yy)) == 5
+  , depth (zero -+- (xx -+- yy)) == 3
+  , size  (((xx -+- yy) -*- zz) -==- ((xx -*- zz) -+- (yy -*- zz))) == 13
+  , depth (((xx -+- yy) -*- zz) -==- ((xx -*- zz) -+- (yy -*- zz))) ==  4
+  , depth (xx -*- yy -+- xx -*- zz -==- xx -*- (yy -+- zz)) == 4
+  , size  (xx -*- yy -+- xx -*- zz -==- xx -*- (yy -+- zz)) == 13
+  , depth (xx -*- yy -+- xx -*- zz) == 3
+  , depth (xx -*- (yy -+- zz)) == 3
+
+  , nubConsts (xx -+- yy) == [plus]
+  , nubConsts (xx -+- (yy -+- zz)) == [plus]
+  , nubConsts (zero -+- one) =$ sort $= [zero, one, plus]
+  , nubConsts ((zero -+- abs' zero) -+- (ord' ae -+- ord' cc))
+      =$ sort $= [zero, ae, absE, plus, ordE]
+  , holds n $ \e1 e2 -> times `elem` consts (e1 -*- e2)
+
+  , vars (xx -+- yy) == [xx, yy]
+  , nubVars (xx -+- xx) == [xx]
+  , nubVars (xx -+- xx -+- yy) == [xx, yy]
+  , nubVars (yy -+- xx -+- yy) == [xx, yy]
+  ]
diff --git a/test/express-derive.hs b/test/express-derive.hs
new file mode 100644
--- /dev/null
+++ b/test/express-derive.hs
@@ -0,0 +1,111 @@
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+
+import Test
+
+
+data Choice  =  Ae | Bee | Cee deriving (Show, Eq, Typeable)
+data Peano  =  Zero | Succ Peano deriving (Show, Eq, Typeable)
+data List a  =  a :- List a | Nil deriving (Show, Eq, Typeable)
+data Bush a  =  Bush a :-: Bush a | Leaf a deriving (Show, Eq, Typeable)
+data Tree a  =  Node (Tree a) a (Tree a) | Null deriving (Show, Eq, Typeable)
+
+deriveExpress ''Choice
+deriveExpress ''Peano
+deriveExpress ''List
+deriveExpress ''Bush
+deriveExpress ''Tree
+
+deriveListable ''Choice
+deriveListable ''Peano
+deriveListable ''List
+deriveListable ''Bush
+deriveListable ''Tree
+
+-- Nested datatype cascade
+data Nested  =  Nested N0 (N1 Int) (N2 Int Int) deriving (Eq, Show, Typeable)
+data N0      =  R0 Int deriving (Eq, Show, Typeable)
+data N1 a    =  R1 a   deriving (Eq, Show, Typeable)
+data N2 a b  =  R2 a b deriving (Eq, Show, Typeable)
+
+deriveExpressCascading ''Nested
+deriveListableCascading ''Nested
+
+-- Recursive nested datatype cascade
+data RN       =  RN RN0 (RN1 Int) (RN2 Int RN) deriving (Eq, Show, Typeable)
+data RN0      =  Nest0 Int | Recurse0 RN deriving (Eq, Show, Typeable)
+data RN1 a    =  Nest1 a   | Recurse1 RN deriving (Eq, Show, Typeable)
+data RN2 a b  =  Nest2 a b | Recurse2 RN deriving (Eq, Show, Typeable)
+-- beware: values of the above type are always infinite!
+--         derivation works but full evaluation does not terminate
+
+deriveExpressCascading ''RN
+deriveListableCascading ''RN
+
+-- Those should have no effect (instance already exists):
+{- uncommenting those should generate warnings
+deriveExpress ''Bool
+deriveExpress ''Maybe
+deriveExpress ''Either
+-}
+
+-- Those should not generate warnings
+deriveExpressIfNeeded ''Bool
+deriveExpressIfNeeded ''Maybe
+deriveExpressIfNeeded ''Either
+
+data Mutual    =  Mutual0   | Mutual CoMutual deriving (Eq, Show, Typeable)
+data CoMutual  =  CoMutual0 | CoMutual Mutual deriving (Eq, Show, Typeable)
+
+deriveListableCascading ''Mutual
+deriveExpressCascading ''Mutual
+
+
+main :: IO ()
+main  =  mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n  =
+  [ True
+
+  , holds n (exprIsVal :: Choice -> Bool)
+  , fails n (exprIsVal :: Peano -> Bool)
+
+  , fails n (exprIsVal :: List Int -> Bool)
+  , fails n (exprIsVal :: List Bool -> Bool)
+
+  , fails n (exprIsVal :: Bush Int -> Bool)
+  , fails n (exprIsVal :: Bush Bool -> Bool)
+
+  , fails n (exprIsVal :: Tree Int -> Bool)
+  , fails n (exprIsVal :: Tree Bool -> Bool)
+
+  , holds n (exprIsValUnderEvaluate :: Choice -> Bool)
+  , holds n (exprIsValUnderEvaluate :: Peano -> Bool)
+
+  , holds n (exprIsValUnderEvaluate :: List Int -> Bool)
+  , holds n (exprIsValUnderEvaluate :: List Bool -> Bool)
+
+  , holds n (exprIsValUnderEvaluate :: Bush Int -> Bool)
+  , holds n (exprIsValUnderEvaluate :: Bush Bool -> Bool)
+
+  , holds n (exprIsValUnderEvaluate :: Tree Int -> Bool)
+  , holds n (exprIsValUnderEvaluate :: Tree Bool -> Bool)
+
+  , holds n (exprIsValUnderEvaluate :: Nested -> Bool)
+  , holds n (exprIsValUnderEvaluate :: N0 -> Bool)
+  , holds n (exprIsValUnderEvaluate :: N1 Int -> Bool)
+  , holds n (exprIsValUnderEvaluate :: N2 Int Bool -> Bool)
+
+  , holds n (exprIsValUnderEvaluate :: Mutual -> Bool)
+  , holds n (exprIsValUnderEvaluate :: CoMutual -> Bool)
+  ]
+
+-- not true in all cases
+exprIsVal :: (Listable a, Express a, Show a) => a -> Bool
+exprIsVal x  =  expr x == val x
+
+exprIsValUnderEvaluate :: (Listable a, Express a, Show a, Eq a) => a -> Bool
+exprIsValUnderEvaluate x  =  evaluate (expr x) == (evaluate (val x) -: mayb x)
diff --git a/test/express.hs b/test/express.hs
new file mode 100644
--- /dev/null
+++ b/test/express.hs
@@ -0,0 +1,109 @@
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+{-# LANGUAGE CPP #-}
+import Test
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , holds n $ \x -> expr x == val (x :: Int)
+  , holds n $ \c -> expr c == val (c :: Char)
+  , holds n $ \p -> expr p == val (p :: Bool)
+  , holds n $ \x -> expr x == val (x :: ())
+  , fails n $ \xs -> expr xs == val (xs :: [Int])
+  , holds n $ expr ([] :: [Int]) == val ([] :: [Int])
+  , holds n $ \x xs -> expr (x:xs) /= val (x:xs :: [Int])
+
+  , holds n (okExpress :: ()       -> Bool)
+  , holds n (okExpress :: Bool     -> Bool)
+  , holds n (okExpress :: Int      -> Bool)
+  , holds n (okExpress :: Integer  -> Bool)
+  , holds n (okExpress :: Char     -> Bool)
+  , holds n (okExpress :: Ordering -> Bool)
+
+  , holds n (okExpress :: [Bool]     -> Bool)
+  , holds n (okExpress :: [Int]      -> Bool)
+  , holds n (okExpress :: [Integer]  -> Bool)
+  , holds n (okExpress :: [Char]     -> Bool)
+  , holds n (okExpress :: [Ordering] -> Bool)
+
+  , holds n (okExpress :: ((),())     -> Bool)
+  , holds n (okExpress :: (Bool,Bool) -> Bool)
+  , holds n (okExpress :: (Int,Int)   -> Bool)
+  , holds n (okExpress :: ((),Bool)   -> Bool)
+  , holds n (okExpress :: (Bool,Int)  -> Bool)
+
+  , holds n (okExpress :: Maybe ()   -> Bool)
+  , holds n (okExpress :: Maybe Bool -> Bool)
+  , holds n (okExpress :: Maybe Int  -> Bool)
+
+  , holds n (okExpress :: Either () ()     -> Bool)
+  , holds n (okExpress :: Either Bool Bool -> Bool)
+  , holds n (okExpress :: Either Int Int   -> Bool)
+  , holds n (okExpress :: Either () Bool   -> Bool)
+  , holds n (okExpress :: Either Bool Int  -> Bool)
+
+  , holds n (okExpress :: (Int,Int,Int) -> Bool)
+  , holds n (okExpress :: (Int,Int,Int,Int) -> Bool)
+  , holds n (okExpress :: (Int,Int,Int,Int,Int) -> Bool)
+  , holds n (okExpress :: (Int,Int,Int,Int,Int,Int) -> Bool)
+  , holds n (okExpress :: (Int,Int,Int,Int,Int,Int,Int) -> Bool)
+#if __GLASGOW_HASKELL__ < 710
+-- No 8-tuples for you:
+-- On GHC 7.8, 8-tuples are not Typeable instances.  We could add a standalone
+-- deriving clause, but that may cause trouble if some other library does the
+-- same.  User should declare Generalizable 8-tuples manually when using GHC <=
+-- 7.8.
+#else
+  , holds n (okExpress :: (Int,Int,Int,Int,Int,Int,Int,Int) -> Bool)
+  , holds n (okExpress :: (Int,Int,Int,Int,Int,Int,Int,Int,Int) -> Bool)
+  , holds n (okExpress :: (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int) -> Bool)
+  , holds n (okExpress :: (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int) -> Bool)
+  , holds n (okExpress :: (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int) -> Bool)
+#endif
+
+  -- Transforming lists into Exprs
+  , expr ([]::[Int]) == value "[]" ([]::[Int])
+  , expr ([0::Int])  == zero -:- nil
+  , expr ([0::Int,1])  == zero -:- one -:- nil
+  , holds n $ \xs -> expr xs == foldr (-:-) nil (map expr (xs :: [Int]))
+  , holds n $ \ps -> expr ps == foldr (-:-) nilBool (map expr (ps :: [Bool]))
+
+  -- Transforming Maybes into Exprs
+  , expr (Nothing    :: Maybe Int)   ==  nothing
+  , expr (Nothing    :: Maybe Bool)  ==  nothingBool
+  , expr (Just 1     :: Maybe Int)   ==  just one
+  , expr (Just False :: Maybe Bool)  ==  just false
+  , holds n $ \x -> expr (Just x) == just (expr (x :: Int))
+  , holds n $ \p -> expr (Just p) == just (expr (p :: Bool))
+
+  -- Transforming Tuples into Exprs
+  , expr ((0,False) :: (Int,Bool))  ==  pair zero false
+  , expr ((True,1)  :: (Bool,Int))  ==  pair true one
+
+  -- Showing Exprs
+  , holds n $ \x -> show (expr x) == show (x :: ()) ++ " :: ()"
+  , holds n $ \x -> show (expr x) == show (x :: Int) ++ " :: Int"
+  , holds n $ \p -> show (expr p) == show (p :: Bool) ++ " :: Bool"
+  , holds n $ \x -> show (expr x) == show (x :: ((),Maybe Int,[Bool]))
+                                        ++ " :: ((),(Maybe Int),[Bool])"
+  ]
+
+-- this is true only for some types
+exprIsVal :: (Listable a, Express a, Show a) => a -> Bool
+exprIsVal x  =  expr x == val x
+
+-- this should be true for all types
+exprIsValUnderEvaluate :: (Listable a, Express a, Show a, Eq a) => a -> Bool
+exprIsValUnderEvaluate x  =  evaluate (expr x) == (evaluate (val x) -: mayb x)
+
+-- this should be true for most types
+showExprIsShow :: (Listable a, Express a, Show a) => a -> Bool
+showExprIsShow x  =  showExpr (expr x) == show x
+
+okExpress :: (Listable a, Express a, Show a, Eq a) => a -> Bool
+okExpress  =  exprIsValUnderEvaluate &&& showExprIsShow
diff --git a/test/fixtures.hs b/test/fixtures.hs
new file mode 100644
--- /dev/null
+++ b/test/fixtures.hs
@@ -0,0 +1,173 @@
+-- Copyright (c) 2017-2018 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+import Test.LeanCheck.Error (errorToNothing)
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  -- Bool --
+
+  , show b_ == "_ :: Bool"
+  , show pp == "p :: Bool"
+  , show qq == "q :: Bool"
+  , show false == "False :: Bool"
+  , show true == "True :: Bool"
+  , show notE == "not :: Bool -> Bool"
+  , show andE == "(&&) :: Bool -> Bool -> Bool"
+  , show orE  == "(||) :: Bool -> Bool -> Bool"
+  , show (not' false) == "not False :: Bool"
+  , show (false -&&- true) == "False && True :: Bool"
+  , show (pp -||- false) == "p || False :: Bool"
+  , show (qq -&&- true) == "q && True :: Bool"
+
+  , evl false == False
+  , evl true  == True
+  , holds n $ evl notE === not
+  , holds n $ evl andE ==== (&&)
+  , holds n $ evl orE  ==== (||)
+  , evl (not' false) == True
+  , evl (false -&&- true) == False
+  , evl (false -||- true) == True
+  , holds n $ \p -> evl (not' (val p)) == not p
+  , holds n $ \p q -> evl (val p -&&- val q) == (p && q)
+  , holds n $ \p q -> evl (val p -||- val q) == (p || q)
+
+  -- Int --
+
+  , show i_ == "_ :: Int"
+  , show xx == "x :: Int"
+  , show yy == "y :: Int"
+  , show zz == "z :: Int"
+  , show xx' == "x' :: Int"
+  , show zero == "0 :: Int"
+  , show two == "2 :: Int"
+  , show minusOne == "-1 :: Int"
+
+  , show plus == "(+) :: Int -> Int -> Int"
+  , show times == "(*) :: Int -> Int -> Int"
+  , show (plus :$ one) == "(1 +) :: Int -> Int"
+  , show (times :$ two) == "(2 *) :: Int -> Int"
+
+  , show (one -+- one)             == "1 + 1 :: Int"
+  , show (minusOne -+- minusOne)   == "(-1) + (-1) :: Int"
+  , show (minusTwo -*- two)        == "(-2) * 2 :: Int"
+  , show (two -*- minusTwo)        == "2 * (-2) :: Int"
+  , show (xx -+- (yy -+- zz))      == "x + (y + z) :: Int"
+
+  , evl zero == (0 :: Int)
+  , evl one  == (1 :: Int)
+  , evl two   == (2 :: Int)
+  , evl three == (3 :: Int)
+  , evl minusOne == (-1 :: Int)
+  , evl minusTwo == (-2 :: Int)
+
+  , evl (one -+- one) == (2 :: Int)
+  , evl (two -*- three) == (6 :: Int)
+  , holds n $ \x y -> evl (val x -+- val y) == (x + y :: Int)
+  , holds n $ \x y -> evl (val x -*- val y) == (x * y :: Int)
+  , holds n $ \(IntE ex) (IntE ey) -> isGround ex && isGround ey ==> evl (ex -+- ey) =$ errorToNothing $= evl ex + (evl ey :: Int)
+  , holds n $ \(IntE ex) (IntE ey) -> isGround ex && isGround ey ==> evl (ex -*- ey) =$ errorToNothing $= evl ex * (evl ey :: Int)
+
+  , show xxs  == "xs :: [Int]"
+  , show yys  == "ys :: [Int]"
+  , show nil == "[] :: [Int]"
+  , show (unit one) == "[1] :: [Int]"
+
+  -- Int -> Int --
+
+  , show ffE == "f :: Int -> Int"
+  , show ggE == "g :: Int -> Int"
+  , show (ff xx) == "f x :: Int"
+  , show (gg yy) == "g y :: Int"
+  , show (ff one) == "f 1 :: Int"
+  , show (gg minusTwo) == "g (-2) :: Int"
+
+  , show idE == "id :: Int -> Int"
+  , show negateE == "negate :: Int -> Int"
+  , show absE == "abs :: Int -> Int"
+  , show (id' yy) == "id y :: Int"
+  , show (id' one) == "id 1 :: Int"
+  , show (id' true) == "id True :: Bool"
+  , show (negate' yy) == "negate y :: Int"
+  , show (abs' xx') == "abs x' :: Int"
+
+  , evl (id' one) == (1 :: Int)
+  , evl (id' true) == (True :: Bool)
+  , evl (negate' one) == (-1 :: Int)
+  , evl (abs' minusTwo) == (2 :: Int)
+
+  , evl nil == ([] :: [Int])
+  , evl (unit one) == [1 :: Int]
+
+  -- Int -> Int -> Int
+  , show (var "?" (undefined :: Int->Int->Int)) == "(?) :: Int -> Int -> Int"
+  , show (var "?" (undefined :: Int->Int->Int) :$ xx) == "(x ?) :: Int -> Int"
+  , show (xx -?- yy) == "x ? y :: Int"
+  , show (pp -?- qq) == "p ? q :: Bool"
+  , show (xxs -?- yys) == "xs ? ys :: [Int]"
+  , show (bee -?- cee) == "'b' ? 'c' :: Char"
+
+  -- Char --
+
+  , show c_ == "_ :: Char"
+  , show bee == "'b' :: Char"
+  , show cee == "'c' :: Char"
+  , show dee == "'d' :: Char"
+
+  , evl bee == 'b'
+  , evl cee == 'c'
+  , evl dee == 'd'
+
+
+  -- [a] & [Int] --
+
+  , show xxs == "xs :: [Int]"
+  , show yys == "ys :: [Int]"
+  , show nil == "[] :: [Int]"
+  , show cons == "(:) :: Int -> [Int] -> [Int]"
+  , show (unit zero) == "[0] :: [Int]"
+  , show (unit false) == "[False] :: [Bool]"
+  , show (zero -:- one -:- unit two) == "[0,1,2] :: [Int]"
+  , show (zero -:- one -:- two -:- nil) == "[0,1,2] :: [Int]"
+
+  , evl nil == ([] :: [Int])
+  , holds n $ \x -> evl (unit (val x)) == [x :: Int]
+  , holds n $ \c -> evl (unit (val c)) == [c :: Char]
+  , holds n $ \p -> evl (unit (val p)) == [p :: Bool]
+  , holds n $ \x xs -> evl (val x -:- val xs) == (x:xs :: [Int])
+  , holds n $ \p ps -> evl (val p -:- val ps) == (p:ps :: [Int])
+  , holds n $ \c cs -> evl (val c -:- val cs) == (c:cs :: [Int])
+
+  , show (nil -++- nil) == "[] ++ [] :: [Int]"
+  , show ((zero -:- one -:- nil) -++- (two -:- three -:- nil)) == "[0,1] ++ [2,3] :: [Int]"
+
+  , show (head' $ unit one) == "head [1] :: Int"
+  , show (tail' $ unit one) == "tail [1] :: [Int]"
+  , show (head' $ unit bee) == "head \"b\" :: Char"
+  , show (tail' $ unit bee) == "tail \"b\" :: [Char]"
+  , show (head' $ zero -:- unit two) == "head [0,2] :: Int"
+  , show (tail' $ zero -:- unit two) == "tail [0,2] :: [Int]"
+
+  , evl (unit one) == [1 :: Int]
+  , evl (head' $ unit one) == (1 :: Int)
+  , evl (tail' $ zero -:- unit two) == ([2] :: [Int])
+
+  -- String --
+
+  , show emptyString == "\"\" :: [Char]"
+  , show (unit bee) == "\"b\" :: [Char]"
+  , show (bee -:- unit cee) == "\"bc\" :: [Char]"
+  , show (emptyString -++- emptyString) == "\"\" ++ \"\" :: [Char]"
+  , show ((bee -:- unit cee) -++- unit dee) == "\"bc\" ++ \"d\" :: [Char]"
+
+  , evl emptyString == ""
+  , evl (unit bee) == "b"
+  , evl (bee -:- unit cee) == "bc"
+  , evl (bee -:- cee -:- unit dee) == "bcd"
+  ]
diff --git a/test/fold.hs b/test/fold.hs
new file mode 100644
--- /dev/null
+++ b/test/fold.hs
@@ -0,0 +1,34 @@
+-- Copyright (c) 2019 Rudy Matela.  -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , holds n $ \e1 e2 -> (e1,e2) == unfoldPair (foldPair (e1,e2))
+  , holds n $ \e123 -> e123 == unfoldTrio (foldTrio e123)
+
+  -- the result of foldPair and foldTrio is always ill-typed
+  , holds n $ \e1 e2 -> isIllTyped $ foldPair (e1,e2)
+  , holds n $ \e123 -> isIllTyped $ foldTrio e123
+
+  -- (==) works even though foldPair returns an ill-typed expression
+  , holds n $ \e1 e2 -> foldPair (e1,e2) == foldPair (e1,e2)
+  , fails n $ \e1 e2 -> foldPair (e1,e2) == foldPair (e2,e1)
+
+  , show (foldPair (xx,yy)) == "(x,y) :: ill-typed # ExprPair $ Int #"
+  , show (foldTrio (xx,yy,zz)) == "(x,y,z) :: ill-typed # ExprTrio $ Int #"
+
+  , unfoldApp (abs' xx)          == [absE, xx]
+  , unfoldApp (abs' (xx -+- yy)) == [absE, xx -+- yy]
+  , unfoldApp (xx -+- abs' xx)   == [plus, xx, abs' xx]
+  , unfoldApp one                == [one]
+  , unfoldApp false              == [false]
+
+  , holds n $ \e -> foldApp (unfoldApp e) == e
+
+  , holds n $ \es -> es == unfold (fold es)
+  ]
diff --git a/test/hole.hs b/test/hole.hs
new file mode 100644
--- /dev/null
+++ b/test/hole.hs
@@ -0,0 +1,30 @@
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , isHole (hole (undefined :: Int))  == True
+  , isHole (hole (undefined :: Bool)) == True
+  , isHole (hole (undefined :: Char)) == True
+  , holds n $ \x -> isHole (val (x :: Int))  == False
+  , holds n $ \p -> isHole (val (p :: Bool)) == False
+  , holds n $ \c -> isHole (val (c :: Char)) == False
+
+  , holds n $ \e -> isHole e ==> isVar e
+  , holds n $ \e1 e2 -> isHole (e1 :$ e2) == False
+
+  , holds n $ \e -> holes e `isSubsequenceOf` vars e
+  , holds n $ \e -> nubHoles e `isSubsetOf` holes e
+  , holds n $ \e -> nubHoles e `isSubsequenceOf` nubVars e
+
+  , [xx, yy, zz, xx'] `isPrefixOf` listVars "x" (undefined :: Int)
+  , [pp, qq, rr, pp'] `isPrefixOf` listVars "p" (undefined :: Bool)
+  , [xx, yy, zz, xx'] `isPrefixOf` listVarsAsTypeOf "x" zero
+  , [pp, qq, rr, pp'] `isPrefixOf` listVarsAsTypeOf "p" false
+  ]
diff --git a/test/instances.hs b/test/instances.hs
new file mode 100644
--- /dev/null
+++ b/test/instances.hs
@@ -0,0 +1,51 @@
+-- Copyright (c) 2017-2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+{-# LANGUAGE NoMonomorphismRestriction #-} -- ACK!
+import Test
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , eval undefined (eqFor (undefined :: Int) :$ one :$ one) == True
+  , eval undefined (eqFor (undefined :: Int) :$ one :$ two) == False
+
+  , eval undefined (lessEqFor (undefined :: Int) :$ one :$ two) == True
+  , eval undefined (lessEqFor (undefined :: Int) :$ one :$ one) == True
+  , eval undefined (lessEqFor (undefined :: Int) :$ two :$ one) == False
+
+  , eval undefined (lessFor (undefined :: Int) :$ one :$ two) == True
+  , eval undefined (lessFor (undefined :: Int) :$ one :$ one) == False
+  , eval undefined (lessFor (undefined :: Int) :$ two :$ one) == False
+
+-- for the time being, compare has been removed from reifyOrd's result
+--, eval undefined (compareFor (undefined :: Int) :$ one :$ two) == LT
+--, eval undefined (compareFor (undefined :: Int) :$ one :$ one) == EQ
+--, eval undefined (compareFor (undefined :: Int) :$ two :$ one) == GT
+
+  , eval undefined (nameFor (undefined :: Int)  :$ xx) == "x"
+  , eval undefined (nameFor (undefined :: Int)  :$ yy) == "x"
+  , eval undefined (nameFor (undefined :: Bool) :$ pp) == "p"
+  , eval undefined (nameFor (undefined :: Bool) :$ qq) == "p"
+
+  , length (validApps functions one) == 5
+  ]
+  where
+  eqFor = head . reifyEq
+  lessEqFor = head . reifyOrd
+  lessFor = head . tail . reifyOrd
+--compareFor = head . reifyOrd
+  nameFor = head . reifyName
+
+functions :: [Expr]
+functions  =  concat
+  [ reifyEq (undefined :: Int)
+  , reifyEq (undefined :: Bool)
+  , reifyOrd (undefined :: Int)
+  , reifyOrd (undefined :: Bool)
+  , reifyName (undefined :: Int)
+  , reifyName (undefined :: Bool)
+  ]
diff --git a/test/listable.hs b/test/listable.hs
new file mode 100644
--- /dev/null
+++ b/test/listable.hs
@@ -0,0 +1,79 @@
+-- Copyright (c) 2017-2018 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  -- Listable Expr only produces well-typed expressions
+  , holds n $ isJust . toDynamic
+  , holds n $ isJust . mtyp
+
+  -- Listable Ill only produces ill-typed expressions
+  , holds n $ isNothing . toDynamic . unIll
+  , holds n $ isNothing . mtyp      . unIll
+
+  -- Listable TypeE produces expressions of the right type (evaluation)
+  , holds n $ isJust . evaluateInt      . unIntE
+  , holds n $ isJust . evaluateBool     . unBoolE
+  , holds n $ isJust . evaluateInts     . unIntsE
+  , holds n $ isJust . evaluateIntToInt . unIntToIntE
+  , holds n $ isJust . evaluateChar     . unCharE
+  , holds n $ \(IntToIntE ff) (IntE xx) -> isJust . evaluateInt $ ff :$ xx
+  , holds n $ \(IntToIntToIntE ff) (IntE xx) (IntE yy) -> isJust . evaluateInt $ ff :$ xx :$ yy
+
+  -- Listable TypeE produces expressions of the right type (typ)
+  , holds n $ \(SameTypeE e1 e2) -> typ e1 == typ e2
+  , holds n $ \(SameTypedPairsE ees) -> all (\(e1,e2) -> typ e1 == typ e2) ees
+  , holds n $ \(IntE  e) -> typ e == typ i_
+  , holds n $ \(BoolE e) -> typ e == typ b_
+  , holds n $ \(CharE e) -> typ e == typ c_
+  , holds n $ \(IntsE e) -> typ e == typ is_
+
+  -- Listable TypeE does not produce expressions of the wrong type
+  , holds n $ isNothing . evaluateInt      . unBoolE
+  , holds n $ isNothing . evaluateBool     . unIntE
+  , holds n $ isNothing . evaluateInts     . unIntE
+  , holds n $ isNothing . evaluateIntToInt . unIntE
+  , holds n $ isNothing . evaluateChar     . unIntE
+
+  -- Listable TypeE0 only returns terminal constants
+  , holds n $ isConst . unE0
+  , holds n $ isConst . unIntE0
+  , holds n $ isConst . unBoolE0
+  , holds n $ isConst . unIntsE0
+  , holds n $ isConst . unCharE0
+
+  -- Listable TypeEV only returns variables
+  , holds n $ isVar . unEV
+  , holds n $ isVar . unIntEV
+  , holds n $ isVar . unBoolEV
+  , holds n $ isVar . unIntsEV
+  , holds n $ isVar . unCharEV
+
+  -- counter-examples are of the right type
+  , (counterExample n $ \(IntE xx) -> False) == Just ["_ :: Int"]
+
+  , isNub (take (n`div`10) list :: [Expr])
+  , isNub (take (n`div`10) $ map unSameTypeE list)
+  , isNub (take (n`div`10) $ map unIntE list)
+  ]
+
+evaluateInt :: Expr -> Maybe Int
+evaluateInt = evaluate
+
+evaluateBool :: Expr -> Maybe Bool
+evaluateBool = evaluate
+
+evaluateInts :: Expr -> Maybe [Int]
+evaluateInts = evaluate
+
+evaluateIntToInt :: Expr -> Maybe (Int -> Int)
+evaluateIntToInt = evaluate
+
+evaluateChar :: Expr -> Maybe Char
+evaluateChar = evaluate
diff --git a/test/main.hs b/test/main.hs
new file mode 100644
--- /dev/null
+++ b/test/main.hs
@@ -0,0 +1,18 @@
+-- Copyright (c) 2019 Rudy Matela.  -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+import Data.Express.Utils.List
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , evl (val (10 :: Int)) == (10 :: Int)
+  , evl one == (1 :: Int)
+  , holds n $ \x y -> evl (value "+" ((+) :: Int -> Int -> Int) :$ val x :$ val y) == (x + y :: Int)
+  , values (xx -+- yy) == [plus, xx, yy]
+  , (xx -+- yy) // [(yy,yy -+- zz),(xx,xx -+- yy)] == (xx -+- yy) -+- (yy -+- zz)
+  ]
diff --git a/test/map.hs b/test/map.hs
new file mode 100644
--- /dev/null
+++ b/test/map.hs
@@ -0,0 +1,101 @@
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+import Test.LeanCheck.Function
+
+import Data.Express.Utils.List (isNub)
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  -- the order should not matter for //
+  , holds n $ \e ee1 ee2 -> fst ee1 /= fst ee2 ==> e // [ee1, ee2] == e // [ee2, ee1]
+  , holds n $ \e ees -> isNub (map fst ees) ==> e // ees == e // reverse ees
+
+  -- the order should not matter for //-
+  , holds n $ \e ee1 ee2 -> fst ee1 /= fst ee2 ==> e //- [ee1, ee2] == e //- [ee2, ee1]
+  , holds n $ \e ees -> isNub (map fst ees) ==> e //- ees == e //- reverse ees
+  , holds n $ \e ees' -> let ees = map (mapFst unEV) ees' in
+                         isNub (map fst ees) ==> e //- ees == e //- reverse ees
+
+  -- //- and // are essentially different
+  , exists n $ \e es ->  e // es  /=  e //- es
+
+  -- equivalences between // and //-
+  , holds n $ \e ees -> all (isValue . fst) ees ==> e // ees == e //- ees
+  , holds n $ \e ees -> e // filter (isValue . fst) ees == e //- ees
+
+  -- //- ignores replacements of non-variable values
+  , holds n $ \e ees -> e //- filter (not . isValue . fst) ees == e
+
+  -- (in)equivalences between maps
+  , exists n $ \f e -> mapValues f e /= (mapVars f . mapConsts f) e
+  , exists n $ \f e -> mapValues f e /= (mapConsts f . mapVars f) e
+  , exists n $ \f e -> (mapVars f . mapConsts f) e /= (mapConsts f . mapVars f) e
+  , exists n $ \f e -> (mapConsts f . mapVars f) e /= (mapVars f . mapConsts f) e
+  -- the above should fail because of the following
+  , let f _ = id' i_ in mapValues f zero == id' i_
+                     && mapVars   f zero == zero
+                     && mapConsts f zero == id' i_
+                     && mapVars f (mapConsts f zero) == id' (id' i_)
+                     && mapConsts f (mapVars f zero) == id' i_
+
+  -- the following do not hold in general:
+  , exists n $ \f e -> (mapValues f . mapValues f) e /= mapValues f e
+  , exists n $ \f e -> (mapVars   f . mapVars   f) e /= mapVars   f e
+  , exists n $ \f e -> (mapConsts f . mapConsts f) e /= mapConsts f e
+
+  -- what actually holds is this:
+  , holds n $ \f e -> (mapValues f . mapValues f) e == mapValues (mapValues f . f) e
+  , holds n $ \f e -> (mapVars   f . mapVars   f) e == mapVars   (mapVars   f . f) e
+  , holds n $ \f e -> (mapConsts f . mapConsts f) e == mapConsts (mapConsts f . f) e
+
+  -- the following do not hold in general:
+  , exists n $ \f e -> values (mapValues f e) /= map f (values e)
+  , exists n $ \f e -> vars   (mapVars   f e) /= map f (vars   e)
+  , exists n $ \f e -> consts (mapConsts f e) /= map f (consts e)
+
+  -- what actually holds is this:
+  , holds n $ \f e -> values (mapValues f e) == concatMap (values . f) (values e)
+  , holds n $ \f e -> vars   (mapVars   f e) == concatMap (vars   . f) (vars   e)
+  , holds n $ \f e -> consts (mapConsts f e) == concatMap (consts . f) (consts e)
+
+  -- tests of // and //-
+  , ((xx -*- yy) -+- (yy -*- zz)) // [(xx,ii),(yy,jj),(zz,kk)] ==
+    (ii -*- jj) -+- (jj -*- kk)
+
+  , ((xx -*- yy) -+- (yy -*- zz)) //- [(xx,ii),(yy,jj),(zz,kk)] ==
+    (ii -*- jj) -+- (jj -*- kk)
+
+  , ((xx -*- yy) -+- (yy -*- zz)) // [(xx -*- yy,ii),(yy -*- zz,jj)] ==
+    ii -+- jj
+
+  , ((xx -*- yy) -+- (yy -*- zz)) //- [(xx -*- yy,ii),(yy -*- zz,jj)] ==
+    (xx -*- yy) -+- (yy -*- zz)
+
+
+  , ((xx -+- yy) -+- (yy -+- zz)) // [(yy,yy -+- zz)]
+    == (xx -+- (yy -+- zz)) -+- ((yy -+- zz) -+- zz)
+
+  , ((xx -+- yy) -+- (yy -+- zz)) //- [(yy,yy -+- zz)]
+    == (xx -+- (yy -+- zz)) -+- ((yy -+- zz) -+- zz)
+
+  , (xx -+- yy) // [(yy,yy -+- zz),(xx,xx -+- yy)]
+    == (xx -+- yy) -+- (yy -+- zz)
+
+  , (xx -+- yy) //- [(yy,yy -+- zz),(xx,xx -+- yy)]
+    == (xx -+- yy) -+- (yy -+- zz)
+
+  , holds n $ \e -> renameVarsBy id e == e
+  , holds n $ \c e -> (renameVarsBy tail . renameVarsBy (c:)) e == e
+  , renameVarsBy (++ "1") (xx -+- yy) == (var "x1" int -+- var "y1" int)
+  , renameVarsBy (\(c:cs) -> succ c:cs) ((xx -+- yy) -+- ord' cc)
+                                 == ((yy -+- zz) -+- ord' dd)
+  ]
+
+mapFst :: (a -> c) -> (a,b) -> (c,b)
+mapFst f (x,y) = (f x, y)
diff --git a/test/match.hs b/test/match.hs
new file mode 100644
--- /dev/null
+++ b/test/match.hs
@@ -0,0 +1,57 @@
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , holds n $ \(IntE e)            -> e `isInstanceOf` xx
+  , holds n $ \(IntE e)            -> abs' e `isInstanceOf` abs' xx
+  , holds n $ \(IntE e)            -> (e -+- e) `isInstanceOf` (xx -+- xx)
+  , holds n $ \(IntE e1) (IntE e2) -> (e1 -+- e2) `isInstanceOf` (xx -+- yy)
+  , holds n $ \(IntE e1) (IntE e2) -> e1 /= e2 ==> not ((e1 -+- e2) `isInstanceOf` (xx -+- xx))
+  , holds n $ \e                   -> e /= zero ==> not (e `isInstanceOf` zero)
+
+  ,       (zero -+- one)       `isInstanceOf` (xx -+- yy)
+  ,       (zero -+- zero)      `isInstanceOf` (xx -+- yy)
+  ,       (yy -+- xx)          `isInstanceOf` (xx -+- yy)
+  ,       (zero -+- zero)      `isInstanceOf` (xx -+- xx)
+  , not $ (zero -+- one)       `isInstanceOf` (xx -+- xx)
+  ,       zero                 `isInstanceOf`          xx
+  , not $ xx                   `isInstanceOf`        zero
+  ,       (xx -+- (yy -+- xx)) `isInstanceOf` (xx -+- yy)
+  ,       (xx -+- (xx -+- xx)) `isInstanceOf` (xx -+- yy)
+  , not $ (xx -+- (xx -+- xx)) `isInstanceOf` (xx -+- xx)
+
+  , holds n $ \(IntE e1) (IntE e2) -> match (e1 -+- e2) (xx -+- yy) == Just [(yy,e2),(xx,e1)]
+  , holds n $ \(IntE e)            -> match (e -+- e)   (xx -+- xx) == Just [(xx,e)]
+  , holds n $ \(IntE e1) (IntE e2) -> e1 /= e2 ==> match (e1 -+- e2) (xx -+- xx) == Nothing
+  , holds n $ \(IntE e1) (IntE e2) (IntE e3) -> e2 /= e3
+                ==> match ((e1 -+- e1) -+- (e2 -+- e3)) (xx -+- (yy -+- yy)) == Nothing
+  , holds n $ \(IntE e1) (IntE e2) -> matchWith [(xx,e1)] (e1 -+- e2) (xx -+- yy) == Just [(yy,e2),(xx,e1)]
+  , holds n $ \(IntE e1) (IntE e2) -> e1 /= e2 ==> matchWith [(xx,e2)] (e1 -+- e2) (xx -+- yy) == Nothing
+  , holds n $ \e1 e2 -> e1 `match` e2 == matchWith [] e1 e2
+  , holds n $ \(SameTypeE e1 e2) (SameTypeE e3 e4) ->
+                not (isFunTy $ typ e1) && not (isFunTy $ typ e3)
+                  ==>
+                (e1 -==- e2) `match` (e3 -==- e4) == foldPair (e1,e2) `match` foldPair (e3,e4)
+
+  -- tests for isSubexprOf --
+  , holds n $ \e1 e2 -> e1 `isSubexprOf` e2 == (e1 `elem` subexprs e2)
+  , holds n $ \e -> e `isSubexprOf` e
+  , (xx -+- yy) `isSubexprOf` (zz -+- (xx -+- yy)) == True
+  , (xx -+- yy) `isSubexprOf` abs' (yy -+- xx) == False
+  , xx `isSubexprOf` yy == False
+  , xx `isSubexprOf` xx == True
+
+  -- tests of hasInstanceOf --
+  , holds n $ \e1 e2 -> e1 `isInstanceOf` e2 ==> e1 `hasInstanceOf` e2
+  , holds n $ \ef ex e -> ((ef :$ ex) `hasInstanceOf` e)
+                       == ((ef :$ ex) `isInstanceOf` e || ef `hasInstanceOf` e
+                                                       || ex `hasInstanceOf` e)
+  , holds n $ \e1 e2 -> e1 `hasInstanceOf` e2 == any (`isInstanceOf` e2) (subexprs e1)
+  ]
diff --git a/test/model/bench/compare.out b/test/model/bench/compare.out
new file mode 100644
--- /dev/null
+++ b/test/model/bench/compare.out
@@ -0,0 +1,1001 @@
+_ :: Int `compare` _ :: Int  =  EQ
+_ :: Int `compare` x :: Int  =  LT
+_ :: Int `compare` 0 :: Int  =  LT
+_ :: Int `compare` id _ :: Int  =  LT
+_ :: Int `compare` _ :: Bool  =  GT
+_ :: Int `compare` _ :: Char  =  GT
+_ :: Int `compare` _ :: [Int]  =  LT
+x :: Int `compare` _ :: Int  =  GT
+0 :: Int `compare` _ :: Int  =  GT
+id _ :: Int `compare` _ :: Int  =  GT
+_ :: Bool `compare` _ :: Int  =  LT
+_ :: Char `compare` _ :: Int  =  LT
+_ :: [Int] `compare` _ :: Int  =  GT
+_ :: Int `compare` y :: Int  =  LT
+_ :: Int `compare` 1 :: Int  =  LT
+_ :: Int `compare` id x :: Int  =  LT
+_ :: Int `compare` id 0 :: Int  =  LT
+_ :: Int `compare` id (id _) :: Int  =  LT
+_ :: Int `compare` negate _ :: Int  =  LT
+_ :: Int `compare` abs _ :: Int  =  LT
+_ :: Int `compare` _ + _ :: Int  =  LT
+_ :: Int `compare` head _ :: Int  =  LT
+_ :: Int `compare` ord _ :: Int  =  LT
+_ :: Int `compare` p :: Bool  =  GT
+_ :: Int `compare` False :: Bool  =  LT
+_ :: Int `compare` True :: Bool  =  LT
+_ :: Int `compare` not _ :: Bool  =  LT
+_ :: Int `compare` c :: Char  =  GT
+_ :: Int `compare` 'a' :: Char  =  LT
+_ :: Int `compare` xs :: [Int]  =  LT
+_ :: Int `compare` [] :: [Int]  =  LT
+_ :: Int `compare` _:_ :: [Int]  =  LT
+_ :: Int `compare` id :: Int -> Int  =  LT
+_ :: Int `compare` (+) :: Int -> Int -> Int  =  LT
+x :: Int `compare` x :: Int  =  EQ
+x :: Int `compare` 0 :: Int  =  LT
+x :: Int `compare` id _ :: Int  =  LT
+x :: Int `compare` _ :: Bool  =  GT
+x :: Int `compare` _ :: Char  =  GT
+x :: Int `compare` _ :: [Int]  =  LT
+0 :: Int `compare` x :: Int  =  GT
+0 :: Int `compare` 0 :: Int  =  EQ
+0 :: Int `compare` id _ :: Int  =  LT
+0 :: Int `compare` _ :: Bool  =  GT
+0 :: Int `compare` _ :: Char  =  GT
+0 :: Int `compare` _ :: [Int]  =  GT
+id _ :: Int `compare` x :: Int  =  GT
+id _ :: Int `compare` 0 :: Int  =  GT
+id _ :: Int `compare` id _ :: Int  =  EQ
+id _ :: Int `compare` _ :: Bool  =  GT
+id _ :: Int `compare` _ :: Char  =  GT
+id _ :: Int `compare` _ :: [Int]  =  GT
+_ :: Bool `compare` x :: Int  =  LT
+_ :: Bool `compare` 0 :: Int  =  LT
+_ :: Bool `compare` id _ :: Int  =  LT
+_ :: Bool `compare` _ :: Bool  =  EQ
+_ :: Bool `compare` _ :: Char  =  LT
+_ :: Bool `compare` _ :: [Int]  =  LT
+_ :: Char `compare` x :: Int  =  LT
+_ :: Char `compare` 0 :: Int  =  LT
+_ :: Char `compare` id _ :: Int  =  LT
+_ :: Char `compare` _ :: Bool  =  GT
+_ :: Char `compare` _ :: Char  =  EQ
+_ :: Char `compare` _ :: [Int]  =  LT
+_ :: [Int] `compare` x :: Int  =  GT
+_ :: [Int] `compare` 0 :: Int  =  LT
+_ :: [Int] `compare` id _ :: Int  =  LT
+_ :: [Int] `compare` _ :: Bool  =  GT
+_ :: [Int] `compare` _ :: Char  =  GT
+_ :: [Int] `compare` _ :: [Int]  =  EQ
+y :: Int `compare` _ :: Int  =  GT
+1 :: Int `compare` _ :: Int  =  GT
+id x :: Int `compare` _ :: Int  =  GT
+id 0 :: Int `compare` _ :: Int  =  GT
+id (id _) :: Int `compare` _ :: Int  =  GT
+negate _ :: Int `compare` _ :: Int  =  GT
+abs _ :: Int `compare` _ :: Int  =  GT
+_ + _ :: Int `compare` _ :: Int  =  GT
+head _ :: Int `compare` _ :: Int  =  GT
+ord _ :: Int `compare` _ :: Int  =  GT
+p :: Bool `compare` _ :: Int  =  LT
+False :: Bool `compare` _ :: Int  =  GT
+True :: Bool `compare` _ :: Int  =  GT
+not _ :: Bool `compare` _ :: Int  =  GT
+c :: Char `compare` _ :: Int  =  LT
+'a' :: Char `compare` _ :: Int  =  GT
+xs :: [Int] `compare` _ :: Int  =  GT
+[] :: [Int] `compare` _ :: Int  =  GT
+_:_ :: [Int] `compare` _ :: Int  =  GT
+id :: Int -> Int `compare` _ :: Int  =  GT
+(+) :: Int -> Int -> Int `compare` _ :: Int  =  GT
+_ :: Int `compare` z :: Int  =  LT
+_ :: Int `compare` -1 :: Int  =  LT
+_ :: Int `compare` id y :: Int  =  LT
+_ :: Int `compare` id 1 :: Int  =  LT
+_ :: Int `compare` id (id x) :: Int  =  LT
+_ :: Int `compare` id (id 0) :: Int  =  LT
+_ :: Int `compare` id (id (id _)) :: Int  =  LT
+_ :: Int `compare` id (negate _) :: Int  =  LT
+_ :: Int `compare` id (abs _) :: Int  =  LT
+_ :: Int `compare` id (_ + _) :: Int  =  LT
+_ :: Int `compare` id (head _) :: Int  =  LT
+_ :: Int `compare` id (ord _) :: Int  =  LT
+_ :: Int `compare` negate x :: Int  =  LT
+_ :: Int `compare` negate 0 :: Int  =  LT
+_ :: Int `compare` negate (id _) :: Int  =  LT
+_ :: Int `compare` abs x :: Int  =  LT
+_ :: Int `compare` abs 0 :: Int  =  LT
+_ :: Int `compare` abs (id _) :: Int  =  LT
+_ :: Int `compare` _ + x :: Int  =  LT
+_ :: Int `compare` _ + 0 :: Int  =  LT
+_ :: Int `compare` _ + id _ :: Int  =  LT
+_ :: Int `compare` x + _ :: Int  =  LT
+_ :: Int `compare` 0 + _ :: Int  =  LT
+_ :: Int `compare` id _ + _ :: Int  =  LT
+_ :: Int `compare` _ * _ :: Int  =  LT
+_ :: Int `compare` f _ :: Int  =  LT
+_ :: Int `compare` head xs :: Int  =  LT
+_ :: Int `compare` head [] :: Int  =  LT
+_ :: Int `compare` head (_:_) :: Int  =  LT
+_ :: Int `compare` ord c :: Int  =  LT
+_ :: Int `compare` ord 'a' :: Int  =  LT
+_ :: Int `compare` q :: Bool  =  GT
+_ :: Int `compare` not p :: Bool  =  LT
+_ :: Int `compare` not False :: Bool  =  LT
+_ :: Int `compare` not True :: Bool  =  LT
+_ :: Int `compare` not (not _) :: Bool  =  LT
+_ :: Int `compare` _ || _ :: Bool  =  LT
+_ :: Int `compare` d :: Char  =  GT
+_ :: Int `compare` ' ' :: Char  =  LT
+_ :: Int `compare` ys :: [Int]  =  LT
+_ :: Int `compare` [0] :: [Int]  =  LT
+_ :: Int `compare` _:xs :: [Int]  =  LT
+_ :: Int `compare` [_] :: [Int]  =  LT
+_ :: Int `compare` _:_:_ :: [Int]  =  LT
+_ :: Int `compare` x:_ :: [Int]  =  LT
+_ :: Int `compare` 0:_ :: [Int]  =  LT
+_ :: Int `compare` id _:_ :: [Int]  =  LT
+_ :: Int `compare` tail _ :: [Int]  =  LT
+_ :: Int `compare` _ ++ _ :: [Int]  =  LT
+_ :: Int `compare` negate :: Int -> Int  =  LT
+_ :: Int `compare` abs :: Int -> Int  =  LT
+_ :: Int `compare` (_ +) :: Int -> Int  =  LT
+_ :: Int `compare` (*) :: Int -> Int -> Int  =  LT
+_ :: Int `compare` not :: Bool -> Bool  =  LT
+_ :: Int `compare` (||) :: Bool -> Bool -> Bool  =  LT
+x :: Int `compare` y :: Int  =  LT
+x :: Int `compare` 1 :: Int  =  LT
+x :: Int `compare` id x :: Int  =  LT
+x :: Int `compare` id 0 :: Int  =  LT
+x :: Int `compare` id (id _) :: Int  =  LT
+x :: Int `compare` negate _ :: Int  =  LT
+x :: Int `compare` abs _ :: Int  =  LT
+x :: Int `compare` _ + _ :: Int  =  LT
+x :: Int `compare` head _ :: Int  =  LT
+x :: Int `compare` ord _ :: Int  =  LT
+x :: Int `compare` p :: Bool  =  GT
+x :: Int `compare` False :: Bool  =  LT
+x :: Int `compare` True :: Bool  =  LT
+x :: Int `compare` not _ :: Bool  =  LT
+x :: Int `compare` c :: Char  =  GT
+x :: Int `compare` 'a' :: Char  =  LT
+x :: Int `compare` xs :: [Int]  =  LT
+x :: Int `compare` [] :: [Int]  =  LT
+x :: Int `compare` _:_ :: [Int]  =  LT
+x :: Int `compare` id :: Int -> Int  =  LT
+x :: Int `compare` (+) :: Int -> Int -> Int  =  LT
+0 :: Int `compare` y :: Int  =  GT
+0 :: Int `compare` 1 :: Int  =  LT
+0 :: Int `compare` id x :: Int  =  LT
+0 :: Int `compare` id 0 :: Int  =  LT
+0 :: Int `compare` id (id _) :: Int  =  LT
+0 :: Int `compare` negate _ :: Int  =  LT
+0 :: Int `compare` abs _ :: Int  =  LT
+0 :: Int `compare` _ + _ :: Int  =  LT
+0 :: Int `compare` head _ :: Int  =  LT
+0 :: Int `compare` ord _ :: Int  =  LT
+0 :: Int `compare` p :: Bool  =  GT
+0 :: Int `compare` False :: Bool  =  GT
+0 :: Int `compare` True :: Bool  =  GT
+0 :: Int `compare` not _ :: Bool  =  LT
+0 :: Int `compare` c :: Char  =  GT
+0 :: Int `compare` 'a' :: Char  =  GT
+0 :: Int `compare` xs :: [Int]  =  GT
+0 :: Int `compare` [] :: [Int]  =  LT
+0 :: Int `compare` _:_ :: [Int]  =  LT
+0 :: Int `compare` id :: Int -> Int  =  LT
+0 :: Int `compare` (+) :: Int -> Int -> Int  =  LT
+id _ :: Int `compare` y :: Int  =  GT
+id _ :: Int `compare` 1 :: Int  =  GT
+id _ :: Int `compare` id x :: Int  =  LT
+id _ :: Int `compare` id 0 :: Int  =  LT
+id _ :: Int `compare` id (id _) :: Int  =  LT
+id _ :: Int `compare` negate _ :: Int  =  LT
+id _ :: Int `compare` abs _ :: Int  =  GT
+id _ :: Int `compare` _ + _ :: Int  =  LT
+id _ :: Int `compare` head _ :: Int  =  LT
+id _ :: Int `compare` ord _ :: Int  =  GT
+id _ :: Int `compare` p :: Bool  =  GT
+id _ :: Int `compare` False :: Bool  =  GT
+id _ :: Int `compare` True :: Bool  =  GT
+id _ :: Int `compare` not _ :: Bool  =  GT
+id _ :: Int `compare` c :: Char  =  GT
+id _ :: Int `compare` 'a' :: Char  =  GT
+id _ :: Int `compare` xs :: [Int]  =  GT
+id _ :: Int `compare` [] :: [Int]  =  GT
+id _ :: Int `compare` _:_ :: [Int]  =  LT
+id _ :: Int `compare` id :: Int -> Int  =  GT
+id _ :: Int `compare` (+) :: Int -> Int -> Int  =  GT
+_ :: Bool `compare` y :: Int  =  LT
+_ :: Bool `compare` 1 :: Int  =  LT
+_ :: Bool `compare` id x :: Int  =  LT
+_ :: Bool `compare` id 0 :: Int  =  LT
+_ :: Bool `compare` id (id _) :: Int  =  LT
+_ :: Bool `compare` negate _ :: Int  =  LT
+_ :: Bool `compare` abs _ :: Int  =  LT
+_ :: Bool `compare` _ + _ :: Int  =  LT
+_ :: Bool `compare` head _ :: Int  =  LT
+_ :: Bool `compare` ord _ :: Int  =  LT
+_ :: Bool `compare` p :: Bool  =  LT
+_ :: Bool `compare` False :: Bool  =  LT
+_ :: Bool `compare` True :: Bool  =  LT
+_ :: Bool `compare` not _ :: Bool  =  LT
+_ :: Bool `compare` c :: Char  =  LT
+_ :: Bool `compare` 'a' :: Char  =  LT
+_ :: Bool `compare` xs :: [Int]  =  LT
+_ :: Bool `compare` [] :: [Int]  =  LT
+_ :: Bool `compare` _:_ :: [Int]  =  LT
+_ :: Bool `compare` id :: Int -> Int  =  LT
+_ :: Bool `compare` (+) :: Int -> Int -> Int  =  LT
+_ :: Char `compare` y :: Int  =  LT
+_ :: Char `compare` 1 :: Int  =  LT
+_ :: Char `compare` id x :: Int  =  LT
+_ :: Char `compare` id 0 :: Int  =  LT
+_ :: Char `compare` id (id _) :: Int  =  LT
+_ :: Char `compare` negate _ :: Int  =  LT
+_ :: Char `compare` abs _ :: Int  =  LT
+_ :: Char `compare` _ + _ :: Int  =  LT
+_ :: Char `compare` head _ :: Int  =  LT
+_ :: Char `compare` ord _ :: Int  =  LT
+_ :: Char `compare` p :: Bool  =  GT
+_ :: Char `compare` False :: Bool  =  LT
+_ :: Char `compare` True :: Bool  =  LT
+_ :: Char `compare` not _ :: Bool  =  LT
+_ :: Char `compare` c :: Char  =  LT
+_ :: Char `compare` 'a' :: Char  =  LT
+_ :: Char `compare` xs :: [Int]  =  LT
+_ :: Char `compare` [] :: [Int]  =  LT
+_ :: Char `compare` _:_ :: [Int]  =  LT
+_ :: Char `compare` id :: Int -> Int  =  LT
+_ :: Char `compare` (+) :: Int -> Int -> Int  =  LT
+_ :: [Int] `compare` y :: Int  =  GT
+_ :: [Int] `compare` 1 :: Int  =  LT
+_ :: [Int] `compare` id x :: Int  =  LT
+_ :: [Int] `compare` id 0 :: Int  =  LT
+_ :: [Int] `compare` id (id _) :: Int  =  LT
+_ :: [Int] `compare` negate _ :: Int  =  LT
+_ :: [Int] `compare` abs _ :: Int  =  LT
+_ :: [Int] `compare` _ + _ :: Int  =  LT
+_ :: [Int] `compare` head _ :: Int  =  LT
+_ :: [Int] `compare` ord _ :: Int  =  LT
+_ :: [Int] `compare` p :: Bool  =  GT
+_ :: [Int] `compare` False :: Bool  =  LT
+_ :: [Int] `compare` True :: Bool  =  LT
+_ :: [Int] `compare` not _ :: Bool  =  LT
+_ :: [Int] `compare` c :: Char  =  GT
+_ :: [Int] `compare` 'a' :: Char  =  LT
+_ :: [Int] `compare` xs :: [Int]  =  LT
+_ :: [Int] `compare` [] :: [Int]  =  LT
+_ :: [Int] `compare` _:_ :: [Int]  =  LT
+_ :: [Int] `compare` id :: Int -> Int  =  LT
+_ :: [Int] `compare` (+) :: Int -> Int -> Int  =  LT
+y :: Int `compare` x :: Int  =  GT
+y :: Int `compare` 0 :: Int  =  LT
+y :: Int `compare` id _ :: Int  =  LT
+y :: Int `compare` _ :: Bool  =  GT
+y :: Int `compare` _ :: Char  =  GT
+y :: Int `compare` _ :: [Int]  =  LT
+1 :: Int `compare` x :: Int  =  GT
+1 :: Int `compare` 0 :: Int  =  GT
+1 :: Int `compare` id _ :: Int  =  LT
+1 :: Int `compare` _ :: Bool  =  GT
+1 :: Int `compare` _ :: Char  =  GT
+1 :: Int `compare` _ :: [Int]  =  GT
+id x :: Int `compare` x :: Int  =  GT
+id x :: Int `compare` 0 :: Int  =  GT
+id x :: Int `compare` id _ :: Int  =  GT
+id x :: Int `compare` _ :: Bool  =  GT
+id x :: Int `compare` _ :: Char  =  GT
+id x :: Int `compare` _ :: [Int]  =  GT
+id 0 :: Int `compare` x :: Int  =  GT
+id 0 :: Int `compare` 0 :: Int  =  GT
+id 0 :: Int `compare` id _ :: Int  =  GT
+id 0 :: Int `compare` _ :: Bool  =  GT
+id 0 :: Int `compare` _ :: Char  =  GT
+id 0 :: Int `compare` _ :: [Int]  =  GT
+id (id _) :: Int `compare` x :: Int  =  GT
+id (id _) :: Int `compare` 0 :: Int  =  GT
+id (id _) :: Int `compare` id _ :: Int  =  GT
+id (id _) :: Int `compare` _ :: Bool  =  GT
+id (id _) :: Int `compare` _ :: Char  =  GT
+id (id _) :: Int `compare` _ :: [Int]  =  GT
+negate _ :: Int `compare` x :: Int  =  GT
+negate _ :: Int `compare` 0 :: Int  =  GT
+negate _ :: Int `compare` id _ :: Int  =  GT
+negate _ :: Int `compare` _ :: Bool  =  GT
+negate _ :: Int `compare` _ :: Char  =  GT
+negate _ :: Int `compare` _ :: [Int]  =  GT
+abs _ :: Int `compare` x :: Int  =  GT
+abs _ :: Int `compare` 0 :: Int  =  GT
+abs _ :: Int `compare` id _ :: Int  =  LT
+abs _ :: Int `compare` _ :: Bool  =  GT
+abs _ :: Int `compare` _ :: Char  =  GT
+abs _ :: Int `compare` _ :: [Int]  =  GT
+_ + _ :: Int `compare` x :: Int  =  GT
+_ + _ :: Int `compare` 0 :: Int  =  GT
+_ + _ :: Int `compare` id _ :: Int  =  GT
+_ + _ :: Int `compare` _ :: Bool  =  GT
+_ + _ :: Int `compare` _ :: Char  =  GT
+_ + _ :: Int `compare` _ :: [Int]  =  GT
+head _ :: Int `compare` x :: Int  =  GT
+head _ :: Int `compare` 0 :: Int  =  GT
+head _ :: Int `compare` id _ :: Int  =  GT
+head _ :: Int `compare` _ :: Bool  =  GT
+head _ :: Int `compare` _ :: Char  =  GT
+head _ :: Int `compare` _ :: [Int]  =  GT
+ord _ :: Int `compare` x :: Int  =  GT
+ord _ :: Int `compare` 0 :: Int  =  GT
+ord _ :: Int `compare` id _ :: Int  =  LT
+ord _ :: Int `compare` _ :: Bool  =  GT
+ord _ :: Int `compare` _ :: Char  =  GT
+ord _ :: Int `compare` _ :: [Int]  =  GT
+p :: Bool `compare` x :: Int  =  LT
+p :: Bool `compare` 0 :: Int  =  LT
+p :: Bool `compare` id _ :: Int  =  LT
+p :: Bool `compare` _ :: Bool  =  GT
+p :: Bool `compare` _ :: Char  =  LT
+p :: Bool `compare` _ :: [Int]  =  LT
+False :: Bool `compare` x :: Int  =  GT
+False :: Bool `compare` 0 :: Int  =  LT
+False :: Bool `compare` id _ :: Int  =  LT
+False :: Bool `compare` _ :: Bool  =  GT
+False :: Bool `compare` _ :: Char  =  GT
+False :: Bool `compare` _ :: [Int]  =  GT
+True :: Bool `compare` x :: Int  =  GT
+True :: Bool `compare` 0 :: Int  =  LT
+True :: Bool `compare` id _ :: Int  =  LT
+True :: Bool `compare` _ :: Bool  =  GT
+True :: Bool `compare` _ :: Char  =  GT
+True :: Bool `compare` _ :: [Int]  =  GT
+not _ :: Bool `compare` x :: Int  =  GT
+not _ :: Bool `compare` 0 :: Int  =  GT
+not _ :: Bool `compare` id _ :: Int  =  LT
+not _ :: Bool `compare` _ :: Bool  =  GT
+not _ :: Bool `compare` _ :: Char  =  GT
+not _ :: Bool `compare` _ :: [Int]  =  GT
+c :: Char `compare` x :: Int  =  LT
+c :: Char `compare` 0 :: Int  =  LT
+c :: Char `compare` id _ :: Int  =  LT
+c :: Char `compare` _ :: Bool  =  GT
+c :: Char `compare` _ :: Char  =  GT
+c :: Char `compare` _ :: [Int]  =  LT
+'a' :: Char `compare` x :: Int  =  GT
+'a' :: Char `compare` 0 :: Int  =  LT
+'a' :: Char `compare` id _ :: Int  =  LT
+'a' :: Char `compare` _ :: Bool  =  GT
+'a' :: Char `compare` _ :: Char  =  GT
+'a' :: Char `compare` _ :: [Int]  =  GT
+xs :: [Int] `compare` x :: Int  =  GT
+xs :: [Int] `compare` 0 :: Int  =  LT
+xs :: [Int] `compare` id _ :: Int  =  LT
+xs :: [Int] `compare` _ :: Bool  =  GT
+xs :: [Int] `compare` _ :: Char  =  GT
+xs :: [Int] `compare` _ :: [Int]  =  GT
+[] :: [Int] `compare` x :: Int  =  GT
+[] :: [Int] `compare` 0 :: Int  =  GT
+[] :: [Int] `compare` id _ :: Int  =  LT
+[] :: [Int] `compare` _ :: Bool  =  GT
+[] :: [Int] `compare` _ :: Char  =  GT
+[] :: [Int] `compare` _ :: [Int]  =  GT
+_:_ :: [Int] `compare` x :: Int  =  GT
+_:_ :: [Int] `compare` 0 :: Int  =  GT
+_:_ :: [Int] `compare` id _ :: Int  =  GT
+_:_ :: [Int] `compare` _ :: Bool  =  GT
+_:_ :: [Int] `compare` _ :: Char  =  GT
+_:_ :: [Int] `compare` _ :: [Int]  =  GT
+id :: Int -> Int `compare` x :: Int  =  GT
+id :: Int -> Int `compare` 0 :: Int  =  GT
+id :: Int -> Int `compare` id _ :: Int  =  LT
+id :: Int -> Int `compare` _ :: Bool  =  GT
+id :: Int -> Int `compare` _ :: Char  =  GT
+id :: Int -> Int `compare` _ :: [Int]  =  GT
+(+) :: Int -> Int -> Int `compare` x :: Int  =  GT
+(+) :: Int -> Int -> Int `compare` 0 :: Int  =  GT
+(+) :: Int -> Int -> Int `compare` id _ :: Int  =  LT
+(+) :: Int -> Int -> Int `compare` _ :: Bool  =  GT
+(+) :: Int -> Int -> Int `compare` _ :: Char  =  GT
+(+) :: Int -> Int -> Int `compare` _ :: [Int]  =  GT
+z :: Int `compare` _ :: Int  =  GT
+-1 :: Int `compare` _ :: Int  =  GT
+id y :: Int `compare` _ :: Int  =  GT
+id 1 :: Int `compare` _ :: Int  =  GT
+id (id x) :: Int `compare` _ :: Int  =  GT
+id (id 0) :: Int `compare` _ :: Int  =  GT
+id (id (id _)) :: Int `compare` _ :: Int  =  GT
+id (negate _) :: Int `compare` _ :: Int  =  GT
+id (abs _) :: Int `compare` _ :: Int  =  GT
+id (_ + _) :: Int `compare` _ :: Int  =  GT
+id (head _) :: Int `compare` _ :: Int  =  GT
+id (ord _) :: Int `compare` _ :: Int  =  GT
+negate x :: Int `compare` _ :: Int  =  GT
+negate 0 :: Int `compare` _ :: Int  =  GT
+negate (id _) :: Int `compare` _ :: Int  =  GT
+abs x :: Int `compare` _ :: Int  =  GT
+abs 0 :: Int `compare` _ :: Int  =  GT
+abs (id _) :: Int `compare` _ :: Int  =  GT
+_ + x :: Int `compare` _ :: Int  =  GT
+_ + 0 :: Int `compare` _ :: Int  =  GT
+_ + id _ :: Int `compare` _ :: Int  =  GT
+x + _ :: Int `compare` _ :: Int  =  GT
+0 + _ :: Int `compare` _ :: Int  =  GT
+id _ + _ :: Int `compare` _ :: Int  =  GT
+_ * _ :: Int `compare` _ :: Int  =  GT
+f _ :: Int `compare` _ :: Int  =  GT
+head xs :: Int `compare` _ :: Int  =  GT
+head [] :: Int `compare` _ :: Int  =  GT
+head (_:_) :: Int `compare` _ :: Int  =  GT
+ord c :: Int `compare` _ :: Int  =  GT
+ord 'a' :: Int `compare` _ :: Int  =  GT
+q :: Bool `compare` _ :: Int  =  LT
+not p :: Bool `compare` _ :: Int  =  GT
+not False :: Bool `compare` _ :: Int  =  GT
+not True :: Bool `compare` _ :: Int  =  GT
+not (not _) :: Bool `compare` _ :: Int  =  GT
+_ || _ :: Bool `compare` _ :: Int  =  GT
+d :: Char `compare` _ :: Int  =  LT
+' ' :: Char `compare` _ :: Int  =  GT
+ys :: [Int] `compare` _ :: Int  =  GT
+[0] :: [Int] `compare` _ :: Int  =  GT
+_:xs :: [Int] `compare` _ :: Int  =  GT
+[_] :: [Int] `compare` _ :: Int  =  GT
+_:_:_ :: [Int] `compare` _ :: Int  =  GT
+x:_ :: [Int] `compare` _ :: Int  =  GT
+0:_ :: [Int] `compare` _ :: Int  =  GT
+id _:_ :: [Int] `compare` _ :: Int  =  GT
+tail _ :: [Int] `compare` _ :: Int  =  GT
+_ ++ _ :: [Int] `compare` _ :: Int  =  GT
+negate :: Int -> Int `compare` _ :: Int  =  GT
+abs :: Int -> Int `compare` _ :: Int  =  GT
+(_ +) :: Int -> Int `compare` _ :: Int  =  GT
+(*) :: Int -> Int -> Int `compare` _ :: Int  =  GT
+not :: Bool -> Bool `compare` _ :: Int  =  GT
+(||) :: Bool -> Bool -> Bool `compare` _ :: Int  =  GT
+_ :: Int `compare` x' :: Int  =  LT
+_ :: Int `compare` 2 :: Int  =  LT
+_ :: Int `compare` id z :: Int  =  LT
+_ :: Int `compare` id (-1) :: Int  =  LT
+_ :: Int `compare` id (id y) :: Int  =  LT
+_ :: Int `compare` id (id 1) :: Int  =  LT
+_ :: Int `compare` id (id (id x)) :: Int  =  LT
+_ :: Int `compare` id (id (id 0)) :: Int  =  LT
+_ :: Int `compare` id (id (id (id _))) :: Int  =  LT
+_ :: Int `compare` id (id (negate _)) :: Int  =  LT
+_ :: Int `compare` id (id (abs _)) :: Int  =  LT
+_ :: Int `compare` id (id (_ + _)) :: Int  =  LT
+_ :: Int `compare` id (id (head _)) :: Int  =  LT
+_ :: Int `compare` id (id (ord _)) :: Int  =  LT
+_ :: Int `compare` id (negate x) :: Int  =  LT
+_ :: Int `compare` id (negate 0) :: Int  =  LT
+_ :: Int `compare` id (negate (id _)) :: Int  =  LT
+_ :: Int `compare` id (abs x) :: Int  =  LT
+_ :: Int `compare` id (abs 0) :: Int  =  LT
+_ :: Int `compare` id (abs (id _)) :: Int  =  LT
+_ :: Int `compare` id (_ + x) :: Int  =  LT
+_ :: Int `compare` id (_ + 0) :: Int  =  LT
+_ :: Int `compare` id (_ + id _) :: Int  =  LT
+_ :: Int `compare` id (x + _) :: Int  =  LT
+_ :: Int `compare` id (0 + _) :: Int  =  LT
+_ :: Int `compare` id (id _ + _) :: Int  =  LT
+_ :: Int `compare` id (_ * _) :: Int  =  LT
+_ :: Int `compare` id (f _) :: Int  =  LT
+_ :: Int `compare` id (head xs) :: Int  =  LT
+_ :: Int `compare` id (head []) :: Int  =  LT
+_ :: Int `compare` id (head (_:_)) :: Int  =  LT
+_ :: Int `compare` id (ord c) :: Int  =  LT
+_ :: Int `compare` id (ord 'a') :: Int  =  LT
+_ :: Int `compare` negate y :: Int  =  LT
+_ :: Int `compare` negate 1 :: Int  =  LT
+_ :: Int `compare` negate (id x) :: Int  =  LT
+_ :: Int `compare` negate (id 0) :: Int  =  LT
+_ :: Int `compare` negate (id (id _)) :: Int  =  LT
+_ :: Int `compare` negate (negate _) :: Int  =  LT
+_ :: Int `compare` negate (abs _) :: Int  =  LT
+_ :: Int `compare` negate (_ + _) :: Int  =  LT
+_ :: Int `compare` negate (head _) :: Int  =  LT
+_ :: Int `compare` negate (ord _) :: Int  =  LT
+_ :: Int `compare` abs y :: Int  =  LT
+_ :: Int `compare` abs 1 :: Int  =  LT
+_ :: Int `compare` abs (id x) :: Int  =  LT
+_ :: Int `compare` abs (id 0) :: Int  =  LT
+_ :: Int `compare` abs (id (id _)) :: Int  =  LT
+_ :: Int `compare` abs (negate _) :: Int  =  LT
+_ :: Int `compare` abs (abs _) :: Int  =  LT
+_ :: Int `compare` abs (_ + _) :: Int  =  LT
+_ :: Int `compare` abs (head _) :: Int  =  LT
+_ :: Int `compare` abs (ord _) :: Int  =  LT
+_ :: Int `compare` _ + y :: Int  =  LT
+_ :: Int `compare` _ + 1 :: Int  =  LT
+_ :: Int `compare` _ + id x :: Int  =  LT
+_ :: Int `compare` _ + id 0 :: Int  =  LT
+_ :: Int `compare` _ + id (id _) :: Int  =  LT
+_ :: Int `compare` _ + negate _ :: Int  =  LT
+_ :: Int `compare` _ + abs _ :: Int  =  LT
+_ :: Int `compare` _ + (_ + _) :: Int  =  LT
+_ :: Int `compare` _ + head _ :: Int  =  LT
+_ :: Int `compare` _ + ord _ :: Int  =  LT
+_ :: Int `compare` x + x :: Int  =  LT
+_ :: Int `compare` x + 0 :: Int  =  LT
+_ :: Int `compare` x + id _ :: Int  =  LT
+_ :: Int `compare` 0 + x :: Int  =  LT
+_ :: Int `compare` 0 + 0 :: Int  =  LT
+_ :: Int `compare` 0 + id _ :: Int  =  LT
+_ :: Int `compare` id _ + x :: Int  =  LT
+_ :: Int `compare` id _ + 0 :: Int  =  LT
+_ :: Int `compare` id _ + id _ :: Int  =  LT
+_ :: Int `compare` _ * x :: Int  =  LT
+_ :: Int `compare` _ * 0 :: Int  =  LT
+_ :: Int `compare` _ * id _ :: Int  =  LT
+_ :: Int `compare` f x :: Int  =  LT
+_ :: Int `compare` f 0 :: Int  =  LT
+_ :: Int `compare` f (id _) :: Int  =  LT
+_ :: Int `compare` y + _ :: Int  =  LT
+_ :: Int `compare` 1 + _ :: Int  =  LT
+_ :: Int `compare` id x + _ :: Int  =  LT
+_ :: Int `compare` id 0 + _ :: Int  =  LT
+_ :: Int `compare` id (id _) + _ :: Int  =  LT
+_ :: Int `compare` negate _ + _ :: Int  =  LT
+_ :: Int `compare` abs _ + _ :: Int  =  LT
+_ :: Int `compare` (_ + _) + _ :: Int  =  LT
+_ :: Int `compare` head _ + _ :: Int  =  LT
+_ :: Int `compare` ord _ + _ :: Int  =  LT
+_ :: Int `compare` x * _ :: Int  =  LT
+_ :: Int `compare` 0 * _ :: Int  =  LT
+_ :: Int `compare` id _ * _ :: Int  =  LT
+_ :: Int `compare` g _ :: Int  =  LT
+_ :: Int `compare` head ys :: Int  =  LT
+_ :: Int `compare` head [0] :: Int  =  LT
+_ :: Int `compare` head (_:xs) :: Int  =  LT
+_ :: Int `compare` head [_] :: Int  =  LT
+_ :: Int `compare` head (_:_:_) :: Int  =  LT
+_ :: Int `compare` head (x:_) :: Int  =  LT
+_ :: Int `compare` head (0:_) :: Int  =  LT
+_ :: Int `compare` head (id _:_) :: Int  =  LT
+_ :: Int `compare` head (tail _) :: Int  =  LT
+_ :: Int `compare` head (_ ++ _) :: Int  =  LT
+_ :: Int `compare` ord d :: Int  =  LT
+_ :: Int `compare` ord ' ' :: Int  =  LT
+_ :: Int `compare` r :: Bool  =  GT
+_ :: Int `compare` not q :: Bool  =  LT
+_ :: Int `compare` not (not p) :: Bool  =  LT
+_ :: Int `compare` not (not False) :: Bool  =  LT
+_ :: Int `compare` not (not True) :: Bool  =  LT
+_ :: Int `compare` not (not (not _)) :: Bool  =  LT
+_ :: Int `compare` not (_ || _) :: Bool  =  LT
+_ :: Int `compare` _ || p :: Bool  =  LT
+_ :: Int `compare` _ || False :: Bool  =  LT
+_ :: Int `compare` _ || True :: Bool  =  LT
+_ :: Int `compare` _ || not _ :: Bool  =  LT
+_ :: Int `compare` p || _ :: Bool  =  LT
+_ :: Int `compare` False || _ :: Bool  =  LT
+_ :: Int `compare` True || _ :: Bool  =  LT
+_ :: Int `compare` not _ || _ :: Bool  =  LT
+_ :: Int `compare` _ && _ :: Bool  =  LT
+_ :: Int `compare` _ == _ :: Bool  =  LT
+_ :: Int `compare` _ == _ :: Bool  =  LT
+_ :: Int `compare` odd _ :: Bool  =  LT
+_ :: Int `compare` even _ :: Bool  =  LT
+_ :: Int `compare` elem _ _ :: Bool  =  LT
+_ :: Int `compare` e :: Char  =  GT
+_ :: Int `compare` 'b' :: Char  =  LT
+_ :: Int `compare` zs :: [Int]  =  LT
+_ :: Int `compare` [0,0] :: [Int]  =  LT
+_ :: Int `compare` [1] :: [Int]  =  LT
+_ :: Int `compare` _:ys :: [Int]  =  LT
+_ :: Int `compare` [_,0] :: [Int]  =  LT
+_ :: Int `compare` _:_:xs :: [Int]  =  LT
+_ :: Int `compare` [_,_] :: [Int]  =  LT
+_ :: Int `compare` _:_:_:_ :: [Int]  =  LT
+_ :: Int `compare` _:x:_ :: [Int]  =  LT
+_ :: Int `compare` _:0:_ :: [Int]  =  LT
+_ :: Int `compare` _:id _:_ :: [Int]  =  LT
+_ :: Int `compare` _:tail _ :: [Int]  =  LT
+_ :: Int `compare` _:(_ ++ _) :: [Int]  =  LT
+_ :: Int `compare` x:xs :: [Int]  =  LT
+_ :: Int `compare` [x] :: [Int]  =  LT
+_ :: Int `compare` x:_:_ :: [Int]  =  LT
+_ :: Int `compare` 0:xs :: [Int]  =  LT
+_ :: Int `compare` [0] :: [Int]  =  LT
+_ :: Int `compare` 0:_:_ :: [Int]  =  LT
+_ :: Int `compare` id _:xs :: [Int]  =  LT
+_ :: Int `compare` [id _] :: [Int]  =  LT
+_ :: Int `compare` id _:_:_ :: [Int]  =  LT
+_ :: Int `compare` y:_ :: [Int]  =  LT
+_ :: Int `compare` 1:_ :: [Int]  =  LT
+_ :: Int `compare` id x:_ :: [Int]  =  LT
+_ :: Int `compare` id 0:_ :: [Int]  =  LT
+_ :: Int `compare` id (id _):_ :: [Int]  =  LT
+_ :: Int `compare` negate _:_ :: [Int]  =  LT
+_ :: Int `compare` abs _:_ :: [Int]  =  LT
+_ :: Int `compare` _ + _:_ :: [Int]  =  LT
+_ :: Int `compare` head _:_ :: [Int]  =  LT
+_ :: Int `compare` ord _:_ :: [Int]  =  LT
+_ :: Int `compare` tail xs :: [Int]  =  LT
+_ :: Int `compare` tail [] :: [Int]  =  LT
+_ :: Int `compare` tail (_:_) :: [Int]  =  LT
+_ :: Int `compare` _ ++ xs :: [Int]  =  LT
+_ :: Int `compare` _ ++ [] :: [Int]  =  LT
+_ :: Int `compare` _ ++ (_:_) :: [Int]  =  LT
+_ :: Int `compare` xs ++ _ :: [Int]  =  LT
+_ :: Int `compare` [] ++ _ :: [Int]  =  LT
+_ :: Int `compare` (_:_) ++ _ :: [Int]  =  LT
+_ :: Int `compare` sort _ :: [Int]  =  LT
+_ :: Int `compare` insert _ _ :: [Int]  =  LT
+_ :: Int `compare` (x +) :: Int -> Int  =  LT
+_ :: Int `compare` (0 +) :: Int -> Int  =  LT
+_ :: Int `compare` (id _ +) :: Int -> Int  =  LT
+_ :: Int `compare` (_ *) :: Int -> Int  =  LT
+_ :: Int `compare` f :: Int -> Int  =  LT
+_ :: Int `compare` (_ ||) :: Bool -> Bool  =  LT
+_ :: Int `compare` (&&) :: Bool -> Bool -> Bool  =  LT
+x :: Int `compare` z :: Int  =  LT
+x :: Int `compare` -1 :: Int  =  LT
+x :: Int `compare` id y :: Int  =  LT
+x :: Int `compare` id 1 :: Int  =  LT
+x :: Int `compare` id (id x) :: Int  =  LT
+x :: Int `compare` id (id 0) :: Int  =  LT
+x :: Int `compare` id (id (id _)) :: Int  =  LT
+x :: Int `compare` id (negate _) :: Int  =  LT
+x :: Int `compare` id (abs _) :: Int  =  LT
+x :: Int `compare` id (_ + _) :: Int  =  LT
+x :: Int `compare` id (head _) :: Int  =  LT
+x :: Int `compare` id (ord _) :: Int  =  LT
+x :: Int `compare` negate x :: Int  =  LT
+x :: Int `compare` negate 0 :: Int  =  LT
+x :: Int `compare` negate (id _) :: Int  =  LT
+x :: Int `compare` abs x :: Int  =  LT
+x :: Int `compare` abs 0 :: Int  =  LT
+x :: Int `compare` abs (id _) :: Int  =  LT
+x :: Int `compare` _ + x :: Int  =  LT
+x :: Int `compare` _ + 0 :: Int  =  LT
+x :: Int `compare` _ + id _ :: Int  =  LT
+x :: Int `compare` x + _ :: Int  =  LT
+x :: Int `compare` 0 + _ :: Int  =  LT
+x :: Int `compare` id _ + _ :: Int  =  LT
+x :: Int `compare` _ * _ :: Int  =  LT
+x :: Int `compare` f _ :: Int  =  LT
+x :: Int `compare` head xs :: Int  =  LT
+x :: Int `compare` head [] :: Int  =  LT
+x :: Int `compare` head (_:_) :: Int  =  LT
+x :: Int `compare` ord c :: Int  =  LT
+x :: Int `compare` ord 'a' :: Int  =  LT
+x :: Int `compare` q :: Bool  =  GT
+x :: Int `compare` not p :: Bool  =  LT
+x :: Int `compare` not False :: Bool  =  LT
+x :: Int `compare` not True :: Bool  =  LT
+x :: Int `compare` not (not _) :: Bool  =  LT
+x :: Int `compare` _ || _ :: Bool  =  LT
+x :: Int `compare` d :: Char  =  GT
+x :: Int `compare` ' ' :: Char  =  LT
+x :: Int `compare` ys :: [Int]  =  LT
+x :: Int `compare` [0] :: [Int]  =  LT
+x :: Int `compare` _:xs :: [Int]  =  LT
+x :: Int `compare` [_] :: [Int]  =  LT
+x :: Int `compare` _:_:_ :: [Int]  =  LT
+x :: Int `compare` x:_ :: [Int]  =  LT
+x :: Int `compare` 0:_ :: [Int]  =  LT
+x :: Int `compare` id _:_ :: [Int]  =  LT
+x :: Int `compare` tail _ :: [Int]  =  LT
+x :: Int `compare` _ ++ _ :: [Int]  =  LT
+x :: Int `compare` negate :: Int -> Int  =  LT
+x :: Int `compare` abs :: Int -> Int  =  LT
+x :: Int `compare` (_ +) :: Int -> Int  =  LT
+x :: Int `compare` (*) :: Int -> Int -> Int  =  LT
+x :: Int `compare` not :: Bool -> Bool  =  LT
+x :: Int `compare` (||) :: Bool -> Bool -> Bool  =  LT
+0 :: Int `compare` z :: Int  =  GT
+0 :: Int `compare` -1 :: Int  =  GT
+0 :: Int `compare` id y :: Int  =  LT
+0 :: Int `compare` id 1 :: Int  =  LT
+0 :: Int `compare` id (id x) :: Int  =  LT
+0 :: Int `compare` id (id 0) :: Int  =  LT
+0 :: Int `compare` id (id (id _)) :: Int  =  LT
+0 :: Int `compare` id (negate _) :: Int  =  LT
+0 :: Int `compare` id (abs _) :: Int  =  LT
+0 :: Int `compare` id (_ + _) :: Int  =  LT
+0 :: Int `compare` id (head _) :: Int  =  LT
+0 :: Int `compare` id (ord _) :: Int  =  LT
+0 :: Int `compare` negate x :: Int  =  LT
+0 :: Int `compare` negate 0 :: Int  =  LT
+0 :: Int `compare` negate (id _) :: Int  =  LT
+0 :: Int `compare` abs x :: Int  =  LT
+0 :: Int `compare` abs 0 :: Int  =  LT
+0 :: Int `compare` abs (id _) :: Int  =  LT
+0 :: Int `compare` _ + x :: Int  =  LT
+0 :: Int `compare` _ + 0 :: Int  =  LT
+0 :: Int `compare` _ + id _ :: Int  =  LT
+0 :: Int `compare` x + _ :: Int  =  LT
+0 :: Int `compare` 0 + _ :: Int  =  LT
+0 :: Int `compare` id _ + _ :: Int  =  LT
+0 :: Int `compare` _ * _ :: Int  =  LT
+0 :: Int `compare` f _ :: Int  =  LT
+0 :: Int `compare` head xs :: Int  =  LT
+0 :: Int `compare` head [] :: Int  =  LT
+0 :: Int `compare` head (_:_) :: Int  =  LT
+0 :: Int `compare` ord c :: Int  =  LT
+0 :: Int `compare` ord 'a' :: Int  =  LT
+0 :: Int `compare` q :: Bool  =  GT
+0 :: Int `compare` not p :: Bool  =  LT
+0 :: Int `compare` not False :: Bool  =  LT
+0 :: Int `compare` not True :: Bool  =  LT
+0 :: Int `compare` not (not _) :: Bool  =  LT
+0 :: Int `compare` _ || _ :: Bool  =  LT
+0 :: Int `compare` d :: Char  =  GT
+0 :: Int `compare` ' ' :: Char  =  GT
+0 :: Int `compare` ys :: [Int]  =  GT
+0 :: Int `compare` [0] :: [Int]  =  LT
+0 :: Int `compare` _:xs :: [Int]  =  LT
+0 :: Int `compare` [_] :: [Int]  =  LT
+0 :: Int `compare` _:_:_ :: [Int]  =  LT
+0 :: Int `compare` x:_ :: [Int]  =  LT
+0 :: Int `compare` 0:_ :: [Int]  =  LT
+0 :: Int `compare` id _:_ :: [Int]  =  LT
+0 :: Int `compare` tail _ :: [Int]  =  LT
+0 :: Int `compare` _ ++ _ :: [Int]  =  LT
+0 :: Int `compare` negate :: Int -> Int  =  LT
+0 :: Int `compare` abs :: Int -> Int  =  LT
+0 :: Int `compare` (_ +) :: Int -> Int  =  LT
+0 :: Int `compare` (*) :: Int -> Int -> Int  =  LT
+0 :: Int `compare` not :: Bool -> Bool  =  LT
+0 :: Int `compare` (||) :: Bool -> Bool -> Bool  =  LT
+id _ :: Int `compare` z :: Int  =  GT
+id _ :: Int `compare` -1 :: Int  =  GT
+id _ :: Int `compare` id y :: Int  =  LT
+id _ :: Int `compare` id 1 :: Int  =  LT
+id _ :: Int `compare` id (id x) :: Int  =  LT
+id _ :: Int `compare` id (id 0) :: Int  =  LT
+id _ :: Int `compare` id (id (id _)) :: Int  =  LT
+id _ :: Int `compare` id (negate _) :: Int  =  LT
+id _ :: Int `compare` id (abs _) :: Int  =  LT
+id _ :: Int `compare` id (_ + _) :: Int  =  LT
+id _ :: Int `compare` id (head _) :: Int  =  LT
+id _ :: Int `compare` id (ord _) :: Int  =  LT
+id _ :: Int `compare` negate x :: Int  =  LT
+id _ :: Int `compare` negate 0 :: Int  =  LT
+id _ :: Int `compare` negate (id _) :: Int  =  LT
+id _ :: Int `compare` abs x :: Int  =  GT
+id _ :: Int `compare` abs 0 :: Int  =  LT
+id _ :: Int `compare` abs (id _) :: Int  =  LT
+id _ :: Int `compare` _ + x :: Int  =  LT
+id _ :: Int `compare` _ + 0 :: Int  =  LT
+id _ :: Int `compare` _ + id _ :: Int  =  LT
+id _ :: Int `compare` x + _ :: Int  =  LT
+id _ :: Int `compare` 0 + _ :: Int  =  LT
+id _ :: Int `compare` id _ + _ :: Int  =  LT
+id _ :: Int `compare` _ * _ :: Int  =  LT
+id _ :: Int `compare` f _ :: Int  =  GT
+id _ :: Int `compare` head xs :: Int  =  LT
+id _ :: Int `compare` head [] :: Int  =  LT
+id _ :: Int `compare` head (_:_) :: Int  =  LT
+id _ :: Int `compare` ord c :: Int  =  GT
+id _ :: Int `compare` ord 'a' :: Int  =  LT
+id _ :: Int `compare` q :: Bool  =  GT
+id _ :: Int `compare` not p :: Bool  =  GT
+id _ :: Int `compare` not False :: Bool  =  LT
+id _ :: Int `compare` not True :: Bool  =  LT
+id _ :: Int `compare` not (not _) :: Bool  =  LT
+id _ :: Int `compare` _ || _ :: Bool  =  LT
+id _ :: Int `compare` d :: Char  =  GT
+id _ :: Int `compare` ' ' :: Char  =  GT
+id _ :: Int `compare` ys :: [Int]  =  GT
+id _ :: Int `compare` [0] :: [Int]  =  GT
+id _ :: Int `compare` _:xs :: [Int]  =  LT
+id _ :: Int `compare` [_] :: [Int]  =  LT
+id _ :: Int `compare` _:_:_ :: [Int]  =  LT
+id _ :: Int `compare` x:_ :: [Int]  =  LT
+id _ :: Int `compare` 0:_ :: [Int]  =  LT
+id _ :: Int `compare` id _:_ :: [Int]  =  LT
+id _ :: Int `compare` tail _ :: [Int]  =  LT
+id _ :: Int `compare` _ ++ _ :: [Int]  =  LT
+id _ :: Int `compare` negate :: Int -> Int  =  GT
+id _ :: Int `compare` abs :: Int -> Int  =  GT
+id _ :: Int `compare` (_ +) :: Int -> Int  =  LT
+id _ :: Int `compare` (*) :: Int -> Int -> Int  =  GT
+id _ :: Int `compare` not :: Bool -> Bool  =  GT
+id _ :: Int `compare` (||) :: Bool -> Bool -> Bool  =  GT
+_ :: Bool `compare` z :: Int  =  LT
+_ :: Bool `compare` -1 :: Int  =  LT
+_ :: Bool `compare` id y :: Int  =  LT
+_ :: Bool `compare` id 1 :: Int  =  LT
+_ :: Bool `compare` id (id x) :: Int  =  LT
+_ :: Bool `compare` id (id 0) :: Int  =  LT
+_ :: Bool `compare` id (id (id _)) :: Int  =  LT
+_ :: Bool `compare` id (negate _) :: Int  =  LT
+_ :: Bool `compare` id (abs _) :: Int  =  LT
+_ :: Bool `compare` id (_ + _) :: Int  =  LT
+_ :: Bool `compare` id (head _) :: Int  =  LT
+_ :: Bool `compare` id (ord _) :: Int  =  LT
+_ :: Bool `compare` negate x :: Int  =  LT
+_ :: Bool `compare` negate 0 :: Int  =  LT
+_ :: Bool `compare` negate (id _) :: Int  =  LT
+_ :: Bool `compare` abs x :: Int  =  LT
+_ :: Bool `compare` abs 0 :: Int  =  LT
+_ :: Bool `compare` abs (id _) :: Int  =  LT
+_ :: Bool `compare` _ + x :: Int  =  LT
+_ :: Bool `compare` _ + 0 :: Int  =  LT
+_ :: Bool `compare` _ + id _ :: Int  =  LT
+_ :: Bool `compare` x + _ :: Int  =  LT
+_ :: Bool `compare` 0 + _ :: Int  =  LT
+_ :: Bool `compare` id _ + _ :: Int  =  LT
+_ :: Bool `compare` _ * _ :: Int  =  LT
+_ :: Bool `compare` f _ :: Int  =  LT
+_ :: Bool `compare` head xs :: Int  =  LT
+_ :: Bool `compare` head [] :: Int  =  LT
+_ :: Bool `compare` head (_:_) :: Int  =  LT
+_ :: Bool `compare` ord c :: Int  =  LT
+_ :: Bool `compare` ord 'a' :: Int  =  LT
+_ :: Bool `compare` q :: Bool  =  LT
+_ :: Bool `compare` not p :: Bool  =  LT
+_ :: Bool `compare` not False :: Bool  =  LT
+_ :: Bool `compare` not True :: Bool  =  LT
+_ :: Bool `compare` not (not _) :: Bool  =  LT
+_ :: Bool `compare` _ || _ :: Bool  =  LT
+_ :: Bool `compare` d :: Char  =  LT
+_ :: Bool `compare` ' ' :: Char  =  LT
+_ :: Bool `compare` ys :: [Int]  =  LT
+_ :: Bool `compare` [0] :: [Int]  =  LT
+_ :: Bool `compare` _:xs :: [Int]  =  LT
+_ :: Bool `compare` [_] :: [Int]  =  LT
+_ :: Bool `compare` _:_:_ :: [Int]  =  LT
+_ :: Bool `compare` x:_ :: [Int]  =  LT
+_ :: Bool `compare` 0:_ :: [Int]  =  LT
+_ :: Bool `compare` id _:_ :: [Int]  =  LT
+_ :: Bool `compare` tail _ :: [Int]  =  LT
+_ :: Bool `compare` _ ++ _ :: [Int]  =  LT
+_ :: Bool `compare` negate :: Int -> Int  =  LT
+_ :: Bool `compare` abs :: Int -> Int  =  LT
+_ :: Bool `compare` (_ +) :: Int -> Int  =  LT
+_ :: Bool `compare` (*) :: Int -> Int -> Int  =  LT
+_ :: Bool `compare` not :: Bool -> Bool  =  LT
+_ :: Bool `compare` (||) :: Bool -> Bool -> Bool  =  LT
+_ :: Char `compare` z :: Int  =  LT
+_ :: Char `compare` -1 :: Int  =  LT
+_ :: Char `compare` id y :: Int  =  LT
+_ :: Char `compare` id 1 :: Int  =  LT
+_ :: Char `compare` id (id x) :: Int  =  LT
+_ :: Char `compare` id (id 0) :: Int  =  LT
+_ :: Char `compare` id (id (id _)) :: Int  =  LT
+_ :: Char `compare` id (negate _) :: Int  =  LT
+_ :: Char `compare` id (abs _) :: Int  =  LT
+_ :: Char `compare` id (_ + _) :: Int  =  LT
+_ :: Char `compare` id (head _) :: Int  =  LT
+_ :: Char `compare` id (ord _) :: Int  =  LT
+_ :: Char `compare` negate x :: Int  =  LT
+_ :: Char `compare` negate 0 :: Int  =  LT
+_ :: Char `compare` negate (id _) :: Int  =  LT
+_ :: Char `compare` abs x :: Int  =  LT
+_ :: Char `compare` abs 0 :: Int  =  LT
+_ :: Char `compare` abs (id _) :: Int  =  LT
+_ :: Char `compare` _ + x :: Int  =  LT
+_ :: Char `compare` _ + 0 :: Int  =  LT
+_ :: Char `compare` _ + id _ :: Int  =  LT
+_ :: Char `compare` x + _ :: Int  =  LT
+_ :: Char `compare` 0 + _ :: Int  =  LT
+_ :: Char `compare` id _ + _ :: Int  =  LT
+_ :: Char `compare` _ * _ :: Int  =  LT
+_ :: Char `compare` f _ :: Int  =  LT
+_ :: Char `compare` head xs :: Int  =  LT
+_ :: Char `compare` head [] :: Int  =  LT
+_ :: Char `compare` head (_:_) :: Int  =  LT
+_ :: Char `compare` ord c :: Int  =  LT
+_ :: Char `compare` ord 'a' :: Int  =  LT
+_ :: Char `compare` q :: Bool  =  GT
+_ :: Char `compare` not p :: Bool  =  LT
+_ :: Char `compare` not False :: Bool  =  LT
+_ :: Char `compare` not True :: Bool  =  LT
+_ :: Char `compare` not (not _) :: Bool  =  LT
+_ :: Char `compare` _ || _ :: Bool  =  LT
+_ :: Char `compare` d :: Char  =  LT
+_ :: Char `compare` ' ' :: Char  =  LT
+_ :: Char `compare` ys :: [Int]  =  LT
+_ :: Char `compare` [0] :: [Int]  =  LT
+_ :: Char `compare` _:xs :: [Int]  =  LT
+_ :: Char `compare` [_] :: [Int]  =  LT
+_ :: Char `compare` _:_:_ :: [Int]  =  LT
+_ :: Char `compare` x:_ :: [Int]  =  LT
+_ :: Char `compare` 0:_ :: [Int]  =  LT
+_ :: Char `compare` id _:_ :: [Int]  =  LT
+_ :: Char `compare` tail _ :: [Int]  =  LT
+_ :: Char `compare` _ ++ _ :: [Int]  =  LT
+_ :: Char `compare` negate :: Int -> Int  =  LT
+_ :: Char `compare` abs :: Int -> Int  =  LT
+_ :: Char `compare` (_ +) :: Int -> Int  =  LT
+_ :: Char `compare` (*) :: Int -> Int -> Int  =  LT
+_ :: Char `compare` not :: Bool -> Bool  =  LT
+_ :: Char `compare` (||) :: Bool -> Bool -> Bool  =  LT
+_ :: [Int] `compare` z :: Int  =  GT
+_ :: [Int] `compare` -1 :: Int  =  LT
+_ :: [Int] `compare` id y :: Int  =  LT
+_ :: [Int] `compare` id 1 :: Int  =  LT
+_ :: [Int] `compare` id (id x) :: Int  =  LT
+_ :: [Int] `compare` id (id 0) :: Int  =  LT
+_ :: [Int] `compare` id (id (id _)) :: Int  =  LT
+_ :: [Int] `compare` id (negate _) :: Int  =  LT
+_ :: [Int] `compare` id (abs _) :: Int  =  LT
+_ :: [Int] `compare` id (_ + _) :: Int  =  LT
+_ :: [Int] `compare` id (head _) :: Int  =  LT
+_ :: [Int] `compare` id (ord _) :: Int  =  LT
+_ :: [Int] `compare` negate x :: Int  =  LT
+_ :: [Int] `compare` negate 0 :: Int  =  LT
+_ :: [Int] `compare` negate (id _) :: Int  =  LT
+_ :: [Int] `compare` abs x :: Int  =  LT
+_ :: [Int] `compare` abs 0 :: Int  =  LT
+_ :: [Int] `compare` abs (id _) :: Int  =  LT
+_ :: [Int] `compare` _ + x :: Int  =  LT
+_ :: [Int] `compare` _ + 0 :: Int  =  LT
+_ :: [Int] `compare` _ + id _ :: Int  =  LT
+_ :: [Int] `compare` x + _ :: Int  =  LT
+_ :: [Int] `compare` 0 + _ :: Int  =  LT
+_ :: [Int] `compare` id _ + _ :: Int  =  LT
+_ :: [Int] `compare` _ * _ :: Int  =  LT
+_ :: [Int] `compare` f _ :: Int  =  LT
+_ :: [Int] `compare` head xs :: Int  =  LT
+_ :: [Int] `compare` head [] :: Int  =  LT
+_ :: [Int] `compare` head (_:_) :: Int  =  LT
+_ :: [Int] `compare` ord c :: Int  =  LT
+_ :: [Int] `compare` ord 'a' :: Int  =  LT
+_ :: [Int] `compare` q :: Bool  =  GT
+_ :: [Int] `compare` not p :: Bool  =  LT
+_ :: [Int] `compare` not False :: Bool  =  LT
+_ :: [Int] `compare` not True :: Bool  =  LT
+_ :: [Int] `compare` not (not _) :: Bool  =  LT
+_ :: [Int] `compare` _ || _ :: Bool  =  LT
+_ :: [Int] `compare` d :: Char  =  GT
+_ :: [Int] `compare` ' ' :: Char  =  LT
+_ :: [Int] `compare` ys :: [Int]  =  LT
+_ :: [Int] `compare` [0] :: [Int]  =  LT
+_ :: [Int] `compare` _:xs :: [Int]  =  LT
+_ :: [Int] `compare` [_] :: [Int]  =  LT
+_ :: [Int] `compare` _:_:_ :: [Int]  =  LT
+_ :: [Int] `compare` x:_ :: [Int]  =  LT
+_ :: [Int] `compare` 0:_ :: [Int]  =  LT
+_ :: [Int] `compare` id _:_ :: [Int]  =  LT
+_ :: [Int] `compare` tail _ :: [Int]  =  LT
+_ :: [Int] `compare` _ ++ _ :: [Int]  =  LT
+_ :: [Int] `compare` negate :: Int -> Int  =  LT
+_ :: [Int] `compare` abs :: Int -> Int  =  LT
+_ :: [Int] `compare` (_ +) :: Int -> Int  =  LT
+_ :: [Int] `compare` (*) :: Int -> Int -> Int  =  LT
+_ :: [Int] `compare` not :: Bool -> Bool  =  LT
+_ :: [Int] `compare` (||) :: Bool -> Bool -> Bool  =  LT
+y :: Int `compare` y :: Int  =  EQ
+y :: Int `compare` 1 :: Int  =  LT
+y :: Int `compare` id x :: Int  =  LT
+y :: Int `compare` id 0 :: Int  =  LT
+y :: Int `compare` id (id _) :: Int  =  LT
+y :: Int `compare` negate _ :: Int  =  LT
+y :: Int `compare` abs _ :: Int  =  LT
+y :: Int `compare` _ + _ :: Int  =  LT
+y :: Int `compare` head _ :: Int  =  LT
+y :: Int `compare` ord _ :: Int  =  LT
+y :: Int `compare` p :: Bool  =  GT
+y :: Int `compare` False :: Bool  =  LT
+y :: Int `compare` True :: Bool  =  LT
+y :: Int `compare` not _ :: Bool  =  LT
+y :: Int `compare` c :: Char  =  GT
+y :: Int `compare` 'a' :: Char  =  LT
+y :: Int `compare` xs :: [Int]  =  LT
+y :: Int `compare` [] :: [Int]  =  LT
+y :: Int `compare` _:_ :: [Int]  =  LT
+y :: Int `compare` id :: Int -> Int  =  LT
+y :: Int `compare` (+) :: Int -> Int -> Int  =  LT
+1 :: Int `compare` y :: Int  =  GT
+1 :: Int `compare` 1 :: Int  =  EQ
+1 :: Int `compare` id x :: Int  =  LT
+1 :: Int `compare` id 0 :: Int  =  LT
+1 :: Int `compare` id (id _) :: Int  =  LT
+1 :: Int `compare` negate _ :: Int  =  LT
+1 :: Int `compare` abs _ :: Int  =  LT
+1 :: Int `compare` _ + _ :: Int  =  LT
+1 :: Int `compare` head _ :: Int  =  LT
+1 :: Int `compare` ord _ :: Int  =  LT
+1 :: Int `compare` p :: Bool  =  GT
+1 :: Int `compare` False :: Bool  =  GT
+1 :: Int `compare` True :: Bool  =  GT
+1 :: Int `compare` not _ :: Bool  =  LT
+1 :: Int `compare` c :: Char  =  GT
+1 :: Int `compare` 'a' :: Char  =  GT
+1 :: Int `compare` xs :: [Int]  =  GT
+1 :: Int `compare` [] :: [Int]  =  LT
+1 :: Int `compare` _:_ :: [Int]  =  LT
+
diff --git a/test/model/bench/pairs.out b/test/model/bench/pairs.out
new file mode 100644
--- /dev/null
+++ b/test/model/bench/pairs.out
@@ -0,0 +1,1001 @@
+(_ :: Int,_ :: Int)
+(_ :: Int,x :: Int)
+(_ :: Int,0 :: Int)
+(_ :: Int,id _ :: Int)
+(_ :: Int,_ :: Bool)
+(_ :: Int,_ :: Char)
+(_ :: Int,_ :: [Int])
+(x :: Int,_ :: Int)
+(0 :: Int,_ :: Int)
+(id _ :: Int,_ :: Int)
+(_ :: Bool,_ :: Int)
+(_ :: Char,_ :: Int)
+(_ :: [Int],_ :: Int)
+(_ :: Int,y :: Int)
+(_ :: Int,1 :: Int)
+(_ :: Int,id x :: Int)
+(_ :: Int,id 0 :: Int)
+(_ :: Int,id (id _) :: Int)
+(_ :: Int,negate _ :: Int)
+(_ :: Int,abs _ :: Int)
+(_ :: Int,_ + _ :: Int)
+(_ :: Int,head _ :: Int)
+(_ :: Int,ord _ :: Int)
+(_ :: Int,p :: Bool)
+(_ :: Int,False :: Bool)
+(_ :: Int,True :: Bool)
+(_ :: Int,not _ :: Bool)
+(_ :: Int,c :: Char)
+(_ :: Int,'a' :: Char)
+(_ :: Int,xs :: [Int])
+(_ :: Int,[] :: [Int])
+(_ :: Int,_:_ :: [Int])
+(_ :: Int,id :: Int -> Int)
+(_ :: Int,(+) :: Int -> Int -> Int)
+(x :: Int,x :: Int)
+(x :: Int,0 :: Int)
+(x :: Int,id _ :: Int)
+(x :: Int,_ :: Bool)
+(x :: Int,_ :: Char)
+(x :: Int,_ :: [Int])
+(0 :: Int,x :: Int)
+(0 :: Int,0 :: Int)
+(0 :: Int,id _ :: Int)
+(0 :: Int,_ :: Bool)
+(0 :: Int,_ :: Char)
+(0 :: Int,_ :: [Int])
+(id _ :: Int,x :: Int)
+(id _ :: Int,0 :: Int)
+(id _ :: Int,id _ :: Int)
+(id _ :: Int,_ :: Bool)
+(id _ :: Int,_ :: Char)
+(id _ :: Int,_ :: [Int])
+(_ :: Bool,x :: Int)
+(_ :: Bool,0 :: Int)
+(_ :: Bool,id _ :: Int)
+(_ :: Bool,_ :: Bool)
+(_ :: Bool,_ :: Char)
+(_ :: Bool,_ :: [Int])
+(_ :: Char,x :: Int)
+(_ :: Char,0 :: Int)
+(_ :: Char,id _ :: Int)
+(_ :: Char,_ :: Bool)
+(_ :: Char,_ :: Char)
+(_ :: Char,_ :: [Int])
+(_ :: [Int],x :: Int)
+(_ :: [Int],0 :: Int)
+(_ :: [Int],id _ :: Int)
+(_ :: [Int],_ :: Bool)
+(_ :: [Int],_ :: Char)
+(_ :: [Int],_ :: [Int])
+(y :: Int,_ :: Int)
+(1 :: Int,_ :: Int)
+(id x :: Int,_ :: Int)
+(id 0 :: Int,_ :: Int)
+(id (id _) :: Int,_ :: Int)
+(negate _ :: Int,_ :: Int)
+(abs _ :: Int,_ :: Int)
+(_ + _ :: Int,_ :: Int)
+(head _ :: Int,_ :: Int)
+(ord _ :: Int,_ :: Int)
+(p :: Bool,_ :: Int)
+(False :: Bool,_ :: Int)
+(True :: Bool,_ :: Int)
+(not _ :: Bool,_ :: Int)
+(c :: Char,_ :: Int)
+('a' :: Char,_ :: Int)
+(xs :: [Int],_ :: Int)
+([] :: [Int],_ :: Int)
+(_:_ :: [Int],_ :: Int)
+(id :: Int -> Int,_ :: Int)
+((+) :: Int -> Int -> Int,_ :: Int)
+(_ :: Int,z :: Int)
+(_ :: Int,-1 :: Int)
+(_ :: Int,id y :: Int)
+(_ :: Int,id 1 :: Int)
+(_ :: Int,id (id x) :: Int)
+(_ :: Int,id (id 0) :: Int)
+(_ :: Int,id (id (id _)) :: Int)
+(_ :: Int,id (negate _) :: Int)
+(_ :: Int,id (abs _) :: Int)
+(_ :: Int,id (_ + _) :: Int)
+(_ :: Int,id (head _) :: Int)
+(_ :: Int,id (ord _) :: Int)
+(_ :: Int,negate x :: Int)
+(_ :: Int,negate 0 :: Int)
+(_ :: Int,negate (id _) :: Int)
+(_ :: Int,abs x :: Int)
+(_ :: Int,abs 0 :: Int)
+(_ :: Int,abs (id _) :: Int)
+(_ :: Int,_ + x :: Int)
+(_ :: Int,_ + 0 :: Int)
+(_ :: Int,_ + id _ :: Int)
+(_ :: Int,x + _ :: Int)
+(_ :: Int,0 + _ :: Int)
+(_ :: Int,id _ + _ :: Int)
+(_ :: Int,_ * _ :: Int)
+(_ :: Int,f _ :: Int)
+(_ :: Int,head xs :: Int)
+(_ :: Int,head [] :: Int)
+(_ :: Int,head (_:_) :: Int)
+(_ :: Int,ord c :: Int)
+(_ :: Int,ord 'a' :: Int)
+(_ :: Int,q :: Bool)
+(_ :: Int,not p :: Bool)
+(_ :: Int,not False :: Bool)
+(_ :: Int,not True :: Bool)
+(_ :: Int,not (not _) :: Bool)
+(_ :: Int,_ || _ :: Bool)
+(_ :: Int,d :: Char)
+(_ :: Int,' ' :: Char)
+(_ :: Int,ys :: [Int])
+(_ :: Int,[0] :: [Int])
+(_ :: Int,_:xs :: [Int])
+(_ :: Int,[_] :: [Int])
+(_ :: Int,_:_:_ :: [Int])
+(_ :: Int,x:_ :: [Int])
+(_ :: Int,0:_ :: [Int])
+(_ :: Int,id _:_ :: [Int])
+(_ :: Int,tail _ :: [Int])
+(_ :: Int,_ ++ _ :: [Int])
+(_ :: Int,negate :: Int -> Int)
+(_ :: Int,abs :: Int -> Int)
+(_ :: Int,(_ +) :: Int -> Int)
+(_ :: Int,(*) :: Int -> Int -> Int)
+(_ :: Int,not :: Bool -> Bool)
+(_ :: Int,(||) :: Bool -> Bool -> Bool)
+(x :: Int,y :: Int)
+(x :: Int,1 :: Int)
+(x :: Int,id x :: Int)
+(x :: Int,id 0 :: Int)
+(x :: Int,id (id _) :: Int)
+(x :: Int,negate _ :: Int)
+(x :: Int,abs _ :: Int)
+(x :: Int,_ + _ :: Int)
+(x :: Int,head _ :: Int)
+(x :: Int,ord _ :: Int)
+(x :: Int,p :: Bool)
+(x :: Int,False :: Bool)
+(x :: Int,True :: Bool)
+(x :: Int,not _ :: Bool)
+(x :: Int,c :: Char)
+(x :: Int,'a' :: Char)
+(x :: Int,xs :: [Int])
+(x :: Int,[] :: [Int])
+(x :: Int,_:_ :: [Int])
+(x :: Int,id :: Int -> Int)
+(x :: Int,(+) :: Int -> Int -> Int)
+(0 :: Int,y :: Int)
+(0 :: Int,1 :: Int)
+(0 :: Int,id x :: Int)
+(0 :: Int,id 0 :: Int)
+(0 :: Int,id (id _) :: Int)
+(0 :: Int,negate _ :: Int)
+(0 :: Int,abs _ :: Int)
+(0 :: Int,_ + _ :: Int)
+(0 :: Int,head _ :: Int)
+(0 :: Int,ord _ :: Int)
+(0 :: Int,p :: Bool)
+(0 :: Int,False :: Bool)
+(0 :: Int,True :: Bool)
+(0 :: Int,not _ :: Bool)
+(0 :: Int,c :: Char)
+(0 :: Int,'a' :: Char)
+(0 :: Int,xs :: [Int])
+(0 :: Int,[] :: [Int])
+(0 :: Int,_:_ :: [Int])
+(0 :: Int,id :: Int -> Int)
+(0 :: Int,(+) :: Int -> Int -> Int)
+(id _ :: Int,y :: Int)
+(id _ :: Int,1 :: Int)
+(id _ :: Int,id x :: Int)
+(id _ :: Int,id 0 :: Int)
+(id _ :: Int,id (id _) :: Int)
+(id _ :: Int,negate _ :: Int)
+(id _ :: Int,abs _ :: Int)
+(id _ :: Int,_ + _ :: Int)
+(id _ :: Int,head _ :: Int)
+(id _ :: Int,ord _ :: Int)
+(id _ :: Int,p :: Bool)
+(id _ :: Int,False :: Bool)
+(id _ :: Int,True :: Bool)
+(id _ :: Int,not _ :: Bool)
+(id _ :: Int,c :: Char)
+(id _ :: Int,'a' :: Char)
+(id _ :: Int,xs :: [Int])
+(id _ :: Int,[] :: [Int])
+(id _ :: Int,_:_ :: [Int])
+(id _ :: Int,id :: Int -> Int)
+(id _ :: Int,(+) :: Int -> Int -> Int)
+(_ :: Bool,y :: Int)
+(_ :: Bool,1 :: Int)
+(_ :: Bool,id x :: Int)
+(_ :: Bool,id 0 :: Int)
+(_ :: Bool,id (id _) :: Int)
+(_ :: Bool,negate _ :: Int)
+(_ :: Bool,abs _ :: Int)
+(_ :: Bool,_ + _ :: Int)
+(_ :: Bool,head _ :: Int)
+(_ :: Bool,ord _ :: Int)
+(_ :: Bool,p :: Bool)
+(_ :: Bool,False :: Bool)
+(_ :: Bool,True :: Bool)
+(_ :: Bool,not _ :: Bool)
+(_ :: Bool,c :: Char)
+(_ :: Bool,'a' :: Char)
+(_ :: Bool,xs :: [Int])
+(_ :: Bool,[] :: [Int])
+(_ :: Bool,_:_ :: [Int])
+(_ :: Bool,id :: Int -> Int)
+(_ :: Bool,(+) :: Int -> Int -> Int)
+(_ :: Char,y :: Int)
+(_ :: Char,1 :: Int)
+(_ :: Char,id x :: Int)
+(_ :: Char,id 0 :: Int)
+(_ :: Char,id (id _) :: Int)
+(_ :: Char,negate _ :: Int)
+(_ :: Char,abs _ :: Int)
+(_ :: Char,_ + _ :: Int)
+(_ :: Char,head _ :: Int)
+(_ :: Char,ord _ :: Int)
+(_ :: Char,p :: Bool)
+(_ :: Char,False :: Bool)
+(_ :: Char,True :: Bool)
+(_ :: Char,not _ :: Bool)
+(_ :: Char,c :: Char)
+(_ :: Char,'a' :: Char)
+(_ :: Char,xs :: [Int])
+(_ :: Char,[] :: [Int])
+(_ :: Char,_:_ :: [Int])
+(_ :: Char,id :: Int -> Int)
+(_ :: Char,(+) :: Int -> Int -> Int)
+(_ :: [Int],y :: Int)
+(_ :: [Int],1 :: Int)
+(_ :: [Int],id x :: Int)
+(_ :: [Int],id 0 :: Int)
+(_ :: [Int],id (id _) :: Int)
+(_ :: [Int],negate _ :: Int)
+(_ :: [Int],abs _ :: Int)
+(_ :: [Int],_ + _ :: Int)
+(_ :: [Int],head _ :: Int)
+(_ :: [Int],ord _ :: Int)
+(_ :: [Int],p :: Bool)
+(_ :: [Int],False :: Bool)
+(_ :: [Int],True :: Bool)
+(_ :: [Int],not _ :: Bool)
+(_ :: [Int],c :: Char)
+(_ :: [Int],'a' :: Char)
+(_ :: [Int],xs :: [Int])
+(_ :: [Int],[] :: [Int])
+(_ :: [Int],_:_ :: [Int])
+(_ :: [Int],id :: Int -> Int)
+(_ :: [Int],(+) :: Int -> Int -> Int)
+(y :: Int,x :: Int)
+(y :: Int,0 :: Int)
+(y :: Int,id _ :: Int)
+(y :: Int,_ :: Bool)
+(y :: Int,_ :: Char)
+(y :: Int,_ :: [Int])
+(1 :: Int,x :: Int)
+(1 :: Int,0 :: Int)
+(1 :: Int,id _ :: Int)
+(1 :: Int,_ :: Bool)
+(1 :: Int,_ :: Char)
+(1 :: Int,_ :: [Int])
+(id x :: Int,x :: Int)
+(id x :: Int,0 :: Int)
+(id x :: Int,id _ :: Int)
+(id x :: Int,_ :: Bool)
+(id x :: Int,_ :: Char)
+(id x :: Int,_ :: [Int])
+(id 0 :: Int,x :: Int)
+(id 0 :: Int,0 :: Int)
+(id 0 :: Int,id _ :: Int)
+(id 0 :: Int,_ :: Bool)
+(id 0 :: Int,_ :: Char)
+(id 0 :: Int,_ :: [Int])
+(id (id _) :: Int,x :: Int)
+(id (id _) :: Int,0 :: Int)
+(id (id _) :: Int,id _ :: Int)
+(id (id _) :: Int,_ :: Bool)
+(id (id _) :: Int,_ :: Char)
+(id (id _) :: Int,_ :: [Int])
+(negate _ :: Int,x :: Int)
+(negate _ :: Int,0 :: Int)
+(negate _ :: Int,id _ :: Int)
+(negate _ :: Int,_ :: Bool)
+(negate _ :: Int,_ :: Char)
+(negate _ :: Int,_ :: [Int])
+(abs _ :: Int,x :: Int)
+(abs _ :: Int,0 :: Int)
+(abs _ :: Int,id _ :: Int)
+(abs _ :: Int,_ :: Bool)
+(abs _ :: Int,_ :: Char)
+(abs _ :: Int,_ :: [Int])
+(_ + _ :: Int,x :: Int)
+(_ + _ :: Int,0 :: Int)
+(_ + _ :: Int,id _ :: Int)
+(_ + _ :: Int,_ :: Bool)
+(_ + _ :: Int,_ :: Char)
+(_ + _ :: Int,_ :: [Int])
+(head _ :: Int,x :: Int)
+(head _ :: Int,0 :: Int)
+(head _ :: Int,id _ :: Int)
+(head _ :: Int,_ :: Bool)
+(head _ :: Int,_ :: Char)
+(head _ :: Int,_ :: [Int])
+(ord _ :: Int,x :: Int)
+(ord _ :: Int,0 :: Int)
+(ord _ :: Int,id _ :: Int)
+(ord _ :: Int,_ :: Bool)
+(ord _ :: Int,_ :: Char)
+(ord _ :: Int,_ :: [Int])
+(p :: Bool,x :: Int)
+(p :: Bool,0 :: Int)
+(p :: Bool,id _ :: Int)
+(p :: Bool,_ :: Bool)
+(p :: Bool,_ :: Char)
+(p :: Bool,_ :: [Int])
+(False :: Bool,x :: Int)
+(False :: Bool,0 :: Int)
+(False :: Bool,id _ :: Int)
+(False :: Bool,_ :: Bool)
+(False :: Bool,_ :: Char)
+(False :: Bool,_ :: [Int])
+(True :: Bool,x :: Int)
+(True :: Bool,0 :: Int)
+(True :: Bool,id _ :: Int)
+(True :: Bool,_ :: Bool)
+(True :: Bool,_ :: Char)
+(True :: Bool,_ :: [Int])
+(not _ :: Bool,x :: Int)
+(not _ :: Bool,0 :: Int)
+(not _ :: Bool,id _ :: Int)
+(not _ :: Bool,_ :: Bool)
+(not _ :: Bool,_ :: Char)
+(not _ :: Bool,_ :: [Int])
+(c :: Char,x :: Int)
+(c :: Char,0 :: Int)
+(c :: Char,id _ :: Int)
+(c :: Char,_ :: Bool)
+(c :: Char,_ :: Char)
+(c :: Char,_ :: [Int])
+('a' :: Char,x :: Int)
+('a' :: Char,0 :: Int)
+('a' :: Char,id _ :: Int)
+('a' :: Char,_ :: Bool)
+('a' :: Char,_ :: Char)
+('a' :: Char,_ :: [Int])
+(xs :: [Int],x :: Int)
+(xs :: [Int],0 :: Int)
+(xs :: [Int],id _ :: Int)
+(xs :: [Int],_ :: Bool)
+(xs :: [Int],_ :: Char)
+(xs :: [Int],_ :: [Int])
+([] :: [Int],x :: Int)
+([] :: [Int],0 :: Int)
+([] :: [Int],id _ :: Int)
+([] :: [Int],_ :: Bool)
+([] :: [Int],_ :: Char)
+([] :: [Int],_ :: [Int])
+(_:_ :: [Int],x :: Int)
+(_:_ :: [Int],0 :: Int)
+(_:_ :: [Int],id _ :: Int)
+(_:_ :: [Int],_ :: Bool)
+(_:_ :: [Int],_ :: Char)
+(_:_ :: [Int],_ :: [Int])
+(id :: Int -> Int,x :: Int)
+(id :: Int -> Int,0 :: Int)
+(id :: Int -> Int,id _ :: Int)
+(id :: Int -> Int,_ :: Bool)
+(id :: Int -> Int,_ :: Char)
+(id :: Int -> Int,_ :: [Int])
+((+) :: Int -> Int -> Int,x :: Int)
+((+) :: Int -> Int -> Int,0 :: Int)
+((+) :: Int -> Int -> Int,id _ :: Int)
+((+) :: Int -> Int -> Int,_ :: Bool)
+((+) :: Int -> Int -> Int,_ :: Char)
+((+) :: Int -> Int -> Int,_ :: [Int])
+(z :: Int,_ :: Int)
+(-1 :: Int,_ :: Int)
+(id y :: Int,_ :: Int)
+(id 1 :: Int,_ :: Int)
+(id (id x) :: Int,_ :: Int)
+(id (id 0) :: Int,_ :: Int)
+(id (id (id _)) :: Int,_ :: Int)
+(id (negate _) :: Int,_ :: Int)
+(id (abs _) :: Int,_ :: Int)
+(id (_ + _) :: Int,_ :: Int)
+(id (head _) :: Int,_ :: Int)
+(id (ord _) :: Int,_ :: Int)
+(negate x :: Int,_ :: Int)
+(negate 0 :: Int,_ :: Int)
+(negate (id _) :: Int,_ :: Int)
+(abs x :: Int,_ :: Int)
+(abs 0 :: Int,_ :: Int)
+(abs (id _) :: Int,_ :: Int)
+(_ + x :: Int,_ :: Int)
+(_ + 0 :: Int,_ :: Int)
+(_ + id _ :: Int,_ :: Int)
+(x + _ :: Int,_ :: Int)
+(0 + _ :: Int,_ :: Int)
+(id _ + _ :: Int,_ :: Int)
+(_ * _ :: Int,_ :: Int)
+(f _ :: Int,_ :: Int)
+(head xs :: Int,_ :: Int)
+(head [] :: Int,_ :: Int)
+(head (_:_) :: Int,_ :: Int)
+(ord c :: Int,_ :: Int)
+(ord 'a' :: Int,_ :: Int)
+(q :: Bool,_ :: Int)
+(not p :: Bool,_ :: Int)
+(not False :: Bool,_ :: Int)
+(not True :: Bool,_ :: Int)
+(not (not _) :: Bool,_ :: Int)
+(_ || _ :: Bool,_ :: Int)
+(d :: Char,_ :: Int)
+(' ' :: Char,_ :: Int)
+(ys :: [Int],_ :: Int)
+([0] :: [Int],_ :: Int)
+(_:xs :: [Int],_ :: Int)
+([_] :: [Int],_ :: Int)
+(_:_:_ :: [Int],_ :: Int)
+(x:_ :: [Int],_ :: Int)
+(0:_ :: [Int],_ :: Int)
+(id _:_ :: [Int],_ :: Int)
+(tail _ :: [Int],_ :: Int)
+(_ ++ _ :: [Int],_ :: Int)
+(negate :: Int -> Int,_ :: Int)
+(abs :: Int -> Int,_ :: Int)
+((_ +) :: Int -> Int,_ :: Int)
+((*) :: Int -> Int -> Int,_ :: Int)
+(not :: Bool -> Bool,_ :: Int)
+((||) :: Bool -> Bool -> Bool,_ :: Int)
+(_ :: Int,x' :: Int)
+(_ :: Int,2 :: Int)
+(_ :: Int,id z :: Int)
+(_ :: Int,id (-1) :: Int)
+(_ :: Int,id (id y) :: Int)
+(_ :: Int,id (id 1) :: Int)
+(_ :: Int,id (id (id x)) :: Int)
+(_ :: Int,id (id (id 0)) :: Int)
+(_ :: Int,id (id (id (id _))) :: Int)
+(_ :: Int,id (id (negate _)) :: Int)
+(_ :: Int,id (id (abs _)) :: Int)
+(_ :: Int,id (id (_ + _)) :: Int)
+(_ :: Int,id (id (head _)) :: Int)
+(_ :: Int,id (id (ord _)) :: Int)
+(_ :: Int,id (negate x) :: Int)
+(_ :: Int,id (negate 0) :: Int)
+(_ :: Int,id (negate (id _)) :: Int)
+(_ :: Int,id (abs x) :: Int)
+(_ :: Int,id (abs 0) :: Int)
+(_ :: Int,id (abs (id _)) :: Int)
+(_ :: Int,id (_ + x) :: Int)
+(_ :: Int,id (_ + 0) :: Int)
+(_ :: Int,id (_ + id _) :: Int)
+(_ :: Int,id (x + _) :: Int)
+(_ :: Int,id (0 + _) :: Int)
+(_ :: Int,id (id _ + _) :: Int)
+(_ :: Int,id (_ * _) :: Int)
+(_ :: Int,id (f _) :: Int)
+(_ :: Int,id (head xs) :: Int)
+(_ :: Int,id (head []) :: Int)
+(_ :: Int,id (head (_:_)) :: Int)
+(_ :: Int,id (ord c) :: Int)
+(_ :: Int,id (ord 'a') :: Int)
+(_ :: Int,negate y :: Int)
+(_ :: Int,negate 1 :: Int)
+(_ :: Int,negate (id x) :: Int)
+(_ :: Int,negate (id 0) :: Int)
+(_ :: Int,negate (id (id _)) :: Int)
+(_ :: Int,negate (negate _) :: Int)
+(_ :: Int,negate (abs _) :: Int)
+(_ :: Int,negate (_ + _) :: Int)
+(_ :: Int,negate (head _) :: Int)
+(_ :: Int,negate (ord _) :: Int)
+(_ :: Int,abs y :: Int)
+(_ :: Int,abs 1 :: Int)
+(_ :: Int,abs (id x) :: Int)
+(_ :: Int,abs (id 0) :: Int)
+(_ :: Int,abs (id (id _)) :: Int)
+(_ :: Int,abs (negate _) :: Int)
+(_ :: Int,abs (abs _) :: Int)
+(_ :: Int,abs (_ + _) :: Int)
+(_ :: Int,abs (head _) :: Int)
+(_ :: Int,abs (ord _) :: Int)
+(_ :: Int,_ + y :: Int)
+(_ :: Int,_ + 1 :: Int)
+(_ :: Int,_ + id x :: Int)
+(_ :: Int,_ + id 0 :: Int)
+(_ :: Int,_ + id (id _) :: Int)
+(_ :: Int,_ + negate _ :: Int)
+(_ :: Int,_ + abs _ :: Int)
+(_ :: Int,_ + (_ + _) :: Int)
+(_ :: Int,_ + head _ :: Int)
+(_ :: Int,_ + ord _ :: Int)
+(_ :: Int,x + x :: Int)
+(_ :: Int,x + 0 :: Int)
+(_ :: Int,x + id _ :: Int)
+(_ :: Int,0 + x :: Int)
+(_ :: Int,0 + 0 :: Int)
+(_ :: Int,0 + id _ :: Int)
+(_ :: Int,id _ + x :: Int)
+(_ :: Int,id _ + 0 :: Int)
+(_ :: Int,id _ + id _ :: Int)
+(_ :: Int,_ * x :: Int)
+(_ :: Int,_ * 0 :: Int)
+(_ :: Int,_ * id _ :: Int)
+(_ :: Int,f x :: Int)
+(_ :: Int,f 0 :: Int)
+(_ :: Int,f (id _) :: Int)
+(_ :: Int,y + _ :: Int)
+(_ :: Int,1 + _ :: Int)
+(_ :: Int,id x + _ :: Int)
+(_ :: Int,id 0 + _ :: Int)
+(_ :: Int,id (id _) + _ :: Int)
+(_ :: Int,negate _ + _ :: Int)
+(_ :: Int,abs _ + _ :: Int)
+(_ :: Int,(_ + _) + _ :: Int)
+(_ :: Int,head _ + _ :: Int)
+(_ :: Int,ord _ + _ :: Int)
+(_ :: Int,x * _ :: Int)
+(_ :: Int,0 * _ :: Int)
+(_ :: Int,id _ * _ :: Int)
+(_ :: Int,g _ :: Int)
+(_ :: Int,head ys :: Int)
+(_ :: Int,head [0] :: Int)
+(_ :: Int,head (_:xs) :: Int)
+(_ :: Int,head [_] :: Int)
+(_ :: Int,head (_:_:_) :: Int)
+(_ :: Int,head (x:_) :: Int)
+(_ :: Int,head (0:_) :: Int)
+(_ :: Int,head (id _:_) :: Int)
+(_ :: Int,head (tail _) :: Int)
+(_ :: Int,head (_ ++ _) :: Int)
+(_ :: Int,ord d :: Int)
+(_ :: Int,ord ' ' :: Int)
+(_ :: Int,r :: Bool)
+(_ :: Int,not q :: Bool)
+(_ :: Int,not (not p) :: Bool)
+(_ :: Int,not (not False) :: Bool)
+(_ :: Int,not (not True) :: Bool)
+(_ :: Int,not (not (not _)) :: Bool)
+(_ :: Int,not (_ || _) :: Bool)
+(_ :: Int,_ || p :: Bool)
+(_ :: Int,_ || False :: Bool)
+(_ :: Int,_ || True :: Bool)
+(_ :: Int,_ || not _ :: Bool)
+(_ :: Int,p || _ :: Bool)
+(_ :: Int,False || _ :: Bool)
+(_ :: Int,True || _ :: Bool)
+(_ :: Int,not _ || _ :: Bool)
+(_ :: Int,_ && _ :: Bool)
+(_ :: Int,_ == _ :: Bool)
+(_ :: Int,_ == _ :: Bool)
+(_ :: Int,odd _ :: Bool)
+(_ :: Int,even _ :: Bool)
+(_ :: Int,elem _ _ :: Bool)
+(_ :: Int,e :: Char)
+(_ :: Int,'b' :: Char)
+(_ :: Int,zs :: [Int])
+(_ :: Int,[0,0] :: [Int])
+(_ :: Int,[1] :: [Int])
+(_ :: Int,_:ys :: [Int])
+(_ :: Int,[_,0] :: [Int])
+(_ :: Int,_:_:xs :: [Int])
+(_ :: Int,[_,_] :: [Int])
+(_ :: Int,_:_:_:_ :: [Int])
+(_ :: Int,_:x:_ :: [Int])
+(_ :: Int,_:0:_ :: [Int])
+(_ :: Int,_:id _:_ :: [Int])
+(_ :: Int,_:tail _ :: [Int])
+(_ :: Int,_:(_ ++ _) :: [Int])
+(_ :: Int,x:xs :: [Int])
+(_ :: Int,[x] :: [Int])
+(_ :: Int,x:_:_ :: [Int])
+(_ :: Int,0:xs :: [Int])
+(_ :: Int,[0] :: [Int])
+(_ :: Int,0:_:_ :: [Int])
+(_ :: Int,id _:xs :: [Int])
+(_ :: Int,[id _] :: [Int])
+(_ :: Int,id _:_:_ :: [Int])
+(_ :: Int,y:_ :: [Int])
+(_ :: Int,1:_ :: [Int])
+(_ :: Int,id x:_ :: [Int])
+(_ :: Int,id 0:_ :: [Int])
+(_ :: Int,id (id _):_ :: [Int])
+(_ :: Int,negate _:_ :: [Int])
+(_ :: Int,abs _:_ :: [Int])
+(_ :: Int,_ + _:_ :: [Int])
+(_ :: Int,head _:_ :: [Int])
+(_ :: Int,ord _:_ :: [Int])
+(_ :: Int,tail xs :: [Int])
+(_ :: Int,tail [] :: [Int])
+(_ :: Int,tail (_:_) :: [Int])
+(_ :: Int,_ ++ xs :: [Int])
+(_ :: Int,_ ++ [] :: [Int])
+(_ :: Int,_ ++ (_:_) :: [Int])
+(_ :: Int,xs ++ _ :: [Int])
+(_ :: Int,[] ++ _ :: [Int])
+(_ :: Int,(_:_) ++ _ :: [Int])
+(_ :: Int,sort _ :: [Int])
+(_ :: Int,insert _ _ :: [Int])
+(_ :: Int,(x +) :: Int -> Int)
+(_ :: Int,(0 +) :: Int -> Int)
+(_ :: Int,(id _ +) :: Int -> Int)
+(_ :: Int,(_ *) :: Int -> Int)
+(_ :: Int,f :: Int -> Int)
+(_ :: Int,(_ ||) :: Bool -> Bool)
+(_ :: Int,(&&) :: Bool -> Bool -> Bool)
+(x :: Int,z :: Int)
+(x :: Int,-1 :: Int)
+(x :: Int,id y :: Int)
+(x :: Int,id 1 :: Int)
+(x :: Int,id (id x) :: Int)
+(x :: Int,id (id 0) :: Int)
+(x :: Int,id (id (id _)) :: Int)
+(x :: Int,id (negate _) :: Int)
+(x :: Int,id (abs _) :: Int)
+(x :: Int,id (_ + _) :: Int)
+(x :: Int,id (head _) :: Int)
+(x :: Int,id (ord _) :: Int)
+(x :: Int,negate x :: Int)
+(x :: Int,negate 0 :: Int)
+(x :: Int,negate (id _) :: Int)
+(x :: Int,abs x :: Int)
+(x :: Int,abs 0 :: Int)
+(x :: Int,abs (id _) :: Int)
+(x :: Int,_ + x :: Int)
+(x :: Int,_ + 0 :: Int)
+(x :: Int,_ + id _ :: Int)
+(x :: Int,x + _ :: Int)
+(x :: Int,0 + _ :: Int)
+(x :: Int,id _ + _ :: Int)
+(x :: Int,_ * _ :: Int)
+(x :: Int,f _ :: Int)
+(x :: Int,head xs :: Int)
+(x :: Int,head [] :: Int)
+(x :: Int,head (_:_) :: Int)
+(x :: Int,ord c :: Int)
+(x :: Int,ord 'a' :: Int)
+(x :: Int,q :: Bool)
+(x :: Int,not p :: Bool)
+(x :: Int,not False :: Bool)
+(x :: Int,not True :: Bool)
+(x :: Int,not (not _) :: Bool)
+(x :: Int,_ || _ :: Bool)
+(x :: Int,d :: Char)
+(x :: Int,' ' :: Char)
+(x :: Int,ys :: [Int])
+(x :: Int,[0] :: [Int])
+(x :: Int,_:xs :: [Int])
+(x :: Int,[_] :: [Int])
+(x :: Int,_:_:_ :: [Int])
+(x :: Int,x:_ :: [Int])
+(x :: Int,0:_ :: [Int])
+(x :: Int,id _:_ :: [Int])
+(x :: Int,tail _ :: [Int])
+(x :: Int,_ ++ _ :: [Int])
+(x :: Int,negate :: Int -> Int)
+(x :: Int,abs :: Int -> Int)
+(x :: Int,(_ +) :: Int -> Int)
+(x :: Int,(*) :: Int -> Int -> Int)
+(x :: Int,not :: Bool -> Bool)
+(x :: Int,(||) :: Bool -> Bool -> Bool)
+(0 :: Int,z :: Int)
+(0 :: Int,-1 :: Int)
+(0 :: Int,id y :: Int)
+(0 :: Int,id 1 :: Int)
+(0 :: Int,id (id x) :: Int)
+(0 :: Int,id (id 0) :: Int)
+(0 :: Int,id (id (id _)) :: Int)
+(0 :: Int,id (negate _) :: Int)
+(0 :: Int,id (abs _) :: Int)
+(0 :: Int,id (_ + _) :: Int)
+(0 :: Int,id (head _) :: Int)
+(0 :: Int,id (ord _) :: Int)
+(0 :: Int,negate x :: Int)
+(0 :: Int,negate 0 :: Int)
+(0 :: Int,negate (id _) :: Int)
+(0 :: Int,abs x :: Int)
+(0 :: Int,abs 0 :: Int)
+(0 :: Int,abs (id _) :: Int)
+(0 :: Int,_ + x :: Int)
+(0 :: Int,_ + 0 :: Int)
+(0 :: Int,_ + id _ :: Int)
+(0 :: Int,x + _ :: Int)
+(0 :: Int,0 + _ :: Int)
+(0 :: Int,id _ + _ :: Int)
+(0 :: Int,_ * _ :: Int)
+(0 :: Int,f _ :: Int)
+(0 :: Int,head xs :: Int)
+(0 :: Int,head [] :: Int)
+(0 :: Int,head (_:_) :: Int)
+(0 :: Int,ord c :: Int)
+(0 :: Int,ord 'a' :: Int)
+(0 :: Int,q :: Bool)
+(0 :: Int,not p :: Bool)
+(0 :: Int,not False :: Bool)
+(0 :: Int,not True :: Bool)
+(0 :: Int,not (not _) :: Bool)
+(0 :: Int,_ || _ :: Bool)
+(0 :: Int,d :: Char)
+(0 :: Int,' ' :: Char)
+(0 :: Int,ys :: [Int])
+(0 :: Int,[0] :: [Int])
+(0 :: Int,_:xs :: [Int])
+(0 :: Int,[_] :: [Int])
+(0 :: Int,_:_:_ :: [Int])
+(0 :: Int,x:_ :: [Int])
+(0 :: Int,0:_ :: [Int])
+(0 :: Int,id _:_ :: [Int])
+(0 :: Int,tail _ :: [Int])
+(0 :: Int,_ ++ _ :: [Int])
+(0 :: Int,negate :: Int -> Int)
+(0 :: Int,abs :: Int -> Int)
+(0 :: Int,(_ +) :: Int -> Int)
+(0 :: Int,(*) :: Int -> Int -> Int)
+(0 :: Int,not :: Bool -> Bool)
+(0 :: Int,(||) :: Bool -> Bool -> Bool)
+(id _ :: Int,z :: Int)
+(id _ :: Int,-1 :: Int)
+(id _ :: Int,id y :: Int)
+(id _ :: Int,id 1 :: Int)
+(id _ :: Int,id (id x) :: Int)
+(id _ :: Int,id (id 0) :: Int)
+(id _ :: Int,id (id (id _)) :: Int)
+(id _ :: Int,id (negate _) :: Int)
+(id _ :: Int,id (abs _) :: Int)
+(id _ :: Int,id (_ + _) :: Int)
+(id _ :: Int,id (head _) :: Int)
+(id _ :: Int,id (ord _) :: Int)
+(id _ :: Int,negate x :: Int)
+(id _ :: Int,negate 0 :: Int)
+(id _ :: Int,negate (id _) :: Int)
+(id _ :: Int,abs x :: Int)
+(id _ :: Int,abs 0 :: Int)
+(id _ :: Int,abs (id _) :: Int)
+(id _ :: Int,_ + x :: Int)
+(id _ :: Int,_ + 0 :: Int)
+(id _ :: Int,_ + id _ :: Int)
+(id _ :: Int,x + _ :: Int)
+(id _ :: Int,0 + _ :: Int)
+(id _ :: Int,id _ + _ :: Int)
+(id _ :: Int,_ * _ :: Int)
+(id _ :: Int,f _ :: Int)
+(id _ :: Int,head xs :: Int)
+(id _ :: Int,head [] :: Int)
+(id _ :: Int,head (_:_) :: Int)
+(id _ :: Int,ord c :: Int)
+(id _ :: Int,ord 'a' :: Int)
+(id _ :: Int,q :: Bool)
+(id _ :: Int,not p :: Bool)
+(id _ :: Int,not False :: Bool)
+(id _ :: Int,not True :: Bool)
+(id _ :: Int,not (not _) :: Bool)
+(id _ :: Int,_ || _ :: Bool)
+(id _ :: Int,d :: Char)
+(id _ :: Int,' ' :: Char)
+(id _ :: Int,ys :: [Int])
+(id _ :: Int,[0] :: [Int])
+(id _ :: Int,_:xs :: [Int])
+(id _ :: Int,[_] :: [Int])
+(id _ :: Int,_:_:_ :: [Int])
+(id _ :: Int,x:_ :: [Int])
+(id _ :: Int,0:_ :: [Int])
+(id _ :: Int,id _:_ :: [Int])
+(id _ :: Int,tail _ :: [Int])
+(id _ :: Int,_ ++ _ :: [Int])
+(id _ :: Int,negate :: Int -> Int)
+(id _ :: Int,abs :: Int -> Int)
+(id _ :: Int,(_ +) :: Int -> Int)
+(id _ :: Int,(*) :: Int -> Int -> Int)
+(id _ :: Int,not :: Bool -> Bool)
+(id _ :: Int,(||) :: Bool -> Bool -> Bool)
+(_ :: Bool,z :: Int)
+(_ :: Bool,-1 :: Int)
+(_ :: Bool,id y :: Int)
+(_ :: Bool,id 1 :: Int)
+(_ :: Bool,id (id x) :: Int)
+(_ :: Bool,id (id 0) :: Int)
+(_ :: Bool,id (id (id _)) :: Int)
+(_ :: Bool,id (negate _) :: Int)
+(_ :: Bool,id (abs _) :: Int)
+(_ :: Bool,id (_ + _) :: Int)
+(_ :: Bool,id (head _) :: Int)
+(_ :: Bool,id (ord _) :: Int)
+(_ :: Bool,negate x :: Int)
+(_ :: Bool,negate 0 :: Int)
+(_ :: Bool,negate (id _) :: Int)
+(_ :: Bool,abs x :: Int)
+(_ :: Bool,abs 0 :: Int)
+(_ :: Bool,abs (id _) :: Int)
+(_ :: Bool,_ + x :: Int)
+(_ :: Bool,_ + 0 :: Int)
+(_ :: Bool,_ + id _ :: Int)
+(_ :: Bool,x + _ :: Int)
+(_ :: Bool,0 + _ :: Int)
+(_ :: Bool,id _ + _ :: Int)
+(_ :: Bool,_ * _ :: Int)
+(_ :: Bool,f _ :: Int)
+(_ :: Bool,head xs :: Int)
+(_ :: Bool,head [] :: Int)
+(_ :: Bool,head (_:_) :: Int)
+(_ :: Bool,ord c :: Int)
+(_ :: Bool,ord 'a' :: Int)
+(_ :: Bool,q :: Bool)
+(_ :: Bool,not p :: Bool)
+(_ :: Bool,not False :: Bool)
+(_ :: Bool,not True :: Bool)
+(_ :: Bool,not (not _) :: Bool)
+(_ :: Bool,_ || _ :: Bool)
+(_ :: Bool,d :: Char)
+(_ :: Bool,' ' :: Char)
+(_ :: Bool,ys :: [Int])
+(_ :: Bool,[0] :: [Int])
+(_ :: Bool,_:xs :: [Int])
+(_ :: Bool,[_] :: [Int])
+(_ :: Bool,_:_:_ :: [Int])
+(_ :: Bool,x:_ :: [Int])
+(_ :: Bool,0:_ :: [Int])
+(_ :: Bool,id _:_ :: [Int])
+(_ :: Bool,tail _ :: [Int])
+(_ :: Bool,_ ++ _ :: [Int])
+(_ :: Bool,negate :: Int -> Int)
+(_ :: Bool,abs :: Int -> Int)
+(_ :: Bool,(_ +) :: Int -> Int)
+(_ :: Bool,(*) :: Int -> Int -> Int)
+(_ :: Bool,not :: Bool -> Bool)
+(_ :: Bool,(||) :: Bool -> Bool -> Bool)
+(_ :: Char,z :: Int)
+(_ :: Char,-1 :: Int)
+(_ :: Char,id y :: Int)
+(_ :: Char,id 1 :: Int)
+(_ :: Char,id (id x) :: Int)
+(_ :: Char,id (id 0) :: Int)
+(_ :: Char,id (id (id _)) :: Int)
+(_ :: Char,id (negate _) :: Int)
+(_ :: Char,id (abs _) :: Int)
+(_ :: Char,id (_ + _) :: Int)
+(_ :: Char,id (head _) :: Int)
+(_ :: Char,id (ord _) :: Int)
+(_ :: Char,negate x :: Int)
+(_ :: Char,negate 0 :: Int)
+(_ :: Char,negate (id _) :: Int)
+(_ :: Char,abs x :: Int)
+(_ :: Char,abs 0 :: Int)
+(_ :: Char,abs (id _) :: Int)
+(_ :: Char,_ + x :: Int)
+(_ :: Char,_ + 0 :: Int)
+(_ :: Char,_ + id _ :: Int)
+(_ :: Char,x + _ :: Int)
+(_ :: Char,0 + _ :: Int)
+(_ :: Char,id _ + _ :: Int)
+(_ :: Char,_ * _ :: Int)
+(_ :: Char,f _ :: Int)
+(_ :: Char,head xs :: Int)
+(_ :: Char,head [] :: Int)
+(_ :: Char,head (_:_) :: Int)
+(_ :: Char,ord c :: Int)
+(_ :: Char,ord 'a' :: Int)
+(_ :: Char,q :: Bool)
+(_ :: Char,not p :: Bool)
+(_ :: Char,not False :: Bool)
+(_ :: Char,not True :: Bool)
+(_ :: Char,not (not _) :: Bool)
+(_ :: Char,_ || _ :: Bool)
+(_ :: Char,d :: Char)
+(_ :: Char,' ' :: Char)
+(_ :: Char,ys :: [Int])
+(_ :: Char,[0] :: [Int])
+(_ :: Char,_:xs :: [Int])
+(_ :: Char,[_] :: [Int])
+(_ :: Char,_:_:_ :: [Int])
+(_ :: Char,x:_ :: [Int])
+(_ :: Char,0:_ :: [Int])
+(_ :: Char,id _:_ :: [Int])
+(_ :: Char,tail _ :: [Int])
+(_ :: Char,_ ++ _ :: [Int])
+(_ :: Char,negate :: Int -> Int)
+(_ :: Char,abs :: Int -> Int)
+(_ :: Char,(_ +) :: Int -> Int)
+(_ :: Char,(*) :: Int -> Int -> Int)
+(_ :: Char,not :: Bool -> Bool)
+(_ :: Char,(||) :: Bool -> Bool -> Bool)
+(_ :: [Int],z :: Int)
+(_ :: [Int],-1 :: Int)
+(_ :: [Int],id y :: Int)
+(_ :: [Int],id 1 :: Int)
+(_ :: [Int],id (id x) :: Int)
+(_ :: [Int],id (id 0) :: Int)
+(_ :: [Int],id (id (id _)) :: Int)
+(_ :: [Int],id (negate _) :: Int)
+(_ :: [Int],id (abs _) :: Int)
+(_ :: [Int],id (_ + _) :: Int)
+(_ :: [Int],id (head _) :: Int)
+(_ :: [Int],id (ord _) :: Int)
+(_ :: [Int],negate x :: Int)
+(_ :: [Int],negate 0 :: Int)
+(_ :: [Int],negate (id _) :: Int)
+(_ :: [Int],abs x :: Int)
+(_ :: [Int],abs 0 :: Int)
+(_ :: [Int],abs (id _) :: Int)
+(_ :: [Int],_ + x :: Int)
+(_ :: [Int],_ + 0 :: Int)
+(_ :: [Int],_ + id _ :: Int)
+(_ :: [Int],x + _ :: Int)
+(_ :: [Int],0 + _ :: Int)
+(_ :: [Int],id _ + _ :: Int)
+(_ :: [Int],_ * _ :: Int)
+(_ :: [Int],f _ :: Int)
+(_ :: [Int],head xs :: Int)
+(_ :: [Int],head [] :: Int)
+(_ :: [Int],head (_:_) :: Int)
+(_ :: [Int],ord c :: Int)
+(_ :: [Int],ord 'a' :: Int)
+(_ :: [Int],q :: Bool)
+(_ :: [Int],not p :: Bool)
+(_ :: [Int],not False :: Bool)
+(_ :: [Int],not True :: Bool)
+(_ :: [Int],not (not _) :: Bool)
+(_ :: [Int],_ || _ :: Bool)
+(_ :: [Int],d :: Char)
+(_ :: [Int],' ' :: Char)
+(_ :: [Int],ys :: [Int])
+(_ :: [Int],[0] :: [Int])
+(_ :: [Int],_:xs :: [Int])
+(_ :: [Int],[_] :: [Int])
+(_ :: [Int],_:_:_ :: [Int])
+(_ :: [Int],x:_ :: [Int])
+(_ :: [Int],0:_ :: [Int])
+(_ :: [Int],id _:_ :: [Int])
+(_ :: [Int],tail _ :: [Int])
+(_ :: [Int],_ ++ _ :: [Int])
+(_ :: [Int],negate :: Int -> Int)
+(_ :: [Int],abs :: Int -> Int)
+(_ :: [Int],(_ +) :: Int -> Int)
+(_ :: [Int],(*) :: Int -> Int -> Int)
+(_ :: [Int],not :: Bool -> Bool)
+(_ :: [Int],(||) :: Bool -> Bool -> Bool)
+(y :: Int,y :: Int)
+(y :: Int,1 :: Int)
+(y :: Int,id x :: Int)
+(y :: Int,id 0 :: Int)
+(y :: Int,id (id _) :: Int)
+(y :: Int,negate _ :: Int)
+(y :: Int,abs _ :: Int)
+(y :: Int,_ + _ :: Int)
+(y :: Int,head _ :: Int)
+(y :: Int,ord _ :: Int)
+(y :: Int,p :: Bool)
+(y :: Int,False :: Bool)
+(y :: Int,True :: Bool)
+(y :: Int,not _ :: Bool)
+(y :: Int,c :: Char)
+(y :: Int,'a' :: Char)
+(y :: Int,xs :: [Int])
+(y :: Int,[] :: [Int])
+(y :: Int,_:_ :: [Int])
+(y :: Int,id :: Int -> Int)
+(y :: Int,(+) :: Int -> Int -> Int)
+(1 :: Int,y :: Int)
+(1 :: Int,1 :: Int)
+(1 :: Int,id x :: Int)
+(1 :: Int,id 0 :: Int)
+(1 :: Int,id (id _) :: Int)
+(1 :: Int,negate _ :: Int)
+(1 :: Int,abs _ :: Int)
+(1 :: Int,_ + _ :: Int)
+(1 :: Int,head _ :: Int)
+(1 :: Int,ord _ :: Int)
+(1 :: Int,p :: Bool)
+(1 :: Int,False :: Bool)
+(1 :: Int,True :: Bool)
+(1 :: Int,not _ :: Bool)
+(1 :: Int,c :: Char)
+(1 :: Int,'a' :: Char)
+(1 :: Int,xs :: [Int])
+(1 :: Int,[] :: [Int])
+(1 :: Int,_:_ :: [Int])
+
diff --git a/test/model/bench/tiers.out b/test/model/bench/tiers.out
new file mode 100644
--- /dev/null
+++ b/test/model/bench/tiers.out
@@ -0,0 +1,3624 @@
+isNub (list :: [Expr])  =  True
+map length (tiers :: [[ Expr ]])  =  [1,6,21,55,177,674,2679,11128,47899,...]
+tiers :: [[ Expr ]]  =
+  [ [_ :: Int]
+  , [ x :: Int
+    , 0 :: Int
+    , id _ :: Int
+    , _ :: Bool
+    , _ :: Char
+    , _ :: [Int]
+    ]
+  , [ y :: Int
+    , 1 :: Int
+    , id x :: Int
+    , id 0 :: Int
+    , id (id _) :: Int
+    , negate _ :: Int
+    , abs _ :: Int
+    , _ + _ :: Int
+    , head _ :: Int
+    , ord _ :: Int
+    , p :: Bool
+    , False :: Bool
+    , True :: Bool
+    , not _ :: Bool
+    , c :: Char
+    , 'a' :: Char
+    , xs :: [Int]
+    , [] :: [Int]
+    , _:_ :: [Int]
+    , id :: Int -> Int
+    , (+) :: Int -> Int -> Int
+    ]
+  , [ z :: Int
+    , -1 :: Int
+    , id y :: Int
+    , id 1 :: Int
+    , id (id x) :: Int
+    , id (id 0) :: Int
+    , id (id (id _)) :: Int
+    , id (negate _) :: Int
+    , id (abs _) :: Int
+    , id (_ + _) :: Int
+    , id (head _) :: Int
+    , id (ord _) :: Int
+    , negate x :: Int
+    , negate 0 :: Int
+    , negate (id _) :: Int
+    , abs x :: Int
+    , abs 0 :: Int
+    , abs (id _) :: Int
+    , _ + x :: Int
+    , _ + 0 :: Int
+    , _ + id _ :: Int
+    , x + _ :: Int
+    , 0 + _ :: Int
+    , id _ + _ :: Int
+    , _ * _ :: Int
+    , f _ :: Int
+    , head xs :: Int
+    , head [] :: Int
+    , head (_:_) :: Int
+    , ord c :: Int
+    , ord 'a' :: Int
+    , q :: Bool
+    , not p :: Bool
+    , not False :: Bool
+    , not True :: Bool
+    , not (not _) :: Bool
+    , _ || _ :: Bool
+    , d :: Char
+    , ' ' :: Char
+    , ys :: [Int]
+    , [0] :: [Int]
+    , _:xs :: [Int]
+    , [_] :: [Int]
+    , _:_:_ :: [Int]
+    , x:_ :: [Int]
+    , 0:_ :: [Int]
+    , id _:_ :: [Int]
+    , tail _ :: [Int]
+    , _ ++ _ :: [Int]
+    , negate :: Int -> Int
+    , abs :: Int -> Int
+    , (_ +) :: Int -> Int
+    , (*) :: Int -> Int -> Int
+    , not :: Bool -> Bool
+    , (||) :: Bool -> Bool -> Bool
+    ]
+  , [ x' :: Int
+    , 2 :: Int
+    , id z :: Int
+    , id (-1) :: Int
+    , id (id y) :: Int
+    , id (id 1) :: Int
+    , id (id (id x)) :: Int
+    , id (id (id 0)) :: Int
+    , id (id (id (id _))) :: Int
+    , id (id (negate _)) :: Int
+    , id (id (abs _)) :: Int
+    , id (id (_ + _)) :: Int
+    , id (id (head _)) :: Int
+    , id (id (ord _)) :: Int
+    , id (negate x) :: Int
+    , id (negate 0) :: Int
+    , id (negate (id _)) :: Int
+    , id (abs x) :: Int
+    , id (abs 0) :: Int
+    , id (abs (id _)) :: Int
+    , id (_ + x) :: Int
+    , id (_ + 0) :: Int
+    , id (_ + id _) :: Int
+    , id (x + _) :: Int
+    , id (0 + _) :: Int
+    , id (id _ + _) :: Int
+    , id (_ * _) :: Int
+    , id (f _) :: Int
+    , id (head xs) :: Int
+    , id (head []) :: Int
+    , id (head (_:_)) :: Int
+    , id (ord c) :: Int
+    , id (ord 'a') :: Int
+    , negate y :: Int
+    , negate 1 :: Int
+    , negate (id x) :: Int
+    , negate (id 0) :: Int
+    , negate (id (id _)) :: Int
+    , negate (negate _) :: Int
+    , negate (abs _) :: Int
+    , negate (_ + _) :: Int
+    , negate (head _) :: Int
+    , negate (ord _) :: Int
+    , abs y :: Int
+    , abs 1 :: Int
+    , abs (id x) :: Int
+    , abs (id 0) :: Int
+    , abs (id (id _)) :: Int
+    , abs (negate _) :: Int
+    , abs (abs _) :: Int
+    , abs (_ + _) :: Int
+    , abs (head _) :: Int
+    , abs (ord _) :: Int
+    , _ + y :: Int
+    , _ + 1 :: Int
+    , _ + id x :: Int
+    , _ + id 0 :: Int
+    , _ + id (id _) :: Int
+    , _ + negate _ :: Int
+    , _ + abs _ :: Int
+    , _ + (_ + _) :: Int
+    , _ + head _ :: Int
+    , _ + ord _ :: Int
+    , x + x :: Int
+    , x + 0 :: Int
+    , x + id _ :: Int
+    , 0 + x :: Int
+    , 0 + 0 :: Int
+    , 0 + id _ :: Int
+    , id _ + x :: Int
+    , id _ + 0 :: Int
+    , id _ + id _ :: Int
+    , _ * x :: Int
+    , _ * 0 :: Int
+    , _ * id _ :: Int
+    , f x :: Int
+    , f 0 :: Int
+    , f (id _) :: Int
+    , y + _ :: Int
+    , 1 + _ :: Int
+    , id x + _ :: Int
+    , id 0 + _ :: Int
+    , id (id _) + _ :: Int
+    , negate _ + _ :: Int
+    , abs _ + _ :: Int
+    , (_ + _) + _ :: Int
+    , head _ + _ :: Int
+    , ord _ + _ :: Int
+    , x * _ :: Int
+    , 0 * _ :: Int
+    , id _ * _ :: Int
+    , g _ :: Int
+    , head ys :: Int
+    , head [0] :: Int
+    , head (_:xs) :: Int
+    , head [_] :: Int
+    , head (_:_:_) :: Int
+    , head (x:_) :: Int
+    , head (0:_) :: Int
+    , head (id _:_) :: Int
+    , head (tail _) :: Int
+    , head (_ ++ _) :: Int
+    , ord d :: Int
+    , ord ' ' :: Int
+    , r :: Bool
+    , not q :: Bool
+    , not (not p) :: Bool
+    , not (not False) :: Bool
+    , not (not True) :: Bool
+    , not (not (not _)) :: Bool
+    , not (_ || _) :: Bool
+    , _ || p :: Bool
+    , _ || False :: Bool
+    , _ || True :: Bool
+    , _ || not _ :: Bool
+    , p || _ :: Bool
+    , False || _ :: Bool
+    , True || _ :: Bool
+    , not _ || _ :: Bool
+    , _ && _ :: Bool
+    , _ == _ :: Bool
+    , _ == _ :: Bool
+    , odd _ :: Bool
+    , even _ :: Bool
+    , elem _ _ :: Bool
+    , e :: Char
+    , 'b' :: Char
+    , zs :: [Int]
+    , [0,0] :: [Int]
+    , [1] :: [Int]
+    , _:ys :: [Int]
+    , [_,0] :: [Int]
+    , _:_:xs :: [Int]
+    , [_,_] :: [Int]
+    , _:_:_:_ :: [Int]
+    , _:x:_ :: [Int]
+    , _:0:_ :: [Int]
+    , _:id _:_ :: [Int]
+    , _:tail _ :: [Int]
+    , _:(_ ++ _) :: [Int]
+    , x:xs :: [Int]
+    , [x] :: [Int]
+    , x:_:_ :: [Int]
+    , 0:xs :: [Int]
+    , [0] :: [Int]
+    , 0:_:_ :: [Int]
+    , id _:xs :: [Int]
+    , [id _] :: [Int]
+    , id _:_:_ :: [Int]
+    , y:_ :: [Int]
+    , 1:_ :: [Int]
+    , id x:_ :: [Int]
+    , id 0:_ :: [Int]
+    , id (id _):_ :: [Int]
+    , negate _:_ :: [Int]
+    , abs _:_ :: [Int]
+    , _ + _:_ :: [Int]
+    , head _:_ :: [Int]
+    , ord _:_ :: [Int]
+    , tail xs :: [Int]
+    , tail [] :: [Int]
+    , tail (_:_) :: [Int]
+    , _ ++ xs :: [Int]
+    , _ ++ [] :: [Int]
+    , _ ++ (_:_) :: [Int]
+    , xs ++ _ :: [Int]
+    , [] ++ _ :: [Int]
+    , (_:_) ++ _ :: [Int]
+    , sort _ :: [Int]
+    , insert _ _ :: [Int]
+    , (x +) :: Int -> Int
+    , (0 +) :: Int -> Int
+    , (id _ +) :: Int -> Int
+    , (_ *) :: Int -> Int
+    , f :: Int -> Int
+    , (_ ||) :: Bool -> Bool
+    , (&&) :: Bool -> Bool -> Bool
+    ]
+  , [ y' :: Int
+    , -2 :: Int
+    , id x' :: Int
+    , id 2 :: Int
+    , id (id z) :: Int
+    , id (id (-1)) :: Int
+    , id (id (id y)) :: Int
+    , id (id (id 1)) :: Int
+    , id (id (id (id x))) :: Int
+    , id (id (id (id 0))) :: Int
+    , id (id (id (id (id _)))) :: Int
+    , id (id (id (negate _))) :: Int
+    , id (id (id (abs _))) :: Int
+    , id (id (id (_ + _))) :: Int
+    , id (id (id (head _))) :: Int
+    , id (id (id (ord _))) :: Int
+    , id (id (negate x)) :: Int
+    , id (id (negate 0)) :: Int
+    , id (id (negate (id _))) :: Int
+    , id (id (abs x)) :: Int
+    , id (id (abs 0)) :: Int
+    , id (id (abs (id _))) :: Int
+    , id (id (_ + x)) :: Int
+    , id (id (_ + 0)) :: Int
+    , id (id (_ + id _)) :: Int
+    , id (id (x + _)) :: Int
+    , id (id (0 + _)) :: Int
+    , id (id (id _ + _)) :: Int
+    , id (id (_ * _)) :: Int
+    , id (id (f _)) :: Int
+    , id (id (head xs)) :: Int
+    , id (id (head [])) :: Int
+    , id (id (head (_:_))) :: Int
+    , id (id (ord c)) :: Int
+    , id (id (ord 'a')) :: Int
+    , id (negate y) :: Int
+    , id (negate 1) :: Int
+    , id (negate (id x)) :: Int
+    , id (negate (id 0)) :: Int
+    , id (negate (id (id _))) :: Int
+    , id (negate (negate _)) :: Int
+    , id (negate (abs _)) :: Int
+    , id (negate (_ + _)) :: Int
+    , id (negate (head _)) :: Int
+    , id (negate (ord _)) :: Int
+    , id (abs y) :: Int
+    , id (abs 1) :: Int
+    , id (abs (id x)) :: Int
+    , id (abs (id 0)) :: Int
+    , id (abs (id (id _))) :: Int
+    , id (abs (negate _)) :: Int
+    , id (abs (abs _)) :: Int
+    , id (abs (_ + _)) :: Int
+    , id (abs (head _)) :: Int
+    , id (abs (ord _)) :: Int
+    , id (_ + y) :: Int
+    , id (_ + 1) :: Int
+    , id (_ + id x) :: Int
+    , id (_ + id 0) :: Int
+    , id (_ + id (id _)) :: Int
+    , id (_ + negate _) :: Int
+    , id (_ + abs _) :: Int
+    , id (_ + (_ + _)) :: Int
+    , id (_ + head _) :: Int
+    , id (_ + ord _) :: Int
+    , id (x + x) :: Int
+    , id (x + 0) :: Int
+    , id (x + id _) :: Int
+    , id (0 + x) :: Int
+    , id (0 + 0) :: Int
+    , id (0 + id _) :: Int
+    , id (id _ + x) :: Int
+    , id (id _ + 0) :: Int
+    , id (id _ + id _) :: Int
+    , id (_ * x) :: Int
+    , id (_ * 0) :: Int
+    , id (_ * id _) :: Int
+    , id (f x) :: Int
+    , id (f 0) :: Int
+    , id (f (id _)) :: Int
+    , id (y + _) :: Int
+    , id (1 + _) :: Int
+    , id (id x + _) :: Int
+    , id (id 0 + _) :: Int
+    , id (id (id _) + _) :: Int
+    , id (negate _ + _) :: Int
+    , id (abs _ + _) :: Int
+    , id ((_ + _) + _) :: Int
+    , id (head _ + _) :: Int
+    , id (ord _ + _) :: Int
+    , id (x * _) :: Int
+    , id (0 * _) :: Int
+    , id (id _ * _) :: Int
+    , id (g _) :: Int
+    , id (head ys) :: Int
+    , id (head [0]) :: Int
+    , id (head (_:xs)) :: Int
+    , id (head [_]) :: Int
+    , id (head (_:_:_)) :: Int
+    , id (head (x:_)) :: Int
+    , id (head (0:_)) :: Int
+    , id (head (id _:_)) :: Int
+    , id (head (tail _)) :: Int
+    , id (head (_ ++ _)) :: Int
+    , id (ord d) :: Int
+    , id (ord ' ') :: Int
+    , negate z :: Int
+    , negate (-1) :: Int
+    , negate (id y) :: Int
+    , negate (id 1) :: Int
+    , negate (id (id x)) :: Int
+    , negate (id (id 0)) :: Int
+    , negate (id (id (id _))) :: Int
+    , negate (id (negate _)) :: Int
+    , negate (id (abs _)) :: Int
+    , negate (id (_ + _)) :: Int
+    , negate (id (head _)) :: Int
+    , negate (id (ord _)) :: Int
+    , negate (negate x) :: Int
+    , negate (negate 0) :: Int
+    , negate (negate (id _)) :: Int
+    , negate (abs x) :: Int
+    , negate (abs 0) :: Int
+    , negate (abs (id _)) :: Int
+    , negate (_ + x) :: Int
+    , negate (_ + 0) :: Int
+    , negate (_ + id _) :: Int
+    , negate (x + _) :: Int
+    , negate (0 + _) :: Int
+    , negate (id _ + _) :: Int
+    , negate (_ * _) :: Int
+    , negate (f _) :: Int
+    , negate (head xs) :: Int
+    , negate (head []) :: Int
+    , negate (head (_:_)) :: Int
+    , negate (ord c) :: Int
+    , negate (ord 'a') :: Int
+    , abs z :: Int
+    , abs (-1) :: Int
+    , abs (id y) :: Int
+    , abs (id 1) :: Int
+    , abs (id (id x)) :: Int
+    , abs (id (id 0)) :: Int
+    , abs (id (id (id _))) :: Int
+    , abs (id (negate _)) :: Int
+    , abs (id (abs _)) :: Int
+    , abs (id (_ + _)) :: Int
+    , abs (id (head _)) :: Int
+    , abs (id (ord _)) :: Int
+    , abs (negate x) :: Int
+    , abs (negate 0) :: Int
+    , abs (negate (id _)) :: Int
+    , abs (abs x) :: Int
+    , abs (abs 0) :: Int
+    , abs (abs (id _)) :: Int
+    , abs (_ + x) :: Int
+    , abs (_ + 0) :: Int
+    , abs (_ + id _) :: Int
+    , abs (x + _) :: Int
+    , abs (0 + _) :: Int
+    , abs (id _ + _) :: Int
+    , abs (_ * _) :: Int
+    , abs (f _) :: Int
+    , abs (head xs) :: Int
+    , abs (head []) :: Int
+    , abs (head (_:_)) :: Int
+    , abs (ord c) :: Int
+    , abs (ord 'a') :: Int
+    , _ + z :: Int
+    , _ + (-1) :: Int
+    , _ + id y :: Int
+    , _ + id 1 :: Int
+    , _ + id (id x) :: Int
+    , _ + id (id 0) :: Int
+    , _ + id (id (id _)) :: Int
+    , _ + id (negate _) :: Int
+    , _ + id (abs _) :: Int
+    , _ + id (_ + _) :: Int
+    , _ + id (head _) :: Int
+    , _ + id (ord _) :: Int
+    , _ + negate x :: Int
+    , _ + negate 0 :: Int
+    , _ + negate (id _) :: Int
+    , _ + abs x :: Int
+    , _ + abs 0 :: Int
+    , _ + abs (id _) :: Int
+    , _ + (_ + x) :: Int
+    , _ + (_ + 0) :: Int
+    , _ + (_ + id _) :: Int
+    , _ + (x + _) :: Int
+    , _ + (0 + _) :: Int
+    , _ + (id _ + _) :: Int
+    , _ + _ * _ :: Int
+    , _ + f _ :: Int
+    , _ + head xs :: Int
+    , _ + head [] :: Int
+    , _ + head (_:_) :: Int
+    , _ + ord c :: Int
+    , _ + ord 'a' :: Int
+    , x + y :: Int
+    , x + 1 :: Int
+    , x + id x :: Int
+    , x + id 0 :: Int
+    , x + id (id _) :: Int
+    , x + negate _ :: Int
+    , x + abs _ :: Int
+    , x + (_ + _) :: Int
+    , x + head _ :: Int
+    , x + ord _ :: Int
+    , 0 + y :: Int
+    , 0 + 1 :: Int
+    , 0 + id x :: Int
+    , 0 + id 0 :: Int
+    , 0 + id (id _) :: Int
+    , 0 + negate _ :: Int
+    , 0 + abs _ :: Int
+    , 0 + (_ + _) :: Int
+    , 0 + head _ :: Int
+    , 0 + ord _ :: Int
+    , id _ + y :: Int
+    , id _ + 1 :: Int
+    , id _ + id x :: Int
+    , id _ + id 0 :: Int
+    , id _ + id (id _) :: Int
+    , id _ + negate _ :: Int
+    , id _ + abs _ :: Int
+    , id _ + (_ + _) :: Int
+    , id _ + head _ :: Int
+    , id _ + ord _ :: Int
+    , _ * y :: Int
+    , _ * 1 :: Int
+    , _ * id x :: Int
+    , _ * id 0 :: Int
+    , _ * id (id _) :: Int
+    , _ * negate _ :: Int
+    , _ * abs _ :: Int
+    , _ * (_ + _) :: Int
+    , _ * head _ :: Int
+    , _ * ord _ :: Int
+    , f y :: Int
+    , f 1 :: Int
+    , f (id x) :: Int
+    , f (id 0) :: Int
+    , f (id (id _)) :: Int
+    , f (negate _) :: Int
+    , f (abs _) :: Int
+    , f (_ + _) :: Int
+    , f (head _) :: Int
+    , f (ord _) :: Int
+    , y + x :: Int
+    , y + 0 :: Int
+    , y + id _ :: Int
+    , 1 + x :: Int
+    , 1 + 0 :: Int
+    , 1 + id _ :: Int
+    , id x + x :: Int
+    , id x + 0 :: Int
+    , id x + id _ :: Int
+    , id 0 + x :: Int
+    , id 0 + 0 :: Int
+    , id 0 + id _ :: Int
+    , id (id _) + x :: Int
+    , id (id _) + 0 :: Int
+    , id (id _) + id _ :: Int
+    , negate _ + x :: Int
+    , negate _ + 0 :: Int
+    , negate _ + id _ :: Int
+    , abs _ + x :: Int
+    , abs _ + 0 :: Int
+    , abs _ + id _ :: Int
+    , (_ + _) + x :: Int
+    , (_ + _) + 0 :: Int
+    , (_ + _) + id _ :: Int
+    , head _ + x :: Int
+    , head _ + 0 :: Int
+    , head _ + id _ :: Int
+    , ord _ + x :: Int
+    , ord _ + 0 :: Int
+    , ord _ + id _ :: Int
+    , x * x :: Int
+    , x * 0 :: Int
+    , x * id _ :: Int
+    , 0 * x :: Int
+    , 0 * 0 :: Int
+    , 0 * id _ :: Int
+    , id _ * x :: Int
+    , id _ * 0 :: Int
+    , id _ * id _ :: Int
+    , g x :: Int
+    , g 0 :: Int
+    , g (id _) :: Int
+    , z + _ :: Int
+    , (-1) + _ :: Int
+    , id y + _ :: Int
+    , id 1 + _ :: Int
+    , id (id x) + _ :: Int
+    , id (id 0) + _ :: Int
+    , id (id (id _)) + _ :: Int
+    , id (negate _) + _ :: Int
+    , id (abs _) + _ :: Int
+    , id (_ + _) + _ :: Int
+    , id (head _) + _ :: Int
+    , id (ord _) + _ :: Int
+    , negate x + _ :: Int
+    , negate 0 + _ :: Int
+    , negate (id _) + _ :: Int
+    , abs x + _ :: Int
+    , abs 0 + _ :: Int
+    , abs (id _) + _ :: Int
+    , (_ + x) + _ :: Int
+    , (_ + 0) + _ :: Int
+    , (_ + id _) + _ :: Int
+    , (x + _) + _ :: Int
+    , (0 + _) + _ :: Int
+    , (id _ + _) + _ :: Int
+    , _ * _ + _ :: Int
+    , f _ + _ :: Int
+    , head xs + _ :: Int
+    , head [] + _ :: Int
+    , head (_:_) + _ :: Int
+    , ord c + _ :: Int
+    , ord 'a' + _ :: Int
+    , y * _ :: Int
+    , 1 * _ :: Int
+    , id x * _ :: Int
+    , id 0 * _ :: Int
+    , id (id _) * _ :: Int
+    , negate _ * _ :: Int
+    , abs _ * _ :: Int
+    , (_ + _) * _ :: Int
+    , head _ * _ :: Int
+    , ord _ * _ :: Int
+    , h _ :: Int
+    , head zs :: Int
+    , head [0,0] :: Int
+    , head [1] :: Int
+    , head (_:ys) :: Int
+    , head [_,0] :: Int
+    , head (_:_:xs) :: Int
+    , head [_,_] :: Int
+    , head (_:_:_:_) :: Int
+    , head (_:x:_) :: Int
+    , head (_:0:_) :: Int
+    , head (_:id _:_) :: Int
+    , head (_:tail _) :: Int
+    , head (_:(_ ++ _)) :: Int
+    , head (x:xs) :: Int
+    , head [x] :: Int
+    , head (x:_:_) :: Int
+    , head (0:xs) :: Int
+    , head [0] :: Int
+    , head (0:_:_) :: Int
+    , head (id _:xs) :: Int
+    , head [id _] :: Int
+    , head (id _:_:_) :: Int
+    , head (y:_) :: Int
+    , head (1:_) :: Int
+    , head (id x:_) :: Int
+    , head (id 0:_) :: Int
+    , head (id (id _):_) :: Int
+    , head (negate _:_) :: Int
+    , head (abs _:_) :: Int
+    , head (_ + _:_) :: Int
+    , head (head _:_) :: Int
+    , head (ord _:_) :: Int
+    , head (tail xs) :: Int
+    , head (tail []) :: Int
+    , head (tail (_:_)) :: Int
+    , head (_ ++ xs) :: Int
+    , head (_ ++ []) :: Int
+    , head (_ ++ (_:_)) :: Int
+    , head (xs ++ _) :: Int
+    , head ([] ++ _) :: Int
+    , head ((_:_) ++ _) :: Int
+    , head (sort _) :: Int
+    , head (insert _ _) :: Int
+    , ord e :: Int
+    , ord 'b' :: Int
+    , p' :: Bool
+    , not r :: Bool
+    , not (not q) :: Bool
+    , not (not (not p)) :: Bool
+    , not (not (not False)) :: Bool
+    , not (not (not True)) :: Bool
+    , not (not (not (not _))) :: Bool
+    , not (not (_ || _)) :: Bool
+    , not (_ || p) :: Bool
+    , not (_ || False) :: Bool
+    , not (_ || True) :: Bool
+    , not (_ || not _) :: Bool
+    , not (p || _) :: Bool
+    , not (False || _) :: Bool
+    , not (True || _) :: Bool
+    , not (not _ || _) :: Bool
+    , not (_ && _) :: Bool
+    , not (_ == _) :: Bool
+    , not (_ == _) :: Bool
+    , not (odd _) :: Bool
+    , not (even _) :: Bool
+    , not (elem _ _) :: Bool
+    , _ || q :: Bool
+    , _ || not p :: Bool
+    , _ || not False :: Bool
+    , _ || not True :: Bool
+    , _ || not (not _) :: Bool
+    , _ || (_ || _) :: Bool
+    , p || p :: Bool
+    , p || False :: Bool
+    , p || True :: Bool
+    , p || not _ :: Bool
+    , False || p :: Bool
+    , False || False :: Bool
+    , False || True :: Bool
+    , False || not _ :: Bool
+    , True || p :: Bool
+    , True || False :: Bool
+    , True || True :: Bool
+    , True || not _ :: Bool
+    , not _ || p :: Bool
+    , not _ || False :: Bool
+    , not _ || True :: Bool
+    , not _ || not _ :: Bool
+    , _ && p :: Bool
+    , _ && False :: Bool
+    , _ && True :: Bool
+    , _ && not _ :: Bool
+    , q || _ :: Bool
+    , not p || _ :: Bool
+    , not False || _ :: Bool
+    , not True || _ :: Bool
+    , not (not _) || _ :: Bool
+    , (_ || _) || _ :: Bool
+    , p && _ :: Bool
+    , False && _ :: Bool
+    , True && _ :: Bool
+    , not _ && _ :: Bool
+    , _ ==> _ :: Bool
+    , _ == x :: Bool
+    , _ == 0 :: Bool
+    , _ == id _ :: Bool
+    , x == _ :: Bool
+    , 0 == _ :: Bool
+    , id _ == _ :: Bool
+    , _ == p :: Bool
+    , _ == False :: Bool
+    , _ == True :: Bool
+    , _ == not _ :: Bool
+    , p == _ :: Bool
+    , False == _ :: Bool
+    , True == _ :: Bool
+    , not _ == _ :: Bool
+    , _ <= _ :: Bool
+    , _ <= _ :: Bool
+    , odd x :: Bool
+    , odd 0 :: Bool
+    , odd (id _) :: Bool
+    , even x :: Bool
+    , even 0 :: Bool
+    , even (id _) :: Bool
+    , elem _ xs :: Bool
+    , elem _ [] :: Bool
+    , elem _ (_:_) :: Bool
+    , elem x _ :: Bool
+    , elem 0 _ :: Bool
+    , elem (id _) _ :: Bool
+    , c' :: Char
+    , 'A' :: Char
+    , xs' :: [Int]
+    , [0,0,0] :: [Int]
+    , [0,1] :: [Int]
+    , [1,0] :: [Int]
+    , [-1] :: [Int]
+    , _:zs :: [Int]
+    , [_,0,0] :: [Int]
+    , [_,1] :: [Int]
+    , _:_:ys :: [Int]
+    , [_,_,0] :: [Int]
+    , _:_:_:xs :: [Int]
+    , [_,_,_] :: [Int]
+    , _:_:_:_:_ :: [Int]
+    , _:_:x:_ :: [Int]
+    , _:_:0:_ :: [Int]
+    , _:_:id _:_ :: [Int]
+    , _:_:tail _ :: [Int]
+    , _:_:(_ ++ _) :: [Int]
+    , _:x:xs :: [Int]
+    , [_,x] :: [Int]
+    , _:x:_:_ :: [Int]
+    , _:0:xs :: [Int]
+    , [_,0] :: [Int]
+    , _:0:_:_ :: [Int]
+    , _:id _:xs :: [Int]
+    , [_,id _] :: [Int]
+    , _:id _:_:_ :: [Int]
+    , _:y:_ :: [Int]
+    , _:1:_ :: [Int]
+    , _:id x:_ :: [Int]
+    , _:id 0:_ :: [Int]
+    , _:id (id _):_ :: [Int]
+    , _:negate _:_ :: [Int]
+    , _:abs _:_ :: [Int]
+    , _:_ + _:_ :: [Int]
+    , _:head _:_ :: [Int]
+    , _:ord _:_ :: [Int]
+    , _:tail xs :: [Int]
+    , _:tail [] :: [Int]
+    , _:tail (_:_) :: [Int]
+    , _:(_ ++ xs) :: [Int]
+    , _:(_ ++ []) :: [Int]
+    , _:(_ ++ (_:_)) :: [Int]
+    , _:(xs ++ _) :: [Int]
+    , _:([] ++ _) :: [Int]
+    , _:((_:_) ++ _) :: [Int]
+    , _:sort _ :: [Int]
+    , _:insert _ _ :: [Int]
+    , x:ys :: [Int]
+    , [x,0] :: [Int]
+    , x:_:xs :: [Int]
+    , [x,_] :: [Int]
+    , x:_:_:_ :: [Int]
+    , x:x:_ :: [Int]
+    , x:0:_ :: [Int]
+    , x:id _:_ :: [Int]
+    , x:tail _ :: [Int]
+    , x:(_ ++ _) :: [Int]
+    , 0:ys :: [Int]
+    , [0,0] :: [Int]
+    , 0:_:xs :: [Int]
+    , [0,_] :: [Int]
+    , 0:_:_:_ :: [Int]
+    , 0:x:_ :: [Int]
+    , 0:0:_ :: [Int]
+    , 0:id _:_ :: [Int]
+    , 0:tail _ :: [Int]
+    , 0:(_ ++ _) :: [Int]
+    , id _:ys :: [Int]
+    , [id _,0] :: [Int]
+    , id _:_:xs :: [Int]
+    , [id _,_] :: [Int]
+    , id _:_:_:_ :: [Int]
+    , id _:x:_ :: [Int]
+    , id _:0:_ :: [Int]
+    , id _:id _:_ :: [Int]
+    , id _:tail _ :: [Int]
+    , id _:(_ ++ _) :: [Int]
+    , y:xs :: [Int]
+    , [y] :: [Int]
+    , y:_:_ :: [Int]
+    , 1:xs :: [Int]
+    , [1] :: [Int]
+    , 1:_:_ :: [Int]
+    , id x:xs :: [Int]
+    , [id x] :: [Int]
+    , id x:_:_ :: [Int]
+    , id 0:xs :: [Int]
+    , [id 0] :: [Int]
+    , id 0:_:_ :: [Int]
+    , id (id _):xs :: [Int]
+    , [id (id _)] :: [Int]
+    , id (id _):_:_ :: [Int]
+    , negate _:xs :: [Int]
+    , [negate _] :: [Int]
+    , negate _:_:_ :: [Int]
+    , abs _:xs :: [Int]
+    , [abs _] :: [Int]
+    , abs _:_:_ :: [Int]
+    , _ + _:xs :: [Int]
+    , [_ + _] :: [Int]
+    , _ + _:_:_ :: [Int]
+    , head _:xs :: [Int]
+    , [head _] :: [Int]
+    , head _:_:_ :: [Int]
+    , ord _:xs :: [Int]
+    , [ord _] :: [Int]
+    , ord _:_:_ :: [Int]
+    , z:_ :: [Int]
+    , (-1):_ :: [Int]
+    , id y:_ :: [Int]
+    , id 1:_ :: [Int]
+    , id (id x):_ :: [Int]
+    , id (id 0):_ :: [Int]
+    , id (id (id _)):_ :: [Int]
+    , id (negate _):_ :: [Int]
+    , id (abs _):_ :: [Int]
+    , id (_ + _):_ :: [Int]
+    , id (head _):_ :: [Int]
+    , id (ord _):_ :: [Int]
+    , negate x:_ :: [Int]
+    , negate 0:_ :: [Int]
+    , negate (id _):_ :: [Int]
+    , abs x:_ :: [Int]
+    , abs 0:_ :: [Int]
+    , abs (id _):_ :: [Int]
+    , _ + x:_ :: [Int]
+    , _ + 0:_ :: [Int]
+    , _ + id _:_ :: [Int]
+    , x + _:_ :: [Int]
+    , 0 + _:_ :: [Int]
+    , id _ + _:_ :: [Int]
+    , _ * _:_ :: [Int]
+    , f _:_ :: [Int]
+    , head xs:_ :: [Int]
+    , head []:_ :: [Int]
+    , head (_:_):_ :: [Int]
+    , ord c:_ :: [Int]
+    , ord 'a':_ :: [Int]
+    , tail ys :: [Int]
+    , tail [0] :: [Int]
+    , tail (_:xs) :: [Int]
+    , tail [_] :: [Int]
+    , tail (_:_:_) :: [Int]
+    , tail (x:_) :: [Int]
+    , tail (0:_) :: [Int]
+    , tail (id _:_) :: [Int]
+    , tail (tail _) :: [Int]
+    , tail (_ ++ _) :: [Int]
+    , _ ++ ys :: [Int]
+    , _ ++ [0] :: [Int]
+    , _ ++ (_:xs) :: [Int]
+    , _ ++ [_] :: [Int]
+    , _ ++ (_:_:_) :: [Int]
+    , _ ++ (x:_) :: [Int]
+    , _ ++ (0:_) :: [Int]
+    , _ ++ (id _:_) :: [Int]
+    , _ ++ tail _ :: [Int]
+    , _ ++ (_ ++ _) :: [Int]
+    , xs ++ xs :: [Int]
+    , xs ++ [] :: [Int]
+    , xs ++ (_:_) :: [Int]
+    , [] ++ xs :: [Int]
+    , [] ++ [] :: [Int]
+    , [] ++ (_:_) :: [Int]
+    , (_:_) ++ xs :: [Int]
+    , (_:_) ++ [] :: [Int]
+    , (_:_) ++ (_:_) :: [Int]
+    , ys ++ _ :: [Int]
+    , [0] ++ _ :: [Int]
+    , (_:xs) ++ _ :: [Int]
+    , [_] ++ _ :: [Int]
+    , (_:_:_) ++ _ :: [Int]
+    , (x:_) ++ _ :: [Int]
+    , (0:_) ++ _ :: [Int]
+    , (id _:_) ++ _ :: [Int]
+    , tail _ ++ _ :: [Int]
+    , (_ ++ _) ++ _ :: [Int]
+    , sort xs :: [Int]
+    , sort [] :: [Int]
+    , sort (_:_) :: [Int]
+    , insert _ xs :: [Int]
+    , insert _ [] :: [Int]
+    , insert _ (_:_) :: [Int]
+    , insert x _ :: [Int]
+    , insert 0 _ :: [Int]
+    , insert (id _) _ :: [Int]
+    , (y +) :: Int -> Int
+    , (1 +) :: Int -> Int
+    , (id x +) :: Int -> Int
+    , (id 0 +) :: Int -> Int
+    , (id (id _) +) :: Int -> Int
+    , (negate _ +) :: Int -> Int
+    , (abs _ +) :: Int -> Int
+    , ((_ + _) +) :: Int -> Int
+    , (head _ +) :: Int -> Int
+    , (ord _ +) :: Int -> Int
+    , (x *) :: Int -> Int
+    , (0 *) :: Int -> Int
+    , (id _ *) :: Int -> Int
+    , g :: Int -> Int
+    , (p ||) :: Bool -> Bool
+    , (False ||) :: Bool -> Bool
+    , (True ||) :: Bool -> Bool
+    , (not _ ||) :: Bool -> Bool
+    , (_ &&) :: Bool -> Bool
+    , (==>) :: Bool -> Bool -> Bool
+    ]
+  , [ z' :: Int
+    , 3 :: Int
+    , id y' :: Int
+    , id (-2) :: Int
+    , id (id x') :: Int
+    , id (id 2) :: Int
+    , id (id (id z)) :: Int
+    , id (id (id (-1))) :: Int
+    , id (id (id (id y))) :: Int
+    , id (id (id (id 1))) :: Int
+    , id (id (id (id (id x)))) :: Int
+    , id (id (id (id (id 0)))) :: Int
+    , id (id (id (id (id (id _))))) :: Int
+    , id (id (id (id (negate _)))) :: Int
+    , id (id (id (id (abs _)))) :: Int
+    , id (id (id (id (_ + _)))) :: Int
+    , id (id (id (id (head _)))) :: Int
+    , id (id (id (id (ord _)))) :: Int
+    , id (id (id (negate x))) :: Int
+    , id (id (id (negate 0))) :: Int
+    , id (id (id (negate (id _)))) :: Int
+    , id (id (id (abs x))) :: Int
+    , id (id (id (abs 0))) :: Int
+    , id (id (id (abs (id _)))) :: Int
+    , id (id (id (_ + x))) :: Int
+    , id (id (id (_ + 0))) :: Int
+    , id (id (id (_ + id _))) :: Int
+    , id (id (id (x + _))) :: Int
+    , id (id (id (0 + _))) :: Int
+    , id (id (id (id _ + _))) :: Int
+    , id (id (id (_ * _))) :: Int
+    , id (id (id (f _))) :: Int
+    , id (id (id (head xs))) :: Int
+    , id (id (id (head []))) :: Int
+    , id (id (id (head (_:_)))) :: Int
+    , id (id (id (ord c))) :: Int
+    , id (id (id (ord 'a'))) :: Int
+    , id (id (negate y)) :: Int
+    , id (id (negate 1)) :: Int
+    , id (id (negate (id x))) :: Int
+    , id (id (negate (id 0))) :: Int
+    , id (id (negate (id (id _)))) :: Int
+    , id (id (negate (negate _))) :: Int
+    , id (id (negate (abs _))) :: Int
+    , id (id (negate (_ + _))) :: Int
+    , id (id (negate (head _))) :: Int
+    , id (id (negate (ord _))) :: Int
+    , id (id (abs y)) :: Int
+    , id (id (abs 1)) :: Int
+    , id (id (abs (id x))) :: Int
+    , id (id (abs (id 0))) :: Int
+    , id (id (abs (id (id _)))) :: Int
+    , id (id (abs (negate _))) :: Int
+    , id (id (abs (abs _))) :: Int
+    , id (id (abs (_ + _))) :: Int
+    , id (id (abs (head _))) :: Int
+    , id (id (abs (ord _))) :: Int
+    , id (id (_ + y)) :: Int
+    , id (id (_ + 1)) :: Int
+    , id (id (_ + id x)) :: Int
+    , id (id (_ + id 0)) :: Int
+    , id (id (_ + id (id _))) :: Int
+    , id (id (_ + negate _)) :: Int
+    , id (id (_ + abs _)) :: Int
+    , id (id (_ + (_ + _))) :: Int
+    , id (id (_ + head _)) :: Int
+    , id (id (_ + ord _)) :: Int
+    , id (id (x + x)) :: Int
+    , id (id (x + 0)) :: Int
+    , id (id (x + id _)) :: Int
+    , id (id (0 + x)) :: Int
+    , id (id (0 + 0)) :: Int
+    , id (id (0 + id _)) :: Int
+    , id (id (id _ + x)) :: Int
+    , id (id (id _ + 0)) :: Int
+    , id (id (id _ + id _)) :: Int
+    , id (id (_ * x)) :: Int
+    , id (id (_ * 0)) :: Int
+    , id (id (_ * id _)) :: Int
+    , id (id (f x)) :: Int
+    , id (id (f 0)) :: Int
+    , id (id (f (id _))) :: Int
+    , id (id (y + _)) :: Int
+    , id (id (1 + _)) :: Int
+    , id (id (id x + _)) :: Int
+    , id (id (id 0 + _)) :: Int
+    , id (id (id (id _) + _)) :: Int
+    , id (id (negate _ + _)) :: Int
+    , id (id (abs _ + _)) :: Int
+    , id (id ((_ + _) + _)) :: Int
+    , id (id (head _ + _)) :: Int
+    , id (id (ord _ + _)) :: Int
+    , id (id (x * _)) :: Int
+    , id (id (0 * _)) :: Int
+    , id (id (id _ * _)) :: Int
+    , id (id (g _)) :: Int
+    , id (id (head ys)) :: Int
+    , id (id (head [0])) :: Int
+    , id (id (head (_:xs))) :: Int
+    , id (id (head [_])) :: Int
+    , id (id (head (_:_:_))) :: Int
+    , id (id (head (x:_))) :: Int
+    , id (id (head (0:_))) :: Int
+    , id (id (head (id _:_))) :: Int
+    , id (id (head (tail _))) :: Int
+    , id (id (head (_ ++ _))) :: Int
+    , id (id (ord d)) :: Int
+    , id (id (ord ' ')) :: Int
+    , id (negate z) :: Int
+    , id (negate (-1)) :: Int
+    , id (negate (id y)) :: Int
+    , id (negate (id 1)) :: Int
+    , id (negate (id (id x))) :: Int
+    , id (negate (id (id 0))) :: Int
+    , id (negate (id (id (id _)))) :: Int
+    , id (negate (id (negate _))) :: Int
+    , id (negate (id (abs _))) :: Int
+    , id (negate (id (_ + _))) :: Int
+    , id (negate (id (head _))) :: Int
+    , id (negate (id (ord _))) :: Int
+    , id (negate (negate x)) :: Int
+    , id (negate (negate 0)) :: Int
+    , id (negate (negate (id _))) :: Int
+    , id (negate (abs x)) :: Int
+    , id (negate (abs 0)) :: Int
+    , id (negate (abs (id _))) :: Int
+    , id (negate (_ + x)) :: Int
+    , id (negate (_ + 0)) :: Int
+    , id (negate (_ + id _)) :: Int
+    , id (negate (x + _)) :: Int
+    , id (negate (0 + _)) :: Int
+    , id (negate (id _ + _)) :: Int
+    , id (negate (_ * _)) :: Int
+    , id (negate (f _)) :: Int
+    , id (negate (head xs)) :: Int
+    , id (negate (head [])) :: Int
+    , id (negate (head (_:_))) :: Int
+    , id (negate (ord c)) :: Int
+    , id (negate (ord 'a')) :: Int
+    , id (abs z) :: Int
+    , id (abs (-1)) :: Int
+    , id (abs (id y)) :: Int
+    , id (abs (id 1)) :: Int
+    , id (abs (id (id x))) :: Int
+    , id (abs (id (id 0))) :: Int
+    , id (abs (id (id (id _)))) :: Int
+    , id (abs (id (negate _))) :: Int
+    , id (abs (id (abs _))) :: Int
+    , id (abs (id (_ + _))) :: Int
+    , id (abs (id (head _))) :: Int
+    , id (abs (id (ord _))) :: Int
+    , id (abs (negate x)) :: Int
+    , id (abs (negate 0)) :: Int
+    , id (abs (negate (id _))) :: Int
+    , id (abs (abs x)) :: Int
+    , id (abs (abs 0)) :: Int
+    , id (abs (abs (id _))) :: Int
+    , id (abs (_ + x)) :: Int
+    , id (abs (_ + 0)) :: Int
+    , id (abs (_ + id _)) :: Int
+    , id (abs (x + _)) :: Int
+    , id (abs (0 + _)) :: Int
+    , id (abs (id _ + _)) :: Int
+    , id (abs (_ * _)) :: Int
+    , id (abs (f _)) :: Int
+    , id (abs (head xs)) :: Int
+    , id (abs (head [])) :: Int
+    , id (abs (head (_:_))) :: Int
+    , id (abs (ord c)) :: Int
+    , id (abs (ord 'a')) :: Int
+    , id (_ + z) :: Int
+    , id (_ + (-1)) :: Int
+    , id (_ + id y) :: Int
+    , id (_ + id 1) :: Int
+    , id (_ + id (id x)) :: Int
+    , id (_ + id (id 0)) :: Int
+    , id (_ + id (id (id _))) :: Int
+    , id (_ + id (negate _)) :: Int
+    , id (_ + id (abs _)) :: Int
+    , id (_ + id (_ + _)) :: Int
+    , id (_ + id (head _)) :: Int
+    , id (_ + id (ord _)) :: Int
+    , id (_ + negate x) :: Int
+    , id (_ + negate 0) :: Int
+    , id (_ + negate (id _)) :: Int
+    , id (_ + abs x) :: Int
+    , id (_ + abs 0) :: Int
+    , id (_ + abs (id _)) :: Int
+    , id (_ + (_ + x)) :: Int
+    , id (_ + (_ + 0)) :: Int
+    , id (_ + (_ + id _)) :: Int
+    , id (_ + (x + _)) :: Int
+    , id (_ + (0 + _)) :: Int
+    , id (_ + (id _ + _)) :: Int
+    , id (_ + _ * _) :: Int
+    , id (_ + f _) :: Int
+    , id (_ + head xs) :: Int
+    , id (_ + head []) :: Int
+    , id (_ + head (_:_)) :: Int
+    , id (_ + ord c) :: Int
+    , id (_ + ord 'a') :: Int
+    , id (x + y) :: Int
+    , id (x + 1) :: Int
+    , id (x + id x) :: Int
+    , id (x + id 0) :: Int
+    , id (x + id (id _)) :: Int
+    , id (x + negate _) :: Int
+    , id (x + abs _) :: Int
+    , id (x + (_ + _)) :: Int
+    , id (x + head _) :: Int
+    , id (x + ord _) :: Int
+    , id (0 + y) :: Int
+    , id (0 + 1) :: Int
+    , id (0 + id x) :: Int
+    , id (0 + id 0) :: Int
+    , id (0 + id (id _)) :: Int
+    , id (0 + negate _) :: Int
+    , id (0 + abs _) :: Int
+    , id (0 + (_ + _)) :: Int
+    , id (0 + head _) :: Int
+    , id (0 + ord _) :: Int
+    , id (id _ + y) :: Int
+    , id (id _ + 1) :: Int
+    , id (id _ + id x) :: Int
+    , id (id _ + id 0) :: Int
+    , id (id _ + id (id _)) :: Int
+    , id (id _ + negate _) :: Int
+    , id (id _ + abs _) :: Int
+    , id (id _ + (_ + _)) :: Int
+    , id (id _ + head _) :: Int
+    , id (id _ + ord _) :: Int
+    , id (_ * y) :: Int
+    , id (_ * 1) :: Int
+    , id (_ * id x) :: Int
+    , id (_ * id 0) :: Int
+    , id (_ * id (id _)) :: Int
+    , id (_ * negate _) :: Int
+    , id (_ * abs _) :: Int
+    , id (_ * (_ + _)) :: Int
+    , id (_ * head _) :: Int
+    , id (_ * ord _) :: Int
+    , id (f y) :: Int
+    , id (f 1) :: Int
+    , id (f (id x)) :: Int
+    , id (f (id 0)) :: Int
+    , id (f (id (id _))) :: Int
+    , id (f (negate _)) :: Int
+    , id (f (abs _)) :: Int
+    , id (f (_ + _)) :: Int
+    , id (f (head _)) :: Int
+    , id (f (ord _)) :: Int
+    , id (y + x) :: Int
+    , id (y + 0) :: Int
+    , id (y + id _) :: Int
+    , id (1 + x) :: Int
+    , id (1 + 0) :: Int
+    , id (1 + id _) :: Int
+    , id (id x + x) :: Int
+    , id (id x + 0) :: Int
+    , id (id x + id _) :: Int
+    , id (id 0 + x) :: Int
+    , id (id 0 + 0) :: Int
+    , id (id 0 + id _) :: Int
+    , id (id (id _) + x) :: Int
+    , id (id (id _) + 0) :: Int
+    , id (id (id _) + id _) :: Int
+    , id (negate _ + x) :: Int
+    , id (negate _ + 0) :: Int
+    , id (negate _ + id _) :: Int
+    , id (abs _ + x) :: Int
+    , id (abs _ + 0) :: Int
+    , id (abs _ + id _) :: Int
+    , id ((_ + _) + x) :: Int
+    , id ((_ + _) + 0) :: Int
+    , id ((_ + _) + id _) :: Int
+    , id (head _ + x) :: Int
+    , id (head _ + 0) :: Int
+    , id (head _ + id _) :: Int
+    , id (ord _ + x) :: Int
+    , id (ord _ + 0) :: Int
+    , id (ord _ + id _) :: Int
+    , id (x * x) :: Int
+    , id (x * 0) :: Int
+    , id (x * id _) :: Int
+    , id (0 * x) :: Int
+    , id (0 * 0) :: Int
+    , id (0 * id _) :: Int
+    , id (id _ * x) :: Int
+    , id (id _ * 0) :: Int
+    , id (id _ * id _) :: Int
+    , id (g x) :: Int
+    , id (g 0) :: Int
+    , id (g (id _)) :: Int
+    , id (z + _) :: Int
+    , id ((-1) + _) :: Int
+    , id (id y + _) :: Int
+    , id (id 1 + _) :: Int
+    , id (id (id x) + _) :: Int
+    , id (id (id 0) + _) :: Int
+    , id (id (id (id _)) + _) :: Int
+    , id (id (negate _) + _) :: Int
+    , id (id (abs _) + _) :: Int
+    , id (id (_ + _) + _) :: Int
+    , id (id (head _) + _) :: Int
+    , id (id (ord _) + _) :: Int
+    , id (negate x + _) :: Int
+    , id (negate 0 + _) :: Int
+    , id (negate (id _) + _) :: Int
+    , id (abs x + _) :: Int
+    , id (abs 0 + _) :: Int
+    , id (abs (id _) + _) :: Int
+    , id ((_ + x) + _) :: Int
+    , id ((_ + 0) + _) :: Int
+    , id ((_ + id _) + _) :: Int
+    , id ((x + _) + _) :: Int
+    , id ((0 + _) + _) :: Int
+    , id ((id _ + _) + _) :: Int
+    , id (_ * _ + _) :: Int
+    , id (f _ + _) :: Int
+    , id (head xs + _) :: Int
+    , id (head [] + _) :: Int
+    , id (head (_:_) + _) :: Int
+    , id (ord c + _) :: Int
+    , id (ord 'a' + _) :: Int
+    , id (y * _) :: Int
+    , id (1 * _) :: Int
+    , id (id x * _) :: Int
+    , id (id 0 * _) :: Int
+    , id (id (id _) * _) :: Int
+    , id (negate _ * _) :: Int
+    , id (abs _ * _) :: Int
+    , id ((_ + _) * _) :: Int
+    , id (head _ * _) :: Int
+    , id (ord _ * _) :: Int
+    , id (h _) :: Int
+    , id (head zs) :: Int
+    , id (head [0,0]) :: Int
+    , id (head [1]) :: Int
+    , id (head (_:ys)) :: Int
+    , id (head [_,0]) :: Int
+    , id (head (_:_:xs)) :: Int
+    , id (head [_,_]) :: Int
+    , id (head (_:_:_:_)) :: Int
+    , id (head (_:x:_)) :: Int
+    , id (head (_:0:_)) :: Int
+    , id (head (_:id _:_)) :: Int
+    , id (head (_:tail _)) :: Int
+    , id (head (_:(_ ++ _))) :: Int
+    , id (head (x:xs)) :: Int
+    , id (head [x]) :: Int
+    , id (head (x:_:_)) :: Int
+    , id (head (0:xs)) :: Int
+    , id (head [0]) :: Int
+    , id (head (0:_:_)) :: Int
+    , id (head (id _:xs)) :: Int
+    , id (head [id _]) :: Int
+    , id (head (id _:_:_)) :: Int
+    , id (head (y:_)) :: Int
+    , id (head (1:_)) :: Int
+    , id (head (id x:_)) :: Int
+    , id (head (id 0:_)) :: Int
+    , id (head (id (id _):_)) :: Int
+    , id (head (negate _:_)) :: Int
+    , id (head (abs _:_)) :: Int
+    , id (head (_ + _:_)) :: Int
+    , id (head (head _:_)) :: Int
+    , id (head (ord _:_)) :: Int
+    , id (head (tail xs)) :: Int
+    , id (head (tail [])) :: Int
+    , id (head (tail (_:_))) :: Int
+    , id (head (_ ++ xs)) :: Int
+    , id (head (_ ++ [])) :: Int
+    , id (head (_ ++ (_:_))) :: Int
+    , id (head (xs ++ _)) :: Int
+    , id (head ([] ++ _)) :: Int
+    , id (head ((_:_) ++ _)) :: Int
+    , id (head (sort _)) :: Int
+    , id (head (insert _ _)) :: Int
+    , id (ord e) :: Int
+    , id (ord 'b') :: Int
+    , negate x' :: Int
+    , negate 2 :: Int
+    , negate (id z) :: Int
+    , negate (id (-1)) :: Int
+    , negate (id (id y)) :: Int
+    , negate (id (id 1)) :: Int
+    , negate (id (id (id x))) :: Int
+    , negate (id (id (id 0))) :: Int
+    , negate (id (id (id (id _)))) :: Int
+    , negate (id (id (negate _))) :: Int
+    , negate (id (id (abs _))) :: Int
+    , negate (id (id (_ + _))) :: Int
+    , negate (id (id (head _))) :: Int
+    , negate (id (id (ord _))) :: Int
+    , negate (id (negate x)) :: Int
+    , negate (id (negate 0)) :: Int
+    , negate (id (negate (id _))) :: Int
+    , negate (id (abs x)) :: Int
+    , negate (id (abs 0)) :: Int
+    , negate (id (abs (id _))) :: Int
+    , negate (id (_ + x)) :: Int
+    , negate (id (_ + 0)) :: Int
+    , negate (id (_ + id _)) :: Int
+    , negate (id (x + _)) :: Int
+    , negate (id (0 + _)) :: Int
+    , negate (id (id _ + _)) :: Int
+    , negate (id (_ * _)) :: Int
+    , negate (id (f _)) :: Int
+    , negate (id (head xs)) :: Int
+    , negate (id (head [])) :: Int
+    , negate (id (head (_:_))) :: Int
+    , negate (id (ord c)) :: Int
+    , negate (id (ord 'a')) :: Int
+    , negate (negate y) :: Int
+    , negate (negate 1) :: Int
+    , negate (negate (id x)) :: Int
+    , negate (negate (id 0)) :: Int
+    , negate (negate (id (id _))) :: Int
+    , negate (negate (negate _)) :: Int
+    , negate (negate (abs _)) :: Int
+    , negate (negate (_ + _)) :: Int
+    , negate (negate (head _)) :: Int
+    , negate (negate (ord _)) :: Int
+    , negate (abs y) :: Int
+    , negate (abs 1) :: Int
+    , negate (abs (id x)) :: Int
+    , negate (abs (id 0)) :: Int
+    , negate (abs (id (id _))) :: Int
+    , negate (abs (negate _)) :: Int
+    , negate (abs (abs _)) :: Int
+    , negate (abs (_ + _)) :: Int
+    , negate (abs (head _)) :: Int
+    , negate (abs (ord _)) :: Int
+    , negate (_ + y) :: Int
+    , negate (_ + 1) :: Int
+    , negate (_ + id x) :: Int
+    , negate (_ + id 0) :: Int
+    , negate (_ + id (id _)) :: Int
+    , negate (_ + negate _) :: Int
+    , negate (_ + abs _) :: Int
+    , negate (_ + (_ + _)) :: Int
+    , negate (_ + head _) :: Int
+    , negate (_ + ord _) :: Int
+    , negate (x + x) :: Int
+    , negate (x + 0) :: Int
+    , negate (x + id _) :: Int
+    , negate (0 + x) :: Int
+    , negate (0 + 0) :: Int
+    , negate (0 + id _) :: Int
+    , negate (id _ + x) :: Int
+    , negate (id _ + 0) :: Int
+    , negate (id _ + id _) :: Int
+    , negate (_ * x) :: Int
+    , negate (_ * 0) :: Int
+    , negate (_ * id _) :: Int
+    , negate (f x) :: Int
+    , negate (f 0) :: Int
+    , negate (f (id _)) :: Int
+    , negate (y + _) :: Int
+    , negate (1 + _) :: Int
+    , negate (id x + _) :: Int
+    , negate (id 0 + _) :: Int
+    , negate (id (id _) + _) :: Int
+    , negate (negate _ + _) :: Int
+    , negate (abs _ + _) :: Int
+    , negate ((_ + _) + _) :: Int
+    , negate (head _ + _) :: Int
+    , negate (ord _ + _) :: Int
+    , negate (x * _) :: Int
+    , negate (0 * _) :: Int
+    , negate (id _ * _) :: Int
+    , negate (g _) :: Int
+    , negate (head ys) :: Int
+    , negate (head [0]) :: Int
+    , negate (head (_:xs)) :: Int
+    , negate (head [_]) :: Int
+    , negate (head (_:_:_)) :: Int
+    , negate (head (x:_)) :: Int
+    , negate (head (0:_)) :: Int
+    , negate (head (id _:_)) :: Int
+    , negate (head (tail _)) :: Int
+    , negate (head (_ ++ _)) :: Int
+    , negate (ord d) :: Int
+    , negate (ord ' ') :: Int
+    , abs x' :: Int
+    , abs 2 :: Int
+    , abs (id z) :: Int
+    , abs (id (-1)) :: Int
+    , abs (id (id y)) :: Int
+    , abs (id (id 1)) :: Int
+    , abs (id (id (id x))) :: Int
+    , abs (id (id (id 0))) :: Int
+    , abs (id (id (id (id _)))) :: Int
+    , abs (id (id (negate _))) :: Int
+    , abs (id (id (abs _))) :: Int
+    , abs (id (id (_ + _))) :: Int
+    , abs (id (id (head _))) :: Int
+    , abs (id (id (ord _))) :: Int
+    , abs (id (negate x)) :: Int
+    , abs (id (negate 0)) :: Int
+    , abs (id (negate (id _))) :: Int
+    , abs (id (abs x)) :: Int
+    , abs (id (abs 0)) :: Int
+    , abs (id (abs (id _))) :: Int
+    , abs (id (_ + x)) :: Int
+    , abs (id (_ + 0)) :: Int
+    , abs (id (_ + id _)) :: Int
+    , abs (id (x + _)) :: Int
+    , abs (id (0 + _)) :: Int
+    , abs (id (id _ + _)) :: Int
+    , abs (id (_ * _)) :: Int
+    , abs (id (f _)) :: Int
+    , abs (id (head xs)) :: Int
+    , abs (id (head [])) :: Int
+    , abs (id (head (_:_))) :: Int
+    , abs (id (ord c)) :: Int
+    , abs (id (ord 'a')) :: Int
+    , abs (negate y) :: Int
+    , abs (negate 1) :: Int
+    , abs (negate (id x)) :: Int
+    , abs (negate (id 0)) :: Int
+    , abs (negate (id (id _))) :: Int
+    , abs (negate (negate _)) :: Int
+    , abs (negate (abs _)) :: Int
+    , abs (negate (_ + _)) :: Int
+    , abs (negate (head _)) :: Int
+    , abs (negate (ord _)) :: Int
+    , abs (abs y) :: Int
+    , abs (abs 1) :: Int
+    , abs (abs (id x)) :: Int
+    , abs (abs (id 0)) :: Int
+    , abs (abs (id (id _))) :: Int
+    , abs (abs (negate _)) :: Int
+    , abs (abs (abs _)) :: Int
+    , abs (abs (_ + _)) :: Int
+    , abs (abs (head _)) :: Int
+    , abs (abs (ord _)) :: Int
+    , abs (_ + y) :: Int
+    , abs (_ + 1) :: Int
+    , abs (_ + id x) :: Int
+    , abs (_ + id 0) :: Int
+    , abs (_ + id (id _)) :: Int
+    , abs (_ + negate _) :: Int
+    , abs (_ + abs _) :: Int
+    , abs (_ + (_ + _)) :: Int
+    , abs (_ + head _) :: Int
+    , abs (_ + ord _) :: Int
+    , abs (x + x) :: Int
+    , abs (x + 0) :: Int
+    , abs (x + id _) :: Int
+    , abs (0 + x) :: Int
+    , abs (0 + 0) :: Int
+    , abs (0 + id _) :: Int
+    , abs (id _ + x) :: Int
+    , abs (id _ + 0) :: Int
+    , abs (id _ + id _) :: Int
+    , abs (_ * x) :: Int
+    , abs (_ * 0) :: Int
+    , abs (_ * id _) :: Int
+    , abs (f x) :: Int
+    , abs (f 0) :: Int
+    , abs (f (id _)) :: Int
+    , abs (y + _) :: Int
+    , abs (1 + _) :: Int
+    , abs (id x + _) :: Int
+    , abs (id 0 + _) :: Int
+    , abs (id (id _) + _) :: Int
+    , abs (negate _ + _) :: Int
+    , abs (abs _ + _) :: Int
+    , abs ((_ + _) + _) :: Int
+    , abs (head _ + _) :: Int
+    , abs (ord _ + _) :: Int
+    , abs (x * _) :: Int
+    , abs (0 * _) :: Int
+    , abs (id _ * _) :: Int
+    , abs (g _) :: Int
+    , abs (head ys) :: Int
+    , abs (head [0]) :: Int
+    , abs (head (_:xs)) :: Int
+    , abs (head [_]) :: Int
+    , abs (head (_:_:_)) :: Int
+    , abs (head (x:_)) :: Int
+    , abs (head (0:_)) :: Int
+    , abs (head (id _:_)) :: Int
+    , abs (head (tail _)) :: Int
+    , abs (head (_ ++ _)) :: Int
+    , abs (ord d) :: Int
+    , abs (ord ' ') :: Int
+    , _ + x' :: Int
+    , _ + 2 :: Int
+    , _ + id z :: Int
+    , _ + id (-1) :: Int
+    , _ + id (id y) :: Int
+    , _ + id (id 1) :: Int
+    , _ + id (id (id x)) :: Int
+    , _ + id (id (id 0)) :: Int
+    , _ + id (id (id (id _))) :: Int
+    , _ + id (id (negate _)) :: Int
+    , _ + id (id (abs _)) :: Int
+    , _ + id (id (_ + _)) :: Int
+    , _ + id (id (head _)) :: Int
+    , _ + id (id (ord _)) :: Int
+    , _ + id (negate x) :: Int
+    , _ + id (negate 0) :: Int
+    , _ + id (negate (id _)) :: Int
+    , _ + id (abs x) :: Int
+    , _ + id (abs 0) :: Int
+    , _ + id (abs (id _)) :: Int
+    , _ + id (_ + x) :: Int
+    , _ + id (_ + 0) :: Int
+    , _ + id (_ + id _) :: Int
+    , _ + id (x + _) :: Int
+    , _ + id (0 + _) :: Int
+    , _ + id (id _ + _) :: Int
+    , _ + id (_ * _) :: Int
+    , _ + id (f _) :: Int
+    , _ + id (head xs) :: Int
+    , _ + id (head []) :: Int
+    , _ + id (head (_:_)) :: Int
+    , _ + id (ord c) :: Int
+    , _ + id (ord 'a') :: Int
+    , _ + negate y :: Int
+    , _ + negate 1 :: Int
+    , _ + negate (id x) :: Int
+    , _ + negate (id 0) :: Int
+    , _ + negate (id (id _)) :: Int
+    , _ + negate (negate _) :: Int
+    , _ + negate (abs _) :: Int
+    , _ + negate (_ + _) :: Int
+    , _ + negate (head _) :: Int
+    , _ + negate (ord _) :: Int
+    , _ + abs y :: Int
+    , _ + abs 1 :: Int
+    , _ + abs (id x) :: Int
+    , _ + abs (id 0) :: Int
+    , _ + abs (id (id _)) :: Int
+    , _ + abs (negate _) :: Int
+    , _ + abs (abs _) :: Int
+    , _ + abs (_ + _) :: Int
+    , _ + abs (head _) :: Int
+    , _ + abs (ord _) :: Int
+    , _ + (_ + y) :: Int
+    , _ + (_ + 1) :: Int
+    , _ + (_ + id x) :: Int
+    , _ + (_ + id 0) :: Int
+    , _ + (_ + id (id _)) :: Int
+    , _ + (_ + negate _) :: Int
+    , _ + (_ + abs _) :: Int
+    , _ + (_ + (_ + _)) :: Int
+    , _ + (_ + head _) :: Int
+    , _ + (_ + ord _) :: Int
+    , _ + (x + x) :: Int
+    , _ + (x + 0) :: Int
+    , _ + (x + id _) :: Int
+    , _ + (0 + x) :: Int
+    , _ + (0 + 0) :: Int
+    , _ + (0 + id _) :: Int
+    , _ + (id _ + x) :: Int
+    , _ + (id _ + 0) :: Int
+    , _ + (id _ + id _) :: Int
+    , _ + _ * x :: Int
+    , _ + _ * 0 :: Int
+    , _ + _ * id _ :: Int
+    , _ + f x :: Int
+    , _ + f 0 :: Int
+    , _ + f (id _) :: Int
+    , _ + (y + _) :: Int
+    , _ + (1 + _) :: Int
+    , _ + (id x + _) :: Int
+    , _ + (id 0 + _) :: Int
+    , _ + (id (id _) + _) :: Int
+    , _ + (negate _ + _) :: Int
+    , _ + (abs _ + _) :: Int
+    , _ + ((_ + _) + _) :: Int
+    , _ + (head _ + _) :: Int
+    , _ + (ord _ + _) :: Int
+    , _ + x * _ :: Int
+    , _ + 0 * _ :: Int
+    , _ + id _ * _ :: Int
+    , _ + g _ :: Int
+    , _ + head ys :: Int
+    , _ + head [0] :: Int
+    , _ + head (_:xs) :: Int
+    , _ + head [_] :: Int
+    , _ + head (_:_:_) :: Int
+    , _ + head (x:_) :: Int
+    , _ + head (0:_) :: Int
+    , _ + head (id _:_) :: Int
+    , _ + head (tail _) :: Int
+    , _ + head (_ ++ _) :: Int
+    , _ + ord d :: Int
+    , _ + ord ' ' :: Int
+    , x + z :: Int
+    , x + (-1) :: Int
+    , x + id y :: Int
+    , x + id 1 :: Int
+    , x + id (id x) :: Int
+    , x + id (id 0) :: Int
+    , x + id (id (id _)) :: Int
+    , x + id (negate _) :: Int
+    , x + id (abs _) :: Int
+    , x + id (_ + _) :: Int
+    , x + id (head _) :: Int
+    , x + id (ord _) :: Int
+    , x + negate x :: Int
+    , x + negate 0 :: Int
+    , x + negate (id _) :: Int
+    , x + abs x :: Int
+    , x + abs 0 :: Int
+    , x + abs (id _) :: Int
+    , x + (_ + x) :: Int
+    , x + (_ + 0) :: Int
+    , x + (_ + id _) :: Int
+    , x + (x + _) :: Int
+    , x + (0 + _) :: Int
+    , x + (id _ + _) :: Int
+    , x + _ * _ :: Int
+    , x + f _ :: Int
+    , x + head xs :: Int
+    , x + head [] :: Int
+    , x + head (_:_) :: Int
+    , x + ord c :: Int
+    , x + ord 'a' :: Int
+    , 0 + z :: Int
+    , 0 + (-1) :: Int
+    , 0 + id y :: Int
+    , 0 + id 1 :: Int
+    , 0 + id (id x) :: Int
+    , 0 + id (id 0) :: Int
+    , 0 + id (id (id _)) :: Int
+    , 0 + id (negate _) :: Int
+    , 0 + id (abs _) :: Int
+    , 0 + id (_ + _) :: Int
+    , 0 + id (head _) :: Int
+    , 0 + id (ord _) :: Int
+    , 0 + negate x :: Int
+    , 0 + negate 0 :: Int
+    , 0 + negate (id _) :: Int
+    , 0 + abs x :: Int
+    , 0 + abs 0 :: Int
+    , 0 + abs (id _) :: Int
+    , 0 + (_ + x) :: Int
+    , 0 + (_ + 0) :: Int
+    , 0 + (_ + id _) :: Int
+    , 0 + (x + _) :: Int
+    , 0 + (0 + _) :: Int
+    , 0 + (id _ + _) :: Int
+    , 0 + _ * _ :: Int
+    , 0 + f _ :: Int
+    , 0 + head xs :: Int
+    , 0 + head [] :: Int
+    , 0 + head (_:_) :: Int
+    , 0 + ord c :: Int
+    , 0 + ord 'a' :: Int
+    , id _ + z :: Int
+    , id _ + (-1) :: Int
+    , id _ + id y :: Int
+    , id _ + id 1 :: Int
+    , id _ + id (id x) :: Int
+    , id _ + id (id 0) :: Int
+    , id _ + id (id (id _)) :: Int
+    , id _ + id (negate _) :: Int
+    , id _ + id (abs _) :: Int
+    , id _ + id (_ + _) :: Int
+    , id _ + id (head _) :: Int
+    , id _ + id (ord _) :: Int
+    , id _ + negate x :: Int
+    , id _ + negate 0 :: Int
+    , id _ + negate (id _) :: Int
+    , id _ + abs x :: Int
+    , id _ + abs 0 :: Int
+    , id _ + abs (id _) :: Int
+    , id _ + (_ + x) :: Int
+    , id _ + (_ + 0) :: Int
+    , id _ + (_ + id _) :: Int
+    , id _ + (x + _) :: Int
+    , id _ + (0 + _) :: Int
+    , id _ + (id _ + _) :: Int
+    , id _ + _ * _ :: Int
+    , id _ + f _ :: Int
+    , id _ + head xs :: Int
+    , id _ + head [] :: Int
+    , id _ + head (_:_) :: Int
+    , id _ + ord c :: Int
+    , id _ + ord 'a' :: Int
+    , _ * z :: Int
+    , _ * (-1) :: Int
+    , _ * id y :: Int
+    , _ * id 1 :: Int
+    , _ * id (id x) :: Int
+    , _ * id (id 0) :: Int
+    , _ * id (id (id _)) :: Int
+    , _ * id (negate _) :: Int
+    , _ * id (abs _) :: Int
+    , _ * id (_ + _) :: Int
+    , _ * id (head _) :: Int
+    , _ * id (ord _) :: Int
+    , _ * negate x :: Int
+    , _ * negate 0 :: Int
+    , _ * negate (id _) :: Int
+    , _ * abs x :: Int
+    , _ * abs 0 :: Int
+    , _ * abs (id _) :: Int
+    , _ * (_ + x) :: Int
+    , _ * (_ + 0) :: Int
+    , _ * (_ + id _) :: Int
+    , _ * (x + _) :: Int
+    , _ * (0 + _) :: Int
+    , _ * (id _ + _) :: Int
+    , _ * (_ * _) :: Int
+    , _ * f _ :: Int
+    , _ * head xs :: Int
+    , _ * head [] :: Int
+    , _ * head (_:_) :: Int
+    , _ * ord c :: Int
+    , _ * ord 'a' :: Int
+    , f z :: Int
+    , f (-1) :: Int
+    , f (id y) :: Int
+    , f (id 1) :: Int
+    , f (id (id x)) :: Int
+    , f (id (id 0)) :: Int
+    , f (id (id (id _))) :: Int
+    , f (id (negate _)) :: Int
+    , f (id (abs _)) :: Int
+    , f (id (_ + _)) :: Int
+    , f (id (head _)) :: Int
+    , f (id (ord _)) :: Int
+    , f (negate x) :: Int
+    , f (negate 0) :: Int
+    , f (negate (id _)) :: Int
+    , f (abs x) :: Int
+    , f (abs 0) :: Int
+    , f (abs (id _)) :: Int
+    , f (_ + x) :: Int
+    , f (_ + 0) :: Int
+    , f (_ + id _) :: Int
+    , f (x + _) :: Int
+    , f (0 + _) :: Int
+    , f (id _ + _) :: Int
+    , f (_ * _) :: Int
+    , f (f _) :: Int
+    , f (head xs) :: Int
+    , f (head []) :: Int
+    , f (head (_:_)) :: Int
+    , f (ord c) :: Int
+    , f (ord 'a') :: Int
+    , y + y :: Int
+    , y + 1 :: Int
+    , y + id x :: Int
+    , y + id 0 :: Int
+    , y + id (id _) :: Int
+    , y + negate _ :: Int
+    , y + abs _ :: Int
+    , y + (_ + _) :: Int
+    , y + head _ :: Int
+    , y + ord _ :: Int
+    , 1 + y :: Int
+    , 1 + 1 :: Int
+    , 1 + id x :: Int
+    , 1 + id 0 :: Int
+    , 1 + id (id _) :: Int
+    , 1 + negate _ :: Int
+    , 1 + abs _ :: Int
+    , 1 + (_ + _) :: Int
+    , 1 + head _ :: Int
+    , 1 + ord _ :: Int
+    , id x + y :: Int
+    , id x + 1 :: Int
+    , id x + id x :: Int
+    , id x + id 0 :: Int
+    , id x + id (id _) :: Int
+    , id x + negate _ :: Int
+    , id x + abs _ :: Int
+    , id x + (_ + _) :: Int
+    , id x + head _ :: Int
+    , id x + ord _ :: Int
+    , id 0 + y :: Int
+    , id 0 + 1 :: Int
+    , id 0 + id x :: Int
+    , id 0 + id 0 :: Int
+    , id 0 + id (id _) :: Int
+    , id 0 + negate _ :: Int
+    , id 0 + abs _ :: Int
+    , id 0 + (_ + _) :: Int
+    , id 0 + head _ :: Int
+    , id 0 + ord _ :: Int
+    , id (id _) + y :: Int
+    , id (id _) + 1 :: Int
+    , id (id _) + id x :: Int
+    , id (id _) + id 0 :: Int
+    , id (id _) + id (id _) :: Int
+    , id (id _) + negate _ :: Int
+    , id (id _) + abs _ :: Int
+    , id (id _) + (_ + _) :: Int
+    , id (id _) + head _ :: Int
+    , id (id _) + ord _ :: Int
+    , negate _ + y :: Int
+    , negate _ + 1 :: Int
+    , negate _ + id x :: Int
+    , negate _ + id 0 :: Int
+    , negate _ + id (id _) :: Int
+    , negate _ + negate _ :: Int
+    , negate _ + abs _ :: Int
+    , negate _ + (_ + _) :: Int
+    , negate _ + head _ :: Int
+    , negate _ + ord _ :: Int
+    , abs _ + y :: Int
+    , abs _ + 1 :: Int
+    , abs _ + id x :: Int
+    , abs _ + id 0 :: Int
+    , abs _ + id (id _) :: Int
+    , abs _ + negate _ :: Int
+    , abs _ + abs _ :: Int
+    , abs _ + (_ + _) :: Int
+    , abs _ + head _ :: Int
+    , abs _ + ord _ :: Int
+    , (_ + _) + y :: Int
+    , (_ + _) + 1 :: Int
+    , (_ + _) + id x :: Int
+    , (_ + _) + id 0 :: Int
+    , (_ + _) + id (id _) :: Int
+    , (_ + _) + negate _ :: Int
+    , (_ + _) + abs _ :: Int
+    , (_ + _) + (_ + _) :: Int
+    , (_ + _) + head _ :: Int
+    , (_ + _) + ord _ :: Int
+    , head _ + y :: Int
+    , head _ + 1 :: Int
+    , head _ + id x :: Int
+    , head _ + id 0 :: Int
+    , head _ + id (id _) :: Int
+    , head _ + negate _ :: Int
+    , head _ + abs _ :: Int
+    , head _ + (_ + _) :: Int
+    , head _ + head _ :: Int
+    , head _ + ord _ :: Int
+    , ord _ + y :: Int
+    , ord _ + 1 :: Int
+    , ord _ + id x :: Int
+    , ord _ + id 0 :: Int
+    , ord _ + id (id _) :: Int
+    , ord _ + negate _ :: Int
+    , ord _ + abs _ :: Int
+    , ord _ + (_ + _) :: Int
+    , ord _ + head _ :: Int
+    , ord _ + ord _ :: Int
+    , x * y :: Int
+    , x * 1 :: Int
+    , x * id x :: Int
+    , x * id 0 :: Int
+    , x * id (id _) :: Int
+    , x * negate _ :: Int
+    , x * abs _ :: Int
+    , x * (_ + _) :: Int
+    , x * head _ :: Int
+    , x * ord _ :: Int
+    , 0 * y :: Int
+    , 0 * 1 :: Int
+    , 0 * id x :: Int
+    , 0 * id 0 :: Int
+    , 0 * id (id _) :: Int
+    , 0 * negate _ :: Int
+    , 0 * abs _ :: Int
+    , 0 * (_ + _) :: Int
+    , 0 * head _ :: Int
+    , 0 * ord _ :: Int
+    , id _ * y :: Int
+    , id _ * 1 :: Int
+    , id _ * id x :: Int
+    , id _ * id 0 :: Int
+    , id _ * id (id _) :: Int
+    , id _ * negate _ :: Int
+    , id _ * abs _ :: Int
+    , id _ * (_ + _) :: Int
+    , id _ * head _ :: Int
+    , id _ * ord _ :: Int
+    , g y :: Int
+    , g 1 :: Int
+    , g (id x) :: Int
+    , g (id 0) :: Int
+    , g (id (id _)) :: Int
+    , g (negate _) :: Int
+    , g (abs _) :: Int
+    , g (_ + _) :: Int
+    , g (head _) :: Int
+    , g (ord _) :: Int
+    , z + x :: Int
+    , z + 0 :: Int
+    , z + id _ :: Int
+    , (-1) + x :: Int
+    , (-1) + 0 :: Int
+    , (-1) + id _ :: Int
+    , id y + x :: Int
+    , id y + 0 :: Int
+    , id y + id _ :: Int
+    , id 1 + x :: Int
+    , id 1 + 0 :: Int
+    , id 1 + id _ :: Int
+    , id (id x) + x :: Int
+    , id (id x) + 0 :: Int
+    , id (id x) + id _ :: Int
+    , id (id 0) + x :: Int
+    , id (id 0) + 0 :: Int
+    , id (id 0) + id _ :: Int
+    , id (id (id _)) + x :: Int
+    , id (id (id _)) + 0 :: Int
+    , id (id (id _)) + id _ :: Int
+    , id (negate _) + x :: Int
+    , id (negate _) + 0 :: Int
+    , id (negate _) + id _ :: Int
+    , id (abs _) + x :: Int
+    , id (abs _) + 0 :: Int
+    , id (abs _) + id _ :: Int
+    , id (_ + _) + x :: Int
+    , id (_ + _) + 0 :: Int
+    , id (_ + _) + id _ :: Int
+    , id (head _) + x :: Int
+    , id (head _) + 0 :: Int
+    , id (head _) + id _ :: Int
+    , id (ord _) + x :: Int
+    , id (ord _) + 0 :: Int
+    , id (ord _) + id _ :: Int
+    , negate x + x :: Int
+    , negate x + 0 :: Int
+    , negate x + id _ :: Int
+    , negate 0 + x :: Int
+    , negate 0 + 0 :: Int
+    , negate 0 + id _ :: Int
+    , negate (id _) + x :: Int
+    , negate (id _) + 0 :: Int
+    , negate (id _) + id _ :: Int
+    , abs x + x :: Int
+    , abs x + 0 :: Int
+    , abs x + id _ :: Int
+    , abs 0 + x :: Int
+    , abs 0 + 0 :: Int
+    , abs 0 + id _ :: Int
+    , abs (id _) + x :: Int
+    , abs (id _) + 0 :: Int
+    , abs (id _) + id _ :: Int
+    , (_ + x) + x :: Int
+    , (_ + x) + 0 :: Int
+    , (_ + x) + id _ :: Int
+    , (_ + 0) + x :: Int
+    , (_ + 0) + 0 :: Int
+    , (_ + 0) + id _ :: Int
+    , (_ + id _) + x :: Int
+    , (_ + id _) + 0 :: Int
+    , (_ + id _) + id _ :: Int
+    , (x + _) + x :: Int
+    , (x + _) + 0 :: Int
+    , (x + _) + id _ :: Int
+    , (0 + _) + x :: Int
+    , (0 + _) + 0 :: Int
+    , (0 + _) + id _ :: Int
+    , (id _ + _) + x :: Int
+    , (id _ + _) + 0 :: Int
+    , (id _ + _) + id _ :: Int
+    , _ * _ + x :: Int
+    , _ * _ + 0 :: Int
+    , _ * _ + id _ :: Int
+    , f _ + x :: Int
+    , f _ + 0 :: Int
+    , f _ + id _ :: Int
+    , head xs + x :: Int
+    , head xs + 0 :: Int
+    , head xs + id _ :: Int
+    , head [] + x :: Int
+    , head [] + 0 :: Int
+    , head [] + id _ :: Int
+    , head (_:_) + x :: Int
+    , head (_:_) + 0 :: Int
+    , head (_:_) + id _ :: Int
+    , ord c + x :: Int
+    , ord c + 0 :: Int
+    , ord c + id _ :: Int
+    , ord 'a' + x :: Int
+    , ord 'a' + 0 :: Int
+    , ord 'a' + id _ :: Int
+    , y * x :: Int
+    , y * 0 :: Int
+    , y * id _ :: Int
+    , 1 * x :: Int
+    , 1 * 0 :: Int
+    , 1 * id _ :: Int
+    , id x * x :: Int
+    , id x * 0 :: Int
+    , id x * id _ :: Int
+    , id 0 * x :: Int
+    , id 0 * 0 :: Int
+    , id 0 * id _ :: Int
+    , id (id _) * x :: Int
+    , id (id _) * 0 :: Int
+    , id (id _) * id _ :: Int
+    , negate _ * x :: Int
+    , negate _ * 0 :: Int
+    , negate _ * id _ :: Int
+    , abs _ * x :: Int
+    , abs _ * 0 :: Int
+    , abs _ * id _ :: Int
+    , (_ + _) * x :: Int
+    , (_ + _) * 0 :: Int
+    , (_ + _) * id _ :: Int
+    , head _ * x :: Int
+    , head _ * 0 :: Int
+    , head _ * id _ :: Int
+    , ord _ * x :: Int
+    , ord _ * 0 :: Int
+    , ord _ * id _ :: Int
+    , h x :: Int
+    , h 0 :: Int
+    , h (id _) :: Int
+    , x' + _ :: Int
+    , 2 + _ :: Int
+    , id z + _ :: Int
+    , id (-1) + _ :: Int
+    , id (id y) + _ :: Int
+    , id (id 1) + _ :: Int
+    , id (id (id x)) + _ :: Int
+    , id (id (id 0)) + _ :: Int
+    , id (id (id (id _))) + _ :: Int
+    , id (id (negate _)) + _ :: Int
+    , id (id (abs _)) + _ :: Int
+    , id (id (_ + _)) + _ :: Int
+    , id (id (head _)) + _ :: Int
+    , id (id (ord _)) + _ :: Int
+    , id (negate x) + _ :: Int
+    , id (negate 0) + _ :: Int
+    , id (negate (id _)) + _ :: Int
+    , id (abs x) + _ :: Int
+    , id (abs 0) + _ :: Int
+    , id (abs (id _)) + _ :: Int
+    , id (_ + x) + _ :: Int
+    , id (_ + 0) + _ :: Int
+    , id (_ + id _) + _ :: Int
+    , id (x + _) + _ :: Int
+    , id (0 + _) + _ :: Int
+    , id (id _ + _) + _ :: Int
+    , id (_ * _) + _ :: Int
+    , id (f _) + _ :: Int
+    , id (head xs) + _ :: Int
+    , id (head []) + _ :: Int
+    , id (head (_:_)) + _ :: Int
+    , id (ord c) + _ :: Int
+    , id (ord 'a') + _ :: Int
+    , negate y + _ :: Int
+    , negate 1 + _ :: Int
+    , negate (id x) + _ :: Int
+    , negate (id 0) + _ :: Int
+    , negate (id (id _)) + _ :: Int
+    , negate (negate _) + _ :: Int
+    , negate (abs _) + _ :: Int
+    , negate (_ + _) + _ :: Int
+    , negate (head _) + _ :: Int
+    , negate (ord _) + _ :: Int
+    , abs y + _ :: Int
+    , abs 1 + _ :: Int
+    , abs (id x) + _ :: Int
+    , abs (id 0) + _ :: Int
+    , abs (id (id _)) + _ :: Int
+    , abs (negate _) + _ :: Int
+    , abs (abs _) + _ :: Int
+    , abs (_ + _) + _ :: Int
+    , abs (head _) + _ :: Int
+    , abs (ord _) + _ :: Int
+    , (_ + y) + _ :: Int
+    , (_ + 1) + _ :: Int
+    , (_ + id x) + _ :: Int
+    , (_ + id 0) + _ :: Int
+    , (_ + id (id _)) + _ :: Int
+    , (_ + negate _) + _ :: Int
+    , (_ + abs _) + _ :: Int
+    , (_ + (_ + _)) + _ :: Int
+    , (_ + head _) + _ :: Int
+    , (_ + ord _) + _ :: Int
+    , (x + x) + _ :: Int
+    , (x + 0) + _ :: Int
+    , (x + id _) + _ :: Int
+    , (0 + x) + _ :: Int
+    , (0 + 0) + _ :: Int
+    , (0 + id _) + _ :: Int
+    , (id _ + x) + _ :: Int
+    , (id _ + 0) + _ :: Int
+    , (id _ + id _) + _ :: Int
+    , _ * x + _ :: Int
+    , _ * 0 + _ :: Int
+    , _ * id _ + _ :: Int
+    , f x + _ :: Int
+    , f 0 + _ :: Int
+    , f (id _) + _ :: Int
+    , (y + _) + _ :: Int
+    , (1 + _) + _ :: Int
+    , (id x + _) + _ :: Int
+    , (id 0 + _) + _ :: Int
+    , (id (id _) + _) + _ :: Int
+    , (negate _ + _) + _ :: Int
+    , (abs _ + _) + _ :: Int
+    , ((_ + _) + _) + _ :: Int
+    , (head _ + _) + _ :: Int
+    , (ord _ + _) + _ :: Int
+    , x * _ + _ :: Int
+    , 0 * _ + _ :: Int
+    , id _ * _ + _ :: Int
+    , g _ + _ :: Int
+    , head ys + _ :: Int
+    , head [0] + _ :: Int
+    , head (_:xs) + _ :: Int
+    , head [_] + _ :: Int
+    , head (_:_:_) + _ :: Int
+    , head (x:_) + _ :: Int
+    , head (0:_) + _ :: Int
+    , head (id _:_) + _ :: Int
+    , head (tail _) + _ :: Int
+    , head (_ ++ _) + _ :: Int
+    , ord d + _ :: Int
+    , ord ' ' + _ :: Int
+    , z * _ :: Int
+    , (-1) * _ :: Int
+    , id y * _ :: Int
+    , id 1 * _ :: Int
+    , id (id x) * _ :: Int
+    , id (id 0) * _ :: Int
+    , id (id (id _)) * _ :: Int
+    , id (negate _) * _ :: Int
+    , id (abs _) * _ :: Int
+    , id (_ + _) * _ :: Int
+    , id (head _) * _ :: Int
+    , id (ord _) * _ :: Int
+    , negate x * _ :: Int
+    , negate 0 * _ :: Int
+    , negate (id _) * _ :: Int
+    , abs x * _ :: Int
+    , abs 0 * _ :: Int
+    , abs (id _) * _ :: Int
+    , (_ + x) * _ :: Int
+    , (_ + 0) * _ :: Int
+    , (_ + id _) * _ :: Int
+    , (x + _) * _ :: Int
+    , (0 + _) * _ :: Int
+    , (id _ + _) * _ :: Int
+    , (_ * _) * _ :: Int
+    , f _ * _ :: Int
+    , head xs * _ :: Int
+    , head [] * _ :: Int
+    , head (_:_) * _ :: Int
+    , ord c * _ :: Int
+    , ord 'a' * _ :: Int
+    , f' _ :: Int
+    , head xs' :: Int
+    , head [0,0,0] :: Int
+    , head [0,1] :: Int
+    , head [1,0] :: Int
+    , head [-1] :: Int
+    , head (_:zs) :: Int
+    , head [_,0,0] :: Int
+    , head [_,1] :: Int
+    , head (_:_:ys) :: Int
+    , head [_,_,0] :: Int
+    , head (_:_:_:xs) :: Int
+    , head [_,_,_] :: Int
+    , head (_:_:_:_:_) :: Int
+    , head (_:_:x:_) :: Int
+    , head (_:_:0:_) :: Int
+    , head (_:_:id _:_) :: Int
+    , head (_:_:tail _) :: Int
+    , head (_:_:(_ ++ _)) :: Int
+    , head (_:x:xs) :: Int
+    , head [_,x] :: Int
+    , head (_:x:_:_) :: Int
+    , head (_:0:xs) :: Int
+    , head [_,0] :: Int
+    , head (_:0:_:_) :: Int
+    , head (_:id _:xs) :: Int
+    , head [_,id _] :: Int
+    , head (_:id _:_:_) :: Int
+    , head (_:y:_) :: Int
+    , head (_:1:_) :: Int
+    , head (_:id x:_) :: Int
+    , head (_:id 0:_) :: Int
+    , head (_:id (id _):_) :: Int
+    , head (_:negate _:_) :: Int
+    , head (_:abs _:_) :: Int
+    , head (_:_ + _:_) :: Int
+    , head (_:head _:_) :: Int
+    , head (_:ord _:_) :: Int
+    , head (_:tail xs) :: Int
+    , head (_:tail []) :: Int
+    , head (_:tail (_:_)) :: Int
+    , head (_:(_ ++ xs)) :: Int
+    , head (_:(_ ++ [])) :: Int
+    , head (_:(_ ++ (_:_))) :: Int
+    , head (_:(xs ++ _)) :: Int
+    , head (_:([] ++ _)) :: Int
+    , head (_:((_:_) ++ _)) :: Int
+    , head (_:sort _) :: Int
+    , head (_:insert _ _) :: Int
+    , head (x:ys) :: Int
+    , head [x,0] :: Int
+    , head (x:_:xs) :: Int
+    , head [x,_] :: Int
+    , head (x:_:_:_) :: Int
+    , head (x:x:_) :: Int
+    , head (x:0:_) :: Int
+    , head (x:id _:_) :: Int
+    , head (x:tail _) :: Int
+    , head (x:(_ ++ _)) :: Int
+    , head (0:ys) :: Int
+    , head [0,0] :: Int
+    , head (0:_:xs) :: Int
+    , head [0,_] :: Int
+    , head (0:_:_:_) :: Int
+    , head (0:x:_) :: Int
+    , head (0:0:_) :: Int
+    , head (0:id _:_) :: Int
+    , head (0:tail _) :: Int
+    , head (0:(_ ++ _)) :: Int
+    , head (id _:ys) :: Int
+    , head [id _,0] :: Int
+    , head (id _:_:xs) :: Int
+    , head [id _,_] :: Int
+    , head (id _:_:_:_) :: Int
+    , head (id _:x:_) :: Int
+    , head (id _:0:_) :: Int
+    , head (id _:id _:_) :: Int
+    , head (id _:tail _) :: Int
+    , head (id _:(_ ++ _)) :: Int
+    , head (y:xs) :: Int
+    , head [y] :: Int
+    , head (y:_:_) :: Int
+    , head (1:xs) :: Int
+    , head [1] :: Int
+    , head (1:_:_) :: Int
+    , head (id x:xs) :: Int
+    , head [id x] :: Int
+    , head (id x:_:_) :: Int
+    , head (id 0:xs) :: Int
+    , head [id 0] :: Int
+    , head (id 0:_:_) :: Int
+    , head (id (id _):xs) :: Int
+    , head [id (id _)] :: Int
+    , head (id (id _):_:_) :: Int
+    , head (negate _:xs) :: Int
+    , head [negate _] :: Int
+    , head (negate _:_:_) :: Int
+    , head (abs _:xs) :: Int
+    , head [abs _] :: Int
+    , head (abs _:_:_) :: Int
+    , head (_ + _:xs) :: Int
+    , head [_ + _] :: Int
+    , head (_ + _:_:_) :: Int
+    , head (head _:xs) :: Int
+    , head [head _] :: Int
+    , head (head _:_:_) :: Int
+    , head (ord _:xs) :: Int
+    , head [ord _] :: Int
+    , head (ord _:_:_) :: Int
+    , head (z:_) :: Int
+    , head ((-1):_) :: Int
+    , head (id y:_) :: Int
+    , head (id 1:_) :: Int
+    , head (id (id x):_) :: Int
+    , head (id (id 0):_) :: Int
+    , head (id (id (id _)):_) :: Int
+    , head (id (negate _):_) :: Int
+    , head (id (abs _):_) :: Int
+    , head (id (_ + _):_) :: Int
+    , head (id (head _):_) :: Int
+    , head (id (ord _):_) :: Int
+    , head (negate x:_) :: Int
+    , head (negate 0:_) :: Int
+    , head (negate (id _):_) :: Int
+    , head (abs x:_) :: Int
+    , head (abs 0:_) :: Int
+    , head (abs (id _):_) :: Int
+    , head (_ + x:_) :: Int
+    , head (_ + 0:_) :: Int
+    , head (_ + id _:_) :: Int
+    , head (x + _:_) :: Int
+    , head (0 + _:_) :: Int
+    , head (id _ + _:_) :: Int
+    , head (_ * _:_) :: Int
+    , head (f _:_) :: Int
+    , head (head xs:_) :: Int
+    , head (head []:_) :: Int
+    , head (head (_:_):_) :: Int
+    , head (ord c:_) :: Int
+    , head (ord 'a':_) :: Int
+    , head (tail ys) :: Int
+    , head (tail [0]) :: Int
+    , head (tail (_:xs)) :: Int
+    , head (tail [_]) :: Int
+    , head (tail (_:_:_)) :: Int
+    , head (tail (x:_)) :: Int
+    , head (tail (0:_)) :: Int
+    , head (tail (id _:_)) :: Int
+    , head (tail (tail _)) :: Int
+    , head (tail (_ ++ _)) :: Int
+    , head (_ ++ ys) :: Int
+    , head (_ ++ [0]) :: Int
+    , head (_ ++ (_:xs)) :: Int
+    , head (_ ++ [_]) :: Int
+    , head (_ ++ (_:_:_)) :: Int
+    , head (_ ++ (x:_)) :: Int
+    , head (_ ++ (0:_)) :: Int
+    , head (_ ++ (id _:_)) :: Int
+    , head (_ ++ tail _) :: Int
+    , head (_ ++ (_ ++ _)) :: Int
+    , head (xs ++ xs) :: Int
+    , head (xs ++ []) :: Int
+    , head (xs ++ (_:_)) :: Int
+    , head ([] ++ xs) :: Int
+    , head ([] ++ []) :: Int
+    , head ([] ++ (_:_)) :: Int
+    , head ((_:_) ++ xs) :: Int
+    , head ((_:_) ++ []) :: Int
+    , head ((_:_) ++ (_:_)) :: Int
+    , head (ys ++ _) :: Int
+    , head ([0] ++ _) :: Int
+    , head ((_:xs) ++ _) :: Int
+    , head ([_] ++ _) :: Int
+    , head ((_:_:_) ++ _) :: Int
+    , head ((x:_) ++ _) :: Int
+    , head ((0:_) ++ _) :: Int
+    , head ((id _:_) ++ _) :: Int
+    , head (tail _ ++ _) :: Int
+    , head ((_ ++ _) ++ _) :: Int
+    , head (sort xs) :: Int
+    , head (sort []) :: Int
+    , head (sort (_:_)) :: Int
+    , head (insert _ xs) :: Int
+    , head (insert _ []) :: Int
+    , head (insert _ (_:_)) :: Int
+    , head (insert x _) :: Int
+    , head (insert 0 _) :: Int
+    , head (insert (id _) _) :: Int
+    , ord c' :: Int
+    , ord 'A' :: Int
+    , q' :: Bool
+    , not p' :: Bool
+    , not (not r) :: Bool
+    , not (not (not q)) :: Bool
+    , not (not (not (not p))) :: Bool
+    , not (not (not (not False))) :: Bool
+    , not (not (not (not True))) :: Bool
+    , not (not (not (not (not _)))) :: Bool
+    , not (not (not (_ || _))) :: Bool
+    , not (not (_ || p)) :: Bool
+    , not (not (_ || False)) :: Bool
+    , not (not (_ || True)) :: Bool
+    , not (not (_ || not _)) :: Bool
+    , not (not (p || _)) :: Bool
+    , not (not (False || _)) :: Bool
+    , not (not (True || _)) :: Bool
+    , not (not (not _ || _)) :: Bool
+    , not (not (_ && _)) :: Bool
+    , not (not (_ == _)) :: Bool
+    , not (not (_ == _)) :: Bool
+    , not (not (odd _)) :: Bool
+    , not (not (even _)) :: Bool
+    , not (not (elem _ _)) :: Bool
+    , not (_ || q) :: Bool
+    , not (_ || not p) :: Bool
+    , not (_ || not False) :: Bool
+    , not (_ || not True) :: Bool
+    , not (_ || not (not _)) :: Bool
+    , not (_ || (_ || _)) :: Bool
+    , not (p || p) :: Bool
+    , not (p || False) :: Bool
+    , not (p || True) :: Bool
+    , not (p || not _) :: Bool
+    , not (False || p) :: Bool
+    , not (False || False) :: Bool
+    , not (False || True) :: Bool
+    , not (False || not _) :: Bool
+    , not (True || p) :: Bool
+    , not (True || False) :: Bool
+    , not (True || True) :: Bool
+    , not (True || not _) :: Bool
+    , not (not _ || p) :: Bool
+    , not (not _ || False) :: Bool
+    , not (not _ || True) :: Bool
+    , not (not _ || not _) :: Bool
+    , not (_ && p) :: Bool
+    , not (_ && False) :: Bool
+    , not (_ && True) :: Bool
+    , not (_ && not _) :: Bool
+    , not (q || _) :: Bool
+    , not (not p || _) :: Bool
+    , not (not False || _) :: Bool
+    , not (not True || _) :: Bool
+    , not (not (not _) || _) :: Bool
+    , not ((_ || _) || _) :: Bool
+    , not (p && _) :: Bool
+    , not (False && _) :: Bool
+    , not (True && _) :: Bool
+    , not (not _ && _) :: Bool
+    , not (_ ==> _) :: Bool
+    , not (_ == x) :: Bool
+    , not (_ == 0) :: Bool
+    , not (_ == id _) :: Bool
+    , not (x == _) :: Bool
+    , not (0 == _) :: Bool
+    , not (id _ == _) :: Bool
+    , not (_ == p) :: Bool
+    , not (_ == False) :: Bool
+    , not (_ == True) :: Bool
+    , not (_ == not _) :: Bool
+    , not (p == _) :: Bool
+    , not (False == _) :: Bool
+    , not (True == _) :: Bool
+    , not (not _ == _) :: Bool
+    , not (_ <= _) :: Bool
+    , not (_ <= _) :: Bool
+    , not (odd x) :: Bool
+    , not (odd 0) :: Bool
+    , not (odd (id _)) :: Bool
+    , not (even x) :: Bool
+    , not (even 0) :: Bool
+    , not (even (id _)) :: Bool
+    , not (elem _ xs) :: Bool
+    , not (elem _ []) :: Bool
+    , not (elem _ (_:_)) :: Bool
+    , not (elem x _) :: Bool
+    , not (elem 0 _) :: Bool
+    , not (elem (id _) _) :: Bool
+    , _ || r :: Bool
+    , _ || not q :: Bool
+    , _ || not (not p) :: Bool
+    , _ || not (not False) :: Bool
+    , _ || not (not True) :: Bool
+    , _ || not (not (not _)) :: Bool
+    , _ || not (_ || _) :: Bool
+    , _ || (_ || p) :: Bool
+    , _ || (_ || False) :: Bool
+    , _ || (_ || True) :: Bool
+    , _ || (_ || not _) :: Bool
+    , _ || (p || _) :: Bool
+    , _ || (False || _) :: Bool
+    , _ || (True || _) :: Bool
+    , _ || (not _ || _) :: Bool
+    , _ || _ && _ :: Bool
+    , _ || _ == _ :: Bool
+    , _ || _ == _ :: Bool
+    , _ || odd _ :: Bool
+    , _ || even _ :: Bool
+    , _ || elem _ _ :: Bool
+    , p || q :: Bool
+    , p || not p :: Bool
+    , p || not False :: Bool
+    , p || not True :: Bool
+    , p || not (not _) :: Bool
+    , p || (_ || _) :: Bool
+    , False || q :: Bool
+    , False || not p :: Bool
+    , False || not False :: Bool
+    , False || not True :: Bool
+    , False || not (not _) :: Bool
+    , False || (_ || _) :: Bool
+    , True || q :: Bool
+    , True || not p :: Bool
+    , True || not False :: Bool
+    , True || not True :: Bool
+    , True || not (not _) :: Bool
+    , True || (_ || _) :: Bool
+    , not _ || q :: Bool
+    , not _ || not p :: Bool
+    , not _ || not False :: Bool
+    , not _ || not True :: Bool
+    , not _ || not (not _) :: Bool
+    , not _ || (_ || _) :: Bool
+    , _ && q :: Bool
+    , _ && not p :: Bool
+    , _ && not False :: Bool
+    , _ && not True :: Bool
+    , _ && not (not _) :: Bool
+    , _ && (_ || _) :: Bool
+    , q || p :: Bool
+    , q || False :: Bool
+    , q || True :: Bool
+    , q || not _ :: Bool
+    , not p || p :: Bool
+    , not p || False :: Bool
+    , not p || True :: Bool
+    , not p || not _ :: Bool
+    , not False || p :: Bool
+    , not False || False :: Bool
+    , not False || True :: Bool
+    , not False || not _ :: Bool
+    , not True || p :: Bool
+    , not True || False :: Bool
+    , not True || True :: Bool
+    , not True || not _ :: Bool
+    , not (not _) || p :: Bool
+    , not (not _) || False :: Bool
+    , not (not _) || True :: Bool
+    , not (not _) || not _ :: Bool
+    , (_ || _) || p :: Bool
+    , (_ || _) || False :: Bool
+    , (_ || _) || True :: Bool
+    , (_ || _) || not _ :: Bool
+    , p && p :: Bool
+    , p && False :: Bool
+    , p && True :: Bool
+    , p && not _ :: Bool
+    , False && p :: Bool
+    , False && False :: Bool
+    , False && True :: Bool
+    , False && not _ :: Bool
+    , True && p :: Bool
+    , True && False :: Bool
+    , True && True :: Bool
+    , True && not _ :: Bool
+    , not _ && p :: Bool
+    , not _ && False :: Bool
+    , not _ && True :: Bool
+    , not _ && not _ :: Bool
+    , _ ==> p :: Bool
+    , _ ==> False :: Bool
+    , _ ==> True :: Bool
+    , _ ==> not _ :: Bool
+    , r || _ :: Bool
+    , not q || _ :: Bool
+    , not (not p) || _ :: Bool
+    , not (not False) || _ :: Bool
+    , not (not True) || _ :: Bool
+    , not (not (not _)) || _ :: Bool
+    , not (_ || _) || _ :: Bool
+    , (_ || p) || _ :: Bool
+    , (_ || False) || _ :: Bool
+    , (_ || True) || _ :: Bool
+    , (_ || not _) || _ :: Bool
+    , (p || _) || _ :: Bool
+    , (False || _) || _ :: Bool
+    , (True || _) || _ :: Bool
+    , (not _ || _) || _ :: Bool
+    , _ && _ || _ :: Bool
+    , _ == _ || _ :: Bool
+    , _ == _ || _ :: Bool
+    , odd _ || _ :: Bool
+    , even _ || _ :: Bool
+    , elem _ _ || _ :: Bool
+    , q && _ :: Bool
+    , not p && _ :: Bool
+    , not False && _ :: Bool
+    , not True && _ :: Bool
+    , not (not _) && _ :: Bool
+    , (_ || _) && _ :: Bool
+    , p ==> _ :: Bool
+    , False ==> _ :: Bool
+    , True ==> _ :: Bool
+    , not _ ==> _ :: Bool
+    , _ == y :: Bool
+    , _ == 1 :: Bool
+    , _ == id x :: Bool
+    , _ == id 0 :: Bool
+    , _ == id (id _) :: Bool
+    , _ == negate _ :: Bool
+    , _ == abs _ :: Bool
+    , _ == _ + _ :: Bool
+    , _ == head _ :: Bool
+    , _ == ord _ :: Bool
+    , x == x :: Bool
+    , x == 0 :: Bool
+    , x == id _ :: Bool
+    , 0 == x :: Bool
+    , 0 == 0 :: Bool
+    , 0 == id _ :: Bool
+    , id _ == x :: Bool
+    , id _ == 0 :: Bool
+    , id _ == id _ :: Bool
+    , y == _ :: Bool
+    , 1 == _ :: Bool
+    , id x == _ :: Bool
+    , id 0 == _ :: Bool
+    , id (id _) == _ :: Bool
+    , negate _ == _ :: Bool
+    , abs _ == _ :: Bool
+    , _ + _ == _ :: Bool
+    , head _ == _ :: Bool
+    , ord _ == _ :: Bool
+    , _ == q :: Bool
+    , _ == not p :: Bool
+    , _ == not False :: Bool
+    , _ == not True :: Bool
+    , _ == not (not _) :: Bool
+    , _ == (_ || _) :: Bool
+    , p == p :: Bool
+    , p == False :: Bool
+    , p == True :: Bool
+    , p == not _ :: Bool
+    , False == p :: Bool
+    , False == False :: Bool
+    , False == True :: Bool
+    , False == not _ :: Bool
+    , True == p :: Bool
+    , True == False :: Bool
+    , True == True :: Bool
+    , True == not _ :: Bool
+    , not _ == p :: Bool
+    , not _ == False :: Bool
+    , not _ == True :: Bool
+    , not _ == not _ :: Bool
+    , q == _ :: Bool
+    , not p == _ :: Bool
+    , not False == _ :: Bool
+    , not True == _ :: Bool
+    , not (not _) == _ :: Bool
+    , (_ || _) == _ :: Bool
+    , _ <= x :: Bool
+    , _ <= 0 :: Bool
+    , _ <= id _ :: Bool
+    , x <= _ :: Bool
+    , 0 <= _ :: Bool
+    , id _ <= _ :: Bool
+    , _ <= p :: Bool
+    , _ <= False :: Bool
+    , _ <= True :: Bool
+    , _ <= not _ :: Bool
+    , p <= _ :: Bool
+    , False <= _ :: Bool
+    , True <= _ :: Bool
+    , not _ <= _ :: Bool
+    , _ < _ :: Bool
+    , _ < _ :: Bool
+    , odd y :: Bool
+    , odd 1 :: Bool
+    , odd (id x) :: Bool
+    , odd (id 0) :: Bool
+    , odd (id (id _)) :: Bool
+    , odd (negate _) :: Bool
+    , odd (abs _) :: Bool
+    , odd (_ + _) :: Bool
+    , odd (head _) :: Bool
+    , odd (ord _) :: Bool
+    , even y :: Bool
+    , even 1 :: Bool
+    , even (id x) :: Bool
+    , even (id 0) :: Bool
+    , even (id (id _)) :: Bool
+    , even (negate _) :: Bool
+    , even (abs _) :: Bool
+    , even (_ + _) :: Bool
+    , even (head _) :: Bool
+    , even (ord _) :: Bool
+    , elem _ ys :: Bool
+    , elem _ [0] :: Bool
+    , elem _ (_:xs) :: Bool
+    , elem _ [_] :: Bool
+    , elem _ (_:_:_) :: Bool
+    , elem _ (x:_) :: Bool
+    , elem _ (0:_) :: Bool
+    , elem _ (id _:_) :: Bool
+    , elem _ (tail _) :: Bool
+    , elem _ (_ ++ _) :: Bool
+    , elem x xs :: Bool
+    , elem x [] :: Bool
+    , elem x (_:_) :: Bool
+    , elem 0 xs :: Bool
+    , elem 0 [] :: Bool
+    , elem 0 (_:_) :: Bool
+    , elem (id _) xs :: Bool
+    , elem (id _) [] :: Bool
+    , elem (id _) (_:_) :: Bool
+    , elem y _ :: Bool
+    , elem 1 _ :: Bool
+    , elem (id x) _ :: Bool
+    , elem (id 0) _ :: Bool
+    , elem (id (id _)) _ :: Bool
+    , elem (negate _) _ :: Bool
+    , elem (abs _) _ :: Bool
+    , elem (_ + _) _ :: Bool
+    , elem (head _) _ :: Bool
+    , elem (ord _) _ :: Bool
+    , d' :: Char
+    , 'c' :: Char
+    , ys' :: [Int]
+    , [0,0,0,0] :: [Int]
+    , [0,0,1] :: [Int]
+    , [0,1,0] :: [Int]
+    , [0,-1] :: [Int]
+    , [1,0,0] :: [Int]
+    , [1,1] :: [Int]
+    , [-1,0] :: [Int]
+    , [2] :: [Int]
+    , _:xs' :: [Int]
+    , [_,0,0,0] :: [Int]
+    , [_,0,1] :: [Int]
+    , [_,1,0] :: [Int]
+    , [_,-1] :: [Int]
+    , _:_:zs :: [Int]
+    , [_,_,0,0] :: [Int]
+    , [_,_,1] :: [Int]
+    , _:_:_:ys :: [Int]
+    , [_,_,_,0] :: [Int]
+    , _:_:_:_:xs :: [Int]
+    , [_,_,_,_] :: [Int]
+    , _:_:_:_:_:_ :: [Int]
+    , _:_:_:x:_ :: [Int]
+    , _:_:_:0:_ :: [Int]
+    , _:_:_:id _:_ :: [Int]
+    , _:_:_:tail _ :: [Int]
+    , _:_:_:(_ ++ _) :: [Int]
+    , _:_:x:xs :: [Int]
+    , [_,_,x] :: [Int]
+    , _:_:x:_:_ :: [Int]
+    , _:_:0:xs :: [Int]
+    , [_,_,0] :: [Int]
+    , _:_:0:_:_ :: [Int]
+    , _:_:id _:xs :: [Int]
+    , [_,_,id _] :: [Int]
+    , _:_:id _:_:_ :: [Int]
+    , _:_:y:_ :: [Int]
+    , _:_:1:_ :: [Int]
+    , _:_:id x:_ :: [Int]
+    , _:_:id 0:_ :: [Int]
+    , _:_:id (id _):_ :: [Int]
+    , _:_:negate _:_ :: [Int]
+    , _:_:abs _:_ :: [Int]
+    , _:_:_ + _:_ :: [Int]
+    , _:_:head _:_ :: [Int]
+    , _:_:ord _:_ :: [Int]
+    , _:_:tail xs :: [Int]
+    , _:_:tail [] :: [Int]
+    , _:_:tail (_:_) :: [Int]
+    , _:_:(_ ++ xs) :: [Int]
+    , _:_:(_ ++ []) :: [Int]
+    , _:_:(_ ++ (_:_)) :: [Int]
+    , _:_:(xs ++ _) :: [Int]
+    , [_,_,] ++ _ :: [Int]
+    , _:_:((_:_) ++ _) :: [Int]
+    , _:_:sort _ :: [Int]
+    , _:_:insert _ _ :: [Int]
+    , _:x:ys :: [Int]
+    , [_,x,0] :: [Int]
+    , _:x:_:xs :: [Int]
+    , [_,x,_] :: [Int]
+    , _:x:_:_:_ :: [Int]
+    , _:x:x:_ :: [Int]
+    , _:x:0:_ :: [Int]
+    , _:x:id _:_ :: [Int]
+    , _:x:tail _ :: [Int]
+    , _:x:(_ ++ _) :: [Int]
+    , _:0:ys :: [Int]
+    , [_,0,0] :: [Int]
+    , _:0:_:xs :: [Int]
+    , [_,0,_] :: [Int]
+    , _:0:_:_:_ :: [Int]
+    , _:0:x:_ :: [Int]
+    , _:0:0:_ :: [Int]
+    , _:0:id _:_ :: [Int]
+    , _:0:tail _ :: [Int]
+    , _:0:(_ ++ _) :: [Int]
+    , _:id _:ys :: [Int]
+    , [_,id _,0] :: [Int]
+    , _:id _:_:xs :: [Int]
+    , [_,id _,_] :: [Int]
+    , _:id _:_:_:_ :: [Int]
+    , _:id _:x:_ :: [Int]
+    , _:id _:0:_ :: [Int]
+    , _:id _:id _:_ :: [Int]
+    , _:id _:tail _ :: [Int]
+    , _:id _:(_ ++ _) :: [Int]
+    , _:y:xs :: [Int]
+    , [_,y] :: [Int]
+    , _:y:_:_ :: [Int]
+    , _:1:xs :: [Int]
+    , [_,1] :: [Int]
+    , _:1:_:_ :: [Int]
+    , _:id x:xs :: [Int]
+    , [_,id x] :: [Int]
+    , _:id x:_:_ :: [Int]
+    , _:id 0:xs :: [Int]
+    , [_,id 0] :: [Int]
+    , _:id 0:_:_ :: [Int]
+    , _:id (id _):xs :: [Int]
+    , [_,id (id _)] :: [Int]
+    , _:id (id _):_:_ :: [Int]
+    , _:negate _:xs :: [Int]
+    , [_,negate _] :: [Int]
+    , _:negate _:_:_ :: [Int]
+    , _:abs _:xs :: [Int]
+    , [_,abs _] :: [Int]
+    , _:abs _:_:_ :: [Int]
+    , _:_ + _:xs :: [Int]
+    , [_,_ + _] :: [Int]
+    , _:_ + _:_:_ :: [Int]
+    , _:head _:xs :: [Int]
+    , [_,head _] :: [Int]
+    , _:head _:_:_ :: [Int]
+    , _:ord _:xs :: [Int]
+    , [_,ord _] :: [Int]
+    , _:ord _:_:_ :: [Int]
+    , _:z:_ :: [Int]
+    , _:(-1):_ :: [Int]
+    , _:id y:_ :: [Int]
+    , _:id 1:_ :: [Int]
+    , _:id (id x):_ :: [Int]
+    , _:id (id 0):_ :: [Int]
+    , _:id (id (id _)):_ :: [Int]
+    , _:id (negate _):_ :: [Int]
+    , _:id (abs _):_ :: [Int]
+    , _:id (_ + _):_ :: [Int]
+    , _:id (head _):_ :: [Int]
+    , _:id (ord _):_ :: [Int]
+    , _:negate x:_ :: [Int]
+    , _:negate 0:_ :: [Int]
+    , _:negate (id _):_ :: [Int]
+    , _:abs x:_ :: [Int]
+    , _:abs 0:_ :: [Int]
+    , _:abs (id _):_ :: [Int]
+    , _:_ + x:_ :: [Int]
+    , _:_ + 0:_ :: [Int]
+    , _:_ + id _:_ :: [Int]
+    , _:x + _:_ :: [Int]
+    , _:0 + _:_ :: [Int]
+    , _:id _ + _:_ :: [Int]
+    , _:_ * _:_ :: [Int]
+    , _:f _:_ :: [Int]
+    , _:head xs:_ :: [Int]
+    , _:head []:_ :: [Int]
+    , _:head (_:_):_ :: [Int]
+    , _:ord c:_ :: [Int]
+    , _:ord 'a':_ :: [Int]
+    , _:tail ys :: [Int]
+    , _:tail [0] :: [Int]
+    , _:tail (_:xs) :: [Int]
+    , _:tail [_] :: [Int]
+    , _:tail (_:_:_) :: [Int]
+    , _:tail (x:_) :: [Int]
+    , _:tail (0:_) :: [Int]
+    , _:tail (id _:_) :: [Int]
+    , _:tail (tail _) :: [Int]
+    , _:tail (_ ++ _) :: [Int]
+    , _:(_ ++ ys) :: [Int]
+    , _:(_ ++ [0]) :: [Int]
+    , _:(_ ++ (_:xs)) :: [Int]
+    , _:(_ ++ [_]) :: [Int]
+    , _:(_ ++ (_:_:_)) :: [Int]
+    , _:(_ ++ (x:_)) :: [Int]
+    , _:(_ ++ (0:_)) :: [Int]
+    , _:(_ ++ (id _:_)) :: [Int]
+    , _:(_ ++ tail _) :: [Int]
+    , _:(_ ++ (_ ++ _)) :: [Int]
+    , _:(xs ++ xs) :: [Int]
+    , _:(xs ++ []) :: [Int]
+    , _:(xs ++ (_:_)) :: [Int]
+    , _:([] ++ xs) :: [Int]
+    , _:([] ++ []) :: [Int]
+    , _:([] ++ (_:_)) :: [Int]
+    , _:((_:_) ++ xs) :: [Int]
+    , _:((_:_) ++ []) :: [Int]
+    , _:((_:_) ++ (_:_)) :: [Int]
+    , _:(ys ++ _) :: [Int]
+    , _:([0] ++ _) :: [Int]
+    , _:((_:xs) ++ _) :: [Int]
+    , _:([_] ++ _) :: [Int]
+    , _:((_:_:_) ++ _) :: [Int]
+    , _:((x:_) ++ _) :: [Int]
+    , _:((0:_) ++ _) :: [Int]
+    , _:((id _:_) ++ _) :: [Int]
+    , _:(tail _ ++ _) :: [Int]
+    , _:((_ ++ _) ++ _) :: [Int]
+    , _:sort xs :: [Int]
+    , _:sort [] :: [Int]
+    , _:sort (_:_) :: [Int]
+    , _:insert _ xs :: [Int]
+    , _:insert _ [] :: [Int]
+    , _:insert _ (_:_) :: [Int]
+    , _:insert x _ :: [Int]
+    , _:insert 0 _ :: [Int]
+    , _:insert (id _) _ :: [Int]
+    , x:zs :: [Int]
+    , [x,0,0] :: [Int]
+    , [x,1] :: [Int]
+    , x:_:ys :: [Int]
+    , [x,_,0] :: [Int]
+    , x:_:_:xs :: [Int]
+    , [x,_,_] :: [Int]
+    , x:_:_:_:_ :: [Int]
+    , x:_:x:_ :: [Int]
+    , x:_:0:_ :: [Int]
+    , x:_:id _:_ :: [Int]
+    , x:_:tail _ :: [Int]
+    , x:_:(_ ++ _) :: [Int]
+    , x:x:xs :: [Int]
+    , [x,x] :: [Int]
+    , x:x:_:_ :: [Int]
+    , x:0:xs :: [Int]
+    , [x,0] :: [Int]
+    , x:0:_:_ :: [Int]
+    , x:id _:xs :: [Int]
+    , [x,id _] :: [Int]
+    , x:id _:_:_ :: [Int]
+    , x:y:_ :: [Int]
+    , x:1:_ :: [Int]
+    , x:id x:_ :: [Int]
+    , x:id 0:_ :: [Int]
+    , x:id (id _):_ :: [Int]
+    , x:negate _:_ :: [Int]
+    , x:abs _:_ :: [Int]
+    , x:_ + _:_ :: [Int]
+    , x:head _:_ :: [Int]
+    , x:ord _:_ :: [Int]
+    , x:tail xs :: [Int]
+    , x:tail [] :: [Int]
+    , x:tail (_:_) :: [Int]
+    , x:(_ ++ xs) :: [Int]
+    , x:(_ ++ []) :: [Int]
+    , x:(_ ++ (_:_)) :: [Int]
+    , x:(xs ++ _) :: [Int]
+    , x:([] ++ _) :: [Int]
+    , x:((_:_) ++ _) :: [Int]
+    , x:sort _ :: [Int]
+    , x:insert _ _ :: [Int]
+    , 0:zs :: [Int]
+    , [0,0,0] :: [Int]
+    , [0,1] :: [Int]
+    , 0:_:ys :: [Int]
+    , [0,_,0] :: [Int]
+    , 0:_:_:xs :: [Int]
+    , [0,_,_] :: [Int]
+    , 0:_:_:_:_ :: [Int]
+    , 0:_:x:_ :: [Int]
+    , 0:_:0:_ :: [Int]
+    , 0:_:id _:_ :: [Int]
+    , 0:_:tail _ :: [Int]
+    , 0:_:(_ ++ _) :: [Int]
+    , 0:x:xs :: [Int]
+    , [0,x] :: [Int]
+    , 0:x:_:_ :: [Int]
+    , 0:0:xs :: [Int]
+    , [0,0] :: [Int]
+    , 0:0:_:_ :: [Int]
+    , 0:id _:xs :: [Int]
+    , [0,id _] :: [Int]
+    , 0:id _:_:_ :: [Int]
+    , 0:y:_ :: [Int]
+    , 0:1:_ :: [Int]
+    , 0:id x:_ :: [Int]
+    , 0:id 0:_ :: [Int]
+    , 0:id (id _):_ :: [Int]
+    , 0:negate _:_ :: [Int]
+    , 0:abs _:_ :: [Int]
+    , 0:_ + _:_ :: [Int]
+    , 0:head _:_ :: [Int]
+    , 0:ord _:_ :: [Int]
+    , 0:tail xs :: [Int]
+    , 0:tail [] :: [Int]
+    , 0:tail (_:_) :: [Int]
+    , 0:(_ ++ xs) :: [Int]
+    , 0:(_ ++ []) :: [Int]
+    , 0:(_ ++ (_:_)) :: [Int]
+    , 0:(xs ++ _) :: [Int]
+    , 0:([] ++ _) :: [Int]
+    , 0:((_:_) ++ _) :: [Int]
+    , 0:sort _ :: [Int]
+    , 0:insert _ _ :: [Int]
+    , id _:zs :: [Int]
+    , [id _,0,0] :: [Int]
+    , [id _,1] :: [Int]
+    , id _:_:ys :: [Int]
+    , [id _,_,0] :: [Int]
+    , id _:_:_:xs :: [Int]
+    , [id _,_,_] :: [Int]
+    , id _:_:_:_:_ :: [Int]
+    , id _:_:x:_ :: [Int]
+    , id _:_:0:_ :: [Int]
+    , id _:_:id _:_ :: [Int]
+    , id _:_:tail _ :: [Int]
+    , id _:_:(_ ++ _) :: [Int]
+    , id _:x:xs :: [Int]
+    , [id _,x] :: [Int]
+    , id _:x:_:_ :: [Int]
+    , id _:0:xs :: [Int]
+    , [id _,0] :: [Int]
+    , id _:0:_:_ :: [Int]
+    , id _:id _:xs :: [Int]
+    , [id _,id _] :: [Int]
+    , id _:id _:_:_ :: [Int]
+    , id _:y:_ :: [Int]
+    , id _:1:_ :: [Int]
+    , id _:id x:_ :: [Int]
+    , id _:id 0:_ :: [Int]
+    , id _:id (id _):_ :: [Int]
+    , id _:negate _:_ :: [Int]
+    , id _:abs _:_ :: [Int]
+    , id _:_ + _:_ :: [Int]
+    , id _:head _:_ :: [Int]
+    , id _:ord _:_ :: [Int]
+    , id _:tail xs :: [Int]
+    , id _:tail [] :: [Int]
+    , id _:tail (_:_) :: [Int]
+    , id _:(_ ++ xs) :: [Int]
+    , id _:(_ ++ []) :: [Int]
+    , id _:(_ ++ (_:_)) :: [Int]
+    , id _:(xs ++ _) :: [Int]
+    , id _:([] ++ _) :: [Int]
+    , id _:((_:_) ++ _) :: [Int]
+    , id _:sort _ :: [Int]
+    , id _:insert _ _ :: [Int]
+    , y:ys :: [Int]
+    , [y,0] :: [Int]
+    , y:_:xs :: [Int]
+    , [y,_] :: [Int]
+    , y:_:_:_ :: [Int]
+    , y:x:_ :: [Int]
+    , y:0:_ :: [Int]
+    , y:id _:_ :: [Int]
+    , y:tail _ :: [Int]
+    , y:(_ ++ _) :: [Int]
+    , 1:ys :: [Int]
+    , [1,0] :: [Int]
+    , 1:_:xs :: [Int]
+    , [1,_] :: [Int]
+    , 1:_:_:_ :: [Int]
+    , 1:x:_ :: [Int]
+    , 1:0:_ :: [Int]
+    , 1:id _:_ :: [Int]
+    , 1:tail _ :: [Int]
+    , 1:(_ ++ _) :: [Int]
+    , id x:ys :: [Int]
+    , [id x,0] :: [Int]
+    , id x:_:xs :: [Int]
+    , [id x,_] :: [Int]
+    , id x:_:_:_ :: [Int]
+    , id x:x:_ :: [Int]
+    , id x:0:_ :: [Int]
+    , id x:id _:_ :: [Int]
+    , id x:tail _ :: [Int]
+    , id x:(_ ++ _) :: [Int]
+    , id 0:ys :: [Int]
+    , [id 0,0] :: [Int]
+    , id 0:_:xs :: [Int]
+    , [id 0,_] :: [Int]
+    , id 0:_:_:_ :: [Int]
+    , id 0:x:_ :: [Int]
+    , id 0:0:_ :: [Int]
+    , id 0:id _:_ :: [Int]
+    , id 0:tail _ :: [Int]
+    , id 0:(_ ++ _) :: [Int]
+    , id (id _):ys :: [Int]
+    , [id (id _),0] :: [Int]
+    , id (id _):_:xs :: [Int]
+    , [id (id _),_] :: [Int]
+    , id (id _):_:_:_ :: [Int]
+    , id (id _):x:_ :: [Int]
+    , id (id _):0:_ :: [Int]
+    , id (id _):id _:_ :: [Int]
+    , id (id _):tail _ :: [Int]
+    , id (id _):(_ ++ _) :: [Int]
+    , negate _:ys :: [Int]
+    , [negate _,0] :: [Int]
+    , negate _:_:xs :: [Int]
+    , [negate _,_] :: [Int]
+    , negate _:_:_:_ :: [Int]
+    , negate _:x:_ :: [Int]
+    , negate _:0:_ :: [Int]
+    , negate _:id _:_ :: [Int]
+    , negate _:tail _ :: [Int]
+    , negate _:(_ ++ _) :: [Int]
+    , abs _:ys :: [Int]
+    , [abs _,0] :: [Int]
+    , abs _:_:xs :: [Int]
+    , [abs _,_] :: [Int]
+    , abs _:_:_:_ :: [Int]
+    , abs _:x:_ :: [Int]
+    , abs _:0:_ :: [Int]
+    , abs _:id _:_ :: [Int]
+    , abs _:tail _ :: [Int]
+    , abs _:(_ ++ _) :: [Int]
+    , _ + _:ys :: [Int]
+    , [_ + _,0] :: [Int]
+    , _ + _:_:xs :: [Int]
+    , [_ + _,_] :: [Int]
+    , _ + _:_:_:_ :: [Int]
+    , _ + _:x:_ :: [Int]
+    , _ + _:0:_ :: [Int]
+    , _ + _:id _:_ :: [Int]
+    , _ + _:tail _ :: [Int]
+    , _ + _:(_ ++ _) :: [Int]
+    , head _:ys :: [Int]
+    , [head _,0] :: [Int]
+    , head _:_:xs :: [Int]
+    , [head _,_] :: [Int]
+    , head _:_:_:_ :: [Int]
+    , head _:x:_ :: [Int]
+    , head _:0:_ :: [Int]
+    , head _:id _:_ :: [Int]
+    , head _:tail _ :: [Int]
+    , head _:(_ ++ _) :: [Int]
+    , ord _:ys :: [Int]
+    , [ord _,0] :: [Int]
+    , ord _:_:xs :: [Int]
+    , [ord _,_] :: [Int]
+    , ord _:_:_:_ :: [Int]
+    , ord _:x:_ :: [Int]
+    , ord _:0:_ :: [Int]
+    , ord _:id _:_ :: [Int]
+    , ord _:tail _ :: [Int]
+    , ord _:(_ ++ _) :: [Int]
+    , z:xs :: [Int]
+    , [z] :: [Int]
+    , z:_:_ :: [Int]
+    , (-1):xs :: [Int]
+    , [-1] :: [Int]
+    , (-1):_:_ :: [Int]
+    , id y:xs :: [Int]
+    , [id y] :: [Int]
+    , id y:_:_ :: [Int]
+    , id 1:xs :: [Int]
+    , [id 1] :: [Int]
+    , id 1:_:_ :: [Int]
+    , id (id x):xs :: [Int]
+    , [id (id x)] :: [Int]
+    , id (id x):_:_ :: [Int]
+    , id (id 0):xs :: [Int]
+    , [id (id 0)] :: [Int]
+    , id (id 0):_:_ :: [Int]
+    , id (id (id _)):xs :: [Int]
+    , [id (id (id _))] :: [Int]
+    , id (id (id _)):_:_ :: [Int]
+    , id (negate _):xs :: [Int]
+    , [id (negate _)] :: [Int]
+    , id (negate _):_:_ :: [Int]
+    , id (abs _):xs :: [Int]
+    , [id (abs _)] :: [Int]
+    , id (abs _):_:_ :: [Int]
+    , id (_ + _):xs :: [Int]
+    , [id (_ + _)] :: [Int]
+    , id (_ + _):_:_ :: [Int]
+    , id (head _):xs :: [Int]
+    , [id (head _)] :: [Int]
+    , id (head _):_:_ :: [Int]
+    , id (ord _):xs :: [Int]
+    , [id (ord _)] :: [Int]
+    , id (ord _):_:_ :: [Int]
+    , negate x:xs :: [Int]
+    , [negate x] :: [Int]
+    , negate x:_:_ :: [Int]
+    , negate 0:xs :: [Int]
+    , [negate 0] :: [Int]
+    , negate 0:_:_ :: [Int]
+    , negate (id _):xs :: [Int]
+    , [negate (id _)] :: [Int]
+    , negate (id _):_:_ :: [Int]
+    , abs x:xs :: [Int]
+    , [abs x] :: [Int]
+    , abs x:_:_ :: [Int]
+    , abs 0:xs :: [Int]
+    , [abs 0] :: [Int]
+    , abs 0:_:_ :: [Int]
+    , abs (id _):xs :: [Int]
+    , [abs (id _)] :: [Int]
+    , abs (id _):_:_ :: [Int]
+    , _ + x:xs :: [Int]
+    , [_ + x] :: [Int]
+    , _ + x:_:_ :: [Int]
+    , _ + 0:xs :: [Int]
+    , [_ + 0] :: [Int]
+    , _ + 0:_:_ :: [Int]
+    , _ + id _:xs :: [Int]
+    , [_ + id _] :: [Int]
+    , _ + id _:_:_ :: [Int]
+    , x + _:xs :: [Int]
+    , [x + _] :: [Int]
+    , x + _:_:_ :: [Int]
+    , 0 + _:xs :: [Int]
+    , [0 + _] :: [Int]
+    , 0 + _:_:_ :: [Int]
+    , id _ + _:xs :: [Int]
+    , [id _ + _] :: [Int]
+    , id _ + _:_:_ :: [Int]
+    , _ * _:xs :: [Int]
+    , [_ * _] :: [Int]
+    , _ * _:_:_ :: [Int]
+    , f _:xs :: [Int]
+    , [f _] :: [Int]
+    , f _:_:_ :: [Int]
+    , head xs:xs :: [Int]
+    , [head xs] :: [Int]
+    , head xs:_:_ :: [Int]
+    , head []:xs :: [Int]
+    , [head []] :: [Int]
+    , head []:_:_ :: [Int]
+    , head (_:_):xs :: [Int]
+    , [head (_:_)] :: [Int]
+    , head (_:_):_:_ :: [Int]
+    , ord c:xs :: [Int]
+    , [ord c] :: [Int]
+    , ord c:_:_ :: [Int]
+    , ord 'a':xs :: [Int]
+    , [ord 'a'] :: [Int]
+    , ord 'a':_:_ :: [Int]
+    , x':_ :: [Int]
+    , 2:_ :: [Int]
+    , id z:_ :: [Int]
+    , id (-1):_ :: [Int]
+    , id (id y):_ :: [Int]
+    , id (id 1):_ :: [Int]
+    , id (id (id x)):_ :: [Int]
+    , id (id (id 0)):_ :: [Int]
+    , id (id (id (id _))):_ :: [Int]
+    , id (id (negate _)):_ :: [Int]
+    , id (id (abs _)):_ :: [Int]
+    , id (id (_ + _)):_ :: [Int]
+    , id (id (head _)):_ :: [Int]
+    , id (id (ord _)):_ :: [Int]
+    , id (negate x):_ :: [Int]
+    , id (negate 0):_ :: [Int]
+    , id (negate (id _)):_ :: [Int]
+    , id (abs x):_ :: [Int]
+    , id (abs 0):_ :: [Int]
+    , id (abs (id _)):_ :: [Int]
+    , id (_ + x):_ :: [Int]
+    , id (_ + 0):_ :: [Int]
+    , id (_ + id _):_ :: [Int]
+    , id (x + _):_ :: [Int]
+    , id (0 + _):_ :: [Int]
+    , id (id _ + _):_ :: [Int]
+    , id (_ * _):_ :: [Int]
+    , id (f _):_ :: [Int]
+    , id (head xs):_ :: [Int]
+    , id (head []):_ :: [Int]
+    , id (head (_:_)):_ :: [Int]
+    , id (ord c):_ :: [Int]
+    , id (ord 'a'):_ :: [Int]
+    , negate y:_ :: [Int]
+    , negate 1:_ :: [Int]
+    , negate (id x):_ :: [Int]
+    , negate (id 0):_ :: [Int]
+    , negate (id (id _)):_ :: [Int]
+    , negate (negate _):_ :: [Int]
+    , negate (abs _):_ :: [Int]
+    , negate (_ + _):_ :: [Int]
+    , negate (head _):_ :: [Int]
+    , negate (ord _):_ :: [Int]
+    , abs y:_ :: [Int]
+    , abs 1:_ :: [Int]
+    , abs (id x):_ :: [Int]
+    , abs (id 0):_ :: [Int]
+    , abs (id (id _)):_ :: [Int]
+    , abs (negate _):_ :: [Int]
+    , abs (abs _):_ :: [Int]
+    , abs (_ + _):_ :: [Int]
+    , abs (head _):_ :: [Int]
+    , abs (ord _):_ :: [Int]
+    , _ + y:_ :: [Int]
+    , _ + 1:_ :: [Int]
+    , _ + id x:_ :: [Int]
+    , _ + id 0:_ :: [Int]
+    , _ + id (id _):_ :: [Int]
+    , _ + negate _:_ :: [Int]
+    , _ + abs _:_ :: [Int]
+    , _ + (_ + _):_ :: [Int]
+    , _ + head _:_ :: [Int]
+    , _ + ord _:_ :: [Int]
+    , x + x:_ :: [Int]
+    , x + 0:_ :: [Int]
+    , x + id _:_ :: [Int]
+    , 0 + x:_ :: [Int]
+    , 0 + 0:_ :: [Int]
+    , 0 + id _:_ :: [Int]
+    , id _ + x:_ :: [Int]
+    , id _ + 0:_ :: [Int]
+    , id _ + id _:_ :: [Int]
+    , _ * x:_ :: [Int]
+    , _ * 0:_ :: [Int]
+    , _ * id _:_ :: [Int]
+    , f x:_ :: [Int]
+    , f 0:_ :: [Int]
+    , f (id _):_ :: [Int]
+    , y + _:_ :: [Int]
+    , 1 + _:_ :: [Int]
+    , id x + _:_ :: [Int]
+    , id 0 + _:_ :: [Int]
+    , id (id _) + _:_ :: [Int]
+    , negate _ + _:_ :: [Int]
+    , abs _ + _:_ :: [Int]
+    , (_ + _) + _:_ :: [Int]
+    , head _ + _:_ :: [Int]
+    , ord _ + _:_ :: [Int]
+    , x * _:_ :: [Int]
+    , 0 * _:_ :: [Int]
+    , id _ * _:_ :: [Int]
+    , g _:_ :: [Int]
+    , head ys:_ :: [Int]
+    , head [0]:_ :: [Int]
+    , head (_:xs):_ :: [Int]
+    , head [_]:_ :: [Int]
+    , head (_:_:_):_ :: [Int]
+    , head (x:_):_ :: [Int]
+    , head (0:_):_ :: [Int]
+    , head (id _:_):_ :: [Int]
+    , head (tail _):_ :: [Int]
+    , head (_ ++ _):_ :: [Int]
+    , ord d:_ :: [Int]
+    , ord ' ':_ :: [Int]
+    , tail zs :: [Int]
+    , tail [0,0] :: [Int]
+    , tail [1] :: [Int]
+    , tail (_:ys) :: [Int]
+    , tail [_,0] :: [Int]
+    , tail (_:_:xs) :: [Int]
+    , tail [_,_] :: [Int]
+    , tail (_:_:_:_) :: [Int]
+    , tail (_:x:_) :: [Int]
+    , tail (_:0:_) :: [Int]
+    , tail (_:id _:_) :: [Int]
+    , tail (_:tail _) :: [Int]
+    , tail (_:(_ ++ _)) :: [Int]
+    , tail (x:xs) :: [Int]
+    , tail [x] :: [Int]
+    , tail (x:_:_) :: [Int]
+    , tail (0:xs) :: [Int]
+    , tail [0] :: [Int]
+    , tail (0:_:_) :: [Int]
+    , tail (id _:xs) :: [Int]
+    , tail [id _] :: [Int]
+    , tail (id _:_:_) :: [Int]
+    , tail (y:_) :: [Int]
+    , tail (1:_) :: [Int]
+    , tail (id x:_) :: [Int]
+    , tail (id 0:_) :: [Int]
+    , tail (id (id _):_) :: [Int]
+    , tail (negate _:_) :: [Int]
+    , tail (abs _:_) :: [Int]
+    , tail (_ + _:_) :: [Int]
+    , tail (head _:_) :: [Int]
+    , tail (ord _:_) :: [Int]
+    , tail (tail xs) :: [Int]
+    , tail (tail []) :: [Int]
+    , tail (tail (_:_)) :: [Int]
+    , tail (_ ++ xs) :: [Int]
+    , tail (_ ++ []) :: [Int]
+    , tail (_ ++ (_:_)) :: [Int]
+    , tail (xs ++ _) :: [Int]
+    , tail ([] ++ _) :: [Int]
+    , tail ((_:_) ++ _) :: [Int]
+    , tail (sort _) :: [Int]
+    , tail (insert _ _) :: [Int]
+    , _ ++ zs :: [Int]
+    , _ ++ [0,0] :: [Int]
+    , _ ++ [1] :: [Int]
+    , _ ++ (_:ys) :: [Int]
+    , _ ++ [_,0] :: [Int]
+    , _ ++ (_:_:xs) :: [Int]
+    , _ ++ [_,_] :: [Int]
+    , _ ++ (_:_:_:_) :: [Int]
+    , _ ++ (_:x:_) :: [Int]
+    , _ ++ (_:0:_) :: [Int]
+    , _ ++ (_:id _:_) :: [Int]
+    , _ ++ (_:tail _) :: [Int]
+    , _ ++ (_:(_ ++ _)) :: [Int]
+    , _ ++ (x:xs) :: [Int]
+    , _ ++ [x] :: [Int]
+    , _ ++ (x:_:_) :: [Int]
+    , _ ++ (0:xs) :: [Int]
+    , _ ++ [0] :: [Int]
+    , _ ++ (0:_:_) :: [Int]
+    , _ ++ (id _:xs) :: [Int]
+    , _ ++ [id _] :: [Int]
+    , _ ++ (id _:_:_) :: [Int]
+    , _ ++ (y:_) :: [Int]
+    , _ ++ (1:_) :: [Int]
+    , _ ++ (id x:_) :: [Int]
+    , _ ++ (id 0:_) :: [Int]
+    , _ ++ (id (id _):_) :: [Int]
+    , _ ++ (negate _:_) :: [Int]
+    , _ ++ (abs _:_) :: [Int]
+    , _ ++ (_ + _:_) :: [Int]
+    , _ ++ (head _:_) :: [Int]
+    , _ ++ (ord _:_) :: [Int]
+    , _ ++ tail xs :: [Int]
+    , _ ++ tail [] :: [Int]
+    , _ ++ tail (_:_) :: [Int]
+    , _ ++ (_ ++ xs) :: [Int]
+    , _ ++ (_ ++ []) :: [Int]
+    , _ ++ (_ ++ (_:_)) :: [Int]
+    , _ ++ (xs ++ _) :: [Int]
+    , _ ++ ([] ++ _) :: [Int]
+    , _ ++ ((_:_) ++ _) :: [Int]
+    , _ ++ sort _ :: [Int]
+    , _ ++ insert _ _ :: [Int]
+    , xs ++ ys :: [Int]
+    , xs ++ [0] :: [Int]
+    , xs ++ (_:xs) :: [Int]
+    , xs ++ [_] :: [Int]
+    , xs ++ (_:_:_) :: [Int]
+    , xs ++ (x:_) :: [Int]
+    , xs ++ (0:_) :: [Int]
+    , xs ++ (id _:_) :: [Int]
+    , xs ++ tail _ :: [Int]
+    , xs ++ (_ ++ _) :: [Int]
+    , [] ++ ys :: [Int]
+    , [] ++ [0] :: [Int]
+    , [] ++ (_:xs) :: [Int]
+    , [] ++ [_] :: [Int]
+    , [] ++ (_:_:_) :: [Int]
+    , [] ++ (x:_) :: [Int]
+    , [] ++ (0:_) :: [Int]
+    , [] ++ (id _:_) :: [Int]
+    , [] ++ tail _ :: [Int]
+    , [] ++ (_ ++ _) :: [Int]
+    , (_:_) ++ ys :: [Int]
+    , (_:_) ++ [0] :: [Int]
+    , (_:_) ++ (_:xs) :: [Int]
+    , (_:_) ++ [_] :: [Int]
+    , (_:_) ++ (_:_:_) :: [Int]
+    , (_:_) ++ (x:_) :: [Int]
+    , (_:_) ++ (0:_) :: [Int]
+    , (_:_) ++ (id _:_) :: [Int]
+    , (_:_) ++ tail _ :: [Int]
+    , (_:_) ++ (_ ++ _) :: [Int]
+    , ys ++ xs :: [Int]
+    , ys ++ [] :: [Int]
+    , ys ++ (_:_) :: [Int]
+    , [0] ++ xs :: [Int]
+    , [0] ++ [] :: [Int]
+    , [0] ++ (_:_) :: [Int]
+    , (_:xs) ++ xs :: [Int]
+    , (_:xs) ++ [] :: [Int]
+    , (_:xs) ++ (_:_) :: [Int]
+    , [_] ++ xs :: [Int]
+    , [_] ++ [] :: [Int]
+    , [_] ++ (_:_) :: [Int]
+    , (_:_:_) ++ xs :: [Int]
+    , (_:_:_) ++ [] :: [Int]
+    , (_:_:_) ++ (_:_) :: [Int]
+    , (x:_) ++ xs :: [Int]
+    , (x:_) ++ [] :: [Int]
+    , (x:_) ++ (_:_) :: [Int]
+    , (0:_) ++ xs :: [Int]
+    , (0:_) ++ [] :: [Int]
+    , (0:_) ++ (_:_) :: [Int]
+    , (id _:_) ++ xs :: [Int]
+    , (id _:_) ++ [] :: [Int]
+    , (id _:_) ++ (_:_) :: [Int]
+    , tail _ ++ xs :: [Int]
+    , tail _ ++ [] :: [Int]
+    , tail _ ++ (_:_) :: [Int]
+    , (_ ++ _) ++ xs :: [Int]
+    , (_ ++ _) ++ [] :: [Int]
+    , (_ ++ _) ++ (_:_) :: [Int]
+    , zs ++ _ :: [Int]
+    , [0,0] ++ _ :: [Int]
+    , [1] ++ _ :: [Int]
+    , (_:ys) ++ _ :: [Int]
+    , [_,0] ++ _ :: [Int]
+    , (_:_:xs) ++ _ :: [Int]
+    , [_,_] ++ _ :: [Int]
+    , (_:_:_:_) ++ _ :: [Int]
+    , (_:x:_) ++ _ :: [Int]
+    , (_:0:_) ++ _ :: [Int]
+    , (_:id _:_) ++ _ :: [Int]
+    , (_:tail _) ++ _ :: [Int]
+    , (_:(_ ++ _)) ++ _ :: [Int]
+    , (x:xs) ++ _ :: [Int]
+    , [x] ++ _ :: [Int]
+    , (x:_:_) ++ _ :: [Int]
+    , (0:xs) ++ _ :: [Int]
+    , [0] ++ _ :: [Int]
+    , (0:_:_) ++ _ :: [Int]
+    , (id _:xs) ++ _ :: [Int]
+    , [id _] ++ _ :: [Int]
+    , (id _:_:_) ++ _ :: [Int]
+    , (y:_) ++ _ :: [Int]
+    , (1:_) ++ _ :: [Int]
+    , (id x:_) ++ _ :: [Int]
+    , (id 0:_) ++ _ :: [Int]
+    , (id (id _):_) ++ _ :: [Int]
+    , (negate _:_) ++ _ :: [Int]
+    , (abs _:_) ++ _ :: [Int]
+    , (_ + _:_) ++ _ :: [Int]
+    , (head _:_) ++ _ :: [Int]
+    , (ord _:_) ++ _ :: [Int]
+    , tail xs ++ _ :: [Int]
+    , tail [] ++ _ :: [Int]
+    , tail (_:_) ++ _ :: [Int]
+    , (_ ++ xs) ++ _ :: [Int]
+    , (_ ++ []) ++ _ :: [Int]
+    , (_ ++ (_:_)) ++ _ :: [Int]
+    , (xs ++ _) ++ _ :: [Int]
+    , ([] ++ _) ++ _ :: [Int]
+    , ((_:_) ++ _) ++ _ :: [Int]
+    , sort _ ++ _ :: [Int]
+    , insert _ _ ++ _ :: [Int]
+    , sort ys :: [Int]
+    , sort [0] :: [Int]
+    , sort (_:xs) :: [Int]
+    , sort [_] :: [Int]
+    , sort (_:_:_) :: [Int]
+    , sort (x:_) :: [Int]
+    , sort (0:_) :: [Int]
+    , sort (id _:_) :: [Int]
+    , sort (tail _) :: [Int]
+    , sort (_ ++ _) :: [Int]
+    , insert _ ys :: [Int]
+    , insert _ [0] :: [Int]
+    , insert _ (_:xs) :: [Int]
+    , insert _ [_] :: [Int]
+    , insert _ (_:_:_) :: [Int]
+    , insert _ (x:_) :: [Int]
+    , insert _ (0:_) :: [Int]
+    , insert _ (id _:_) :: [Int]
+    , insert _ (tail _) :: [Int]
+    , insert _ (_ ++ _) :: [Int]
+    , insert x xs :: [Int]
+    , insert x [] :: [Int]
+    , insert x (_:_) :: [Int]
+    , insert 0 xs :: [Int]
+    , insert 0 [] :: [Int]
+    , insert 0 (_:_) :: [Int]
+    , insert (id _) xs :: [Int]
+    , insert (id _) [] :: [Int]
+    , insert (id _) (_:_) :: [Int]
+    , insert y _ :: [Int]
+    , insert 1 _ :: [Int]
+    , insert (id x) _ :: [Int]
+    , insert (id 0) _ :: [Int]
+    , insert (id (id _)) _ :: [Int]
+    , insert (negate _) _ :: [Int]
+    , insert (abs _) _ :: [Int]
+    , insert (_ + _) _ :: [Int]
+    , insert (head _) _ :: [Int]
+    , insert (ord _) _ :: [Int]
+    , (z +) :: Int -> Int
+    , ((-1) +) :: Int -> Int
+    , (id y +) :: Int -> Int
+    , (id 1 +) :: Int -> Int
+    , (id (id x) +) :: Int -> Int
+    , (id (id 0) +) :: Int -> Int
+    , (id (id (id _)) +) :: Int -> Int
+    , (id (negate _) +) :: Int -> Int
+    , (id (abs _) +) :: Int -> Int
+    , (id (_ + _) +) :: Int -> Int
+    , (id (head _) +) :: Int -> Int
+    , (id (ord _) +) :: Int -> Int
+    , (negate x +) :: Int -> Int
+    , (negate 0 +) :: Int -> Int
+    , (negate (id _) +) :: Int -> Int
+    , (abs x +) :: Int -> Int
+    , (abs 0 +) :: Int -> Int
+    , (abs (id _) +) :: Int -> Int
+    , ((_ + x) +) :: Int -> Int
+    , ((_ + 0) +) :: Int -> Int
+    , ((_ + id _) +) :: Int -> Int
+    , ((x + _) +) :: Int -> Int
+    , ((0 + _) +) :: Int -> Int
+    , ((id _ + _) +) :: Int -> Int
+    , (_ * _ +) :: Int -> Int
+    , (f _ +) :: Int -> Int
+    , (head xs +) :: Int -> Int
+    , (head [] +) :: Int -> Int
+    , (head (_:_) +) :: Int -> Int
+    , (ord c +) :: Int -> Int
+    , (ord 'a' +) :: Int -> Int
+    , (y *) :: Int -> Int
+    , (1 *) :: Int -> Int
+    , (id x *) :: Int -> Int
+    , (id 0 *) :: Int -> Int
+    , (id (id _) *) :: Int -> Int
+    , (negate _ *) :: Int -> Int
+    , (abs _ *) :: Int -> Int
+    , ((_ + _) *) :: Int -> Int
+    , (head _ *) :: Int -> Int
+    , (ord _ *) :: Int -> Int
+    , h :: Int -> Int
+    , (q ||) :: Bool -> Bool
+    , (not p ||) :: Bool -> Bool
+    , (not False ||) :: Bool -> Bool
+    , (not True ||) :: Bool -> Bool
+    , (not (not _) ||) :: Bool -> Bool
+    , ((_ || _) ||) :: Bool -> Bool
+    , (p &&) :: Bool -> Bool
+    , (False &&) :: Bool -> Bool
+    , (True &&) :: Bool -> Bool
+    , (not _ &&) :: Bool -> Bool
+    , (_ ==>) :: Bool -> Bool
+    ]
+  , ...
+  ]
diff --git a/test/model/eg/u-extrapolate.out b/test/model/eg/u-extrapolate.out
new file mode 100644
--- /dev/null
+++ b/test/model/eg/u-extrapolate.out
@@ -0,0 +1,21 @@
+sort . sort = sort
++++ Tests passed.
+
+length . nub = length  (incorrect when there are repeated elements)
+*** Falsified, counterexample:  [0,0] :: [Int]
+               generalization:  x:x:xs :: [Int]
+
+\(x,y) -> x + y == y + x
++++ Tests passed.
+
+\x -> x == x + 1  (always incorrect)
+*** Falsified, counterexample:  0 :: Int
+               generalization:  x :: Int
+
+\(x,y) -> x + y == x + x  (incorrect)
+*** Falsified, counterexample:  (0,1) :: (Int,Int)
+
+\(x,y) -> x /= y  (incorrect whenever x and y are equal)
+*** Falsified, counterexample:  (0,0) :: (Int,Int)
+               generalization:  (x,x) :: (Int,Int)
+
diff --git a/test/model/eg/u-speculate.out b/test/model/eg/u-speculate.out
new file mode 100644
--- /dev/null
+++ b/test/model/eg/u-speculate.out
@@ -0,0 +1,45 @@
+Equations about _, 0, (+), abs:
+abs 0  =  0
+x + 0  =  x
+0 + x  =  x
+abs (abs x)  =  abs x
+x + y  =  y + x
+abs (x + y)  =  abs (y + x)
+abs (x + abs x)  =  x + abs x
+abs (abs x + x)  =  x + abs x
+abs x + abs x  =  abs (x + x)
+x + (y + z)  =  x + (z + y)
+x + (y + z)  =  y + (x + z)
+x + (y + z)  =  y + (z + x)
+x + (y + z)  =  z + (x + y)
+x + (y + z)  =  z + (y + x)
+(x + y) + z  =  x + (y + z)
+(x + y) + z  =  x + (z + y)
+(x + y) + z  =  y + (x + z)
+(x + y) + z  =  y + (z + x)
+(x + y) + z  =  z + (y + x)
+(x + y) + z  =  (x + z) + y
+(x + y) + z  =  (y + x) + z
+(x + y) + z  =  (y + z) + x
+(x + y) + z  =  (z + x) + y
+(x + y) + z  =  (z + y) + x
+
+Equations about _, False, True, not:
+not False  =  True
+not True  =  False
+not (not p)  =  p
+
+Equations about _, _, [], (:), (++), sort:
+sort []  =  []
+xs ++ []  =  xs
+[] ++ xs  =  xs
+sort (sort xs)  =  sort xs
+sort [x]  =  [x]
+[x] ++ xs  =  x:xs
+sort (xs ++ ys)  =  sort (ys ++ xs)
+sort (x:sort xs)  =  sort (x:xs)
+sort (xs ++ sort ys)  =  sort (xs ++ ys)
+sort (sort xs ++ ys)  =  sort (xs ++ ys)
+(x:xs) ++ ys  =  x:(xs ++ ys)
+(xs ++ ys) ++ zs  =  xs ++ (ys ++ zs)
+
diff --git a/test/name-derive.hs b/test/name-derive.hs
new file mode 100644
--- /dev/null
+++ b/test/name-derive.hs
@@ -0,0 +1,56 @@
+-- Copyright (c) 2019 Rudy Matela.
+-- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+{-# LANGUAGE TemplateHaskell #-}
+
+import Test
+
+import Data.Express.Utils.List
+
+data Peano = Zero | Succ Peano deriving Show
+data List a = a :- List a | Nil deriving Show
+data Bush a = Bush a :-: Bush a | Leaf a deriving (Show, Eq)
+data Tree a = Node (Tree a) a (Tree a) | Null deriving (Show, Eq)
+deriveName ''Peano
+deriveName ''List
+deriveName ''Bush
+deriveName ''Tree
+
+-- Recursive nested datatype cascade
+data RN      = RN RN0 (RN1 Int) (RN2 Int RN)
+data RN0     = Nest0 Int | Recurse0 RN
+data RN1 a   = Nest1 a   | Recurse1 RN
+data RN2 a b = Nest2 a b | Recurse2 RN
+deriveNameCascading ''RN
+
+-- Those should have no effect (instance already exists):
+{- uncommenting those should generate warnings
+deriveName ''Bool
+deriveName ''Maybe
+deriveName ''Either
+-}
+
+-- Those should not generate warnings
+deriveNameIfNeeded ''Bool
+deriveNameIfNeeded ''Maybe
+deriveNameIfNeeded ''Either
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , name (undefined :: Peano) == "p"
+  , name (undefined :: List Int) == "l"
+  , name (undefined :: Bush Int) == "b"
+  , name (undefined :: Tree Int) == "t"
+  , name (undefined :: List Bool) == "l"
+  , name (undefined :: Bush Bool) == "b"
+  , name (undefined :: Tree Bool) == "t"
+
+  , name (undefined :: RN) == "r"
+  , name (undefined :: RN0) == "r"
+  , name (undefined :: RN1 Int) == "r"
+  , name (undefined :: RN2 Bool Int) == "r"
+  ]
diff --git a/test/name.hs b/test/name.hs
new file mode 100644
--- /dev/null
+++ b/test/name.hs
@@ -0,0 +1,64 @@
+-- Copyright (c) 2017-2018 Rudy Matela.  -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+import Data.Express.Utils.List
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  -- simple types
+  , name (undefined :: Int) == "x"
+  , name (undefined :: Integer) == "x"
+  , name (undefined :: Char) == "c"
+  , name (undefined :: Bool) == "p"
+  , name (undefined :: Int -> Int) == "f"
+  , name (undefined :: Double) == "x"
+
+  -- lists
+  , name (undefined :: [Int]) == "xs"
+  , name (undefined :: [[Int]]) == "xss"
+  , name (undefined :: [Bool]) == "ps"
+  , name (undefined :: [[Bool]]) == "pss"
+
+  -- eithers
+  , name (undefined :: Either Bool Bool) == "epq"
+  , name (undefined :: Either Int Int) == "exy"
+  , name (undefined :: Either Bool Char) == "epc"
+
+  -- maybes
+  , name (undefined :: Maybe Int) == "mx"
+  , name (undefined :: Maybe [Int]) == "mxs"
+  , name (undefined :: Maybe [[Int]]) == "mxss"
+
+  -- pairs
+  , name (undefined :: (Int,Int)) == "xy"
+  , name (undefined :: (Bool,Bool)) == "pq"
+  , name (undefined :: (Char,Char)) == "cd"
+  , name (undefined :: (Bool,Int)) == "px"
+  , name (undefined :: (Int,Bool)) == "xp"
+
+  -- triples
+  , name (undefined :: (Int,Int,Int)) == "xyz"
+  , name (undefined :: (Bool,Bool,Bool)) == "pqr"
+  , name (undefined :: (Char,Char,Char)) == "cde"
+  , name (undefined :: (Int,Bool,Char)) == "xpc"
+
+  -- tuples
+  , name (undefined :: (Int,Int,Int,Int)) == "xxxx"
+  , name (undefined :: (Int,Int,Int,Int,Int)) == "xxxxx"
+  , name (undefined :: (Int,Int,Int,Int,Int,Int)) == "xxxxxx"
+  , name (undefined :: (Int,Int,Int,Int,Int,Int,Int)) == "xxxxxxx"
+  , name (undefined :: (Int,Int,Int,Int,Int,Int,Int,Int)) == "xxxxxxxx"
+  , name (undefined :: (Int,Int,Int,Int,Int,Int,Int,Int,Int)) == "xxxxxxxxx"
+  , name (undefined :: (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int)) == "xxxxxxxxxx"
+  , name (undefined :: (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int)) == "xxxxxxxxxxx"
+  , name (undefined :: (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int,Int)) == "xxxxxxxxxxxx"
+
+  , name (undefined :: (Bool,Bool,Bool,Bool)) == "pppp"
+  , name (undefined :: (Bool,Bool,Bool,Bool,Bool)) == "ppppp"
+  , name (undefined :: (Bool,Bool,Bool,Bool,Bool,Bool)) == "pppppp"
+  ]
diff --git a/test/ord.hs b/test/ord.hs
new file mode 100644
--- /dev/null
+++ b/test/ord.hs
@@ -0,0 +1,46 @@
+-- Copyright (c) 2019 Rudy Matela.  -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , holds n $ (okEqOrd :: Expr -> Expr -> Expr -> Bool)
+  , holds n $ \(Ill e0) (Ill e1) (Ill e2) -> okEqOrd e0 e1 e2
+
+  -- Holes < Values < Apps
+  , xx < zero
+  , zero < zero -+- one
+  , xx < xx -+- yy
+  , zero < xx -+- yy
+
+  -- Less arity is less
+  , zero < absE
+  , absE < times
+  , ae   < ordE
+  , ordE < times
+  , value "id" (id -:>  int)  < value "id"    (id    -:>  [int])
+  , value "id" (id -:> [int]) < value "id"    (id    -:> [[int]])
+  , value "id" (id -:>  int)  < value "sum"   (sum   -:>  [int])
+  , value "id" (id -:>  int)  < value "(:[])" ((:[]) -:>   int)
+
+  -- precedent types
+  , pp < xx
+  , cc < xx
+  , pp < cc
+  , xx < xxs
+  , ae < zero
+  , true < zero
+  , true < ae
+  , zero < nil
+
+  -- other precedence rules
+  ,  (xx -+- xx)         < (xx -+- (xx -+- xx))
+  , ((xx -+- xx) -+- xx) > (xx -+- (xx -+- xx))
+  , xx < yy
+  , zero < one
+  , xx < zero
+  ]
diff --git a/test/sdist b/test/sdist
new file mode 100644
--- /dev/null
+++ b/test/sdist
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# test/sdist: tests the package generated by "cabal sdist".
+#
+# Copyright (c) 2015-2018 Rudy Matela.
+# Distributed under the 3-Clause BSD licence.
+export LC_ALL=C  # consistent sort
+pkgver=` cat *.cabal | grep "^version:" | sed -e "s/version: *//"`
+pkgname=`cat *.cabal | grep "^name:"    | sed -e "s/name: *//"`
+pkg=$pkgname-$pkgver
+set -x
+cabal sdist &&
+cd dist &&
+tar -tf $pkg.tar.gz | sort --ignore-case          > ls-cabal-i  &&
+tar -tf $pkg.tar.gz | sort --ignore-case --unique > ls-cabal-iu &&
+diff -rud ls-cabal-i ls-cabal-iu &&
+rm -f ls-cabal ls-cabal-ignore-case &&
+if [ -d ../.git ]
+then
+	# on git repo, test if files are the same
+	git -C .. ls-files                                      | sort > ls-git   &&
+	tar -tf $pkg.tar.gz | grep -v "/$" | sed -e "s,$pkg/,," | sort > ls-cabal &&
+	diff -rud ls-git ls-cabal &&
+	rm -f ls-git ls-cabal
+else
+	# outside of git repo, test build
+	rm -rf $pkg/ &&
+	tar -xzf $pkg.tar.gz &&
+	cd $pkg/ &&
+	cabal configure --enable-tests --enable-benchmarks &&
+	cabal build &&
+	cabal test
+fi
diff --git a/test/show.hs b/test/show.hs
new file mode 100644
--- /dev/null
+++ b/test/show.hs
@@ -0,0 +1,82 @@
+-- Copyright (c) 2017-2018 Rudy Matela.  -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+import Data.Express.Utils.List
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  -- showing expressions
+
+  , show zero == "0 :: Int"
+  , show two == "2 :: Int"
+  , show minusOne == "-1 :: Int"
+  , show (one -+- two -*- three)   == "1 + 2 * 3 :: Int"
+  , show ((one -+- two) -*- three) == "(1 + 2) * 3 :: Int"
+
+  , show plus == "(+) :: Int -> Int -> Int"
+  , show times == "(*) :: Int -> Int -> Int"
+
+  , show (plus :$ one) == "(1 +) :: Int -> Int"
+  , show (times :$ (minusOne -+- two)) == "(((-1) + 2) *) :: Int -> Int"
+
+  , show ffE == "f :: Int -> Int"
+  , show (ff xx) == "f x :: Int"
+  , show (var "f" (undefined :: Int -> Int -> Int)) == "f :: Int -> Int -> Int"
+  , show (var "f" (undefined :: Int -> Int -> Int) :$ one) == "f 1 :: Int -> Int"
+  , show (var "f" (undefined :: Int -> Int -> Int) :$ one :$ two) == "f 1 2 :: Int"
+  , show (var "`f`" (undefined :: Int -> Int -> Int) :$ one) == "(1 `f`) :: Int -> Int"
+  , show (var "`f`" (undefined :: Int -> Int -> Int) :$ one :$ two) == "1 `f` 2 :: Int"
+  , show (one -?- two) == "1 ? 2 :: Int"
+  , show (value "`compare`" (compare :: Int->Int->Ordering) :$ one) == "(1 `compare`) :: Int -> Ordering"
+  , show (value "`compare`" (compare :: Int->Int->Ordering) :$ one :$ two) == "1 `compare` 2 :: Ordering"
+
+  , holds n $ show . mapVars (\(Value ('_':s) d) -> Value (if null s then "_" else s) d) === show
+--, holds n $ show . mapConsts (\(Value s d) -> Value ('_':s) d) === show -- TODO:
+
+  , show emptyString == "\"\" :: [Char]"
+  , show (space -:- emptyString) == "\" \" :: [Char]"
+  , show (space -:- ccs)         == "' ':cs :: [Char]"
+  , show (ae -:- bee -:- emptyString) == "\"ab\" :: [Char]"
+  , show (ae -:- bee -:- ccs)         == "'a':'b':cs :: [Char]"
+  , show (ae -:- space -:- bee -:- lineBreak -:- emptyString) == "\"a b\\n\" :: [Char]"
+  , show (cc -:- space -:- dd -:- lineBreak -:- emptyString)  == "c:' ':d:\"\\n\" :: [Char]"
+  , show (cc -:- space -:- dd -:- lineBreak -:- ccs)          == "c:' ':d:'\\n':cs :: [Char]"
+  , show (cc -:- ae -:- bee -:- emptyString) == "c:\"ab\" :: [Char]"
+  , show (cc -:- ae -:- bee -:- space -:- ae -:- bee -:- emptyString) == "c:\"ab ab\" :: [Char]"
+
+  , show one                     == "1 :: Int"
+  , show (minusOne)              == "-1 :: Int"
+  , show (one -+- one)           == "1 + 1 :: Int"
+  , show (minusOne -+- minusOne) == "(-1) + (-1) :: Int"
+
+  , show (zero -|- one)          == "(0,1) :: (Int,Int)"
+  , show (minusOne -|- minusOne) == "(-1,-1) :: (Int,Int)"
+  , show (triple zero one two)   == "(0,1,2) :: (Int,Int,Int)"
+  , show (quadruple minusOne zero one two) == "(-1,0,1,2) :: (Int,Int,Int,Int)"
+  , show (quintuple minusOne zero one two three) == "(-1,0,1,2,3) :: (Int,Int,Int,Int,Int)"
+  , show (sixtuple minusTwo minusOne zero one two three) == "(-2,-1,0,1,2,3) :: (Int,Int,Int,Int,Int,Int)"
+
+  , show (one -:- nil)                    == "[1] :: [Int]"
+  , show (zero -:- one -:- nil)           == "[0,1] :: [Int]"
+  , show (minusOne -:- nil)               == "[-1] :: [Int]"
+  , show (minusOne -:- minusTwo -:- nil)  == "[-1,-2] :: [Int]"
+  , show (xx -:- minusTwo -:- yy -:- nil) == "[x,-2,y] :: [Int]"
+  , show (xx -:- minusTwo -:- yy -:- xxs) == "x:(-2):y:xs :: [Int]"
+
+  , show (ffE -$- zero)     == "f $ 0 :: Int"
+  , show (ggE -$- xx)       == "g $ x :: Int"
+  , show (ffE -$- minusOne) == "f $ (-1) :: Int"
+
+  , holds n $ \e -> showExpr e `isPrefixOf` show e
+
+  -- showing holes --
+  , show (hole (undefined :: Int -> Int) :$ one)              == "_ 1 :: Int"
+  , show (hole (undefined :: Int -> Int) :$ xx)               == "_ x :: Int"
+  , show (hole (undefined :: Int -> Int -> Int) :$ one :$ xx) == "_ 1 x :: Int"
+  , show (hole (undefined :: Int -> Int -> Int) :$ i_ :$ i_)  == "_ _ _ :: Int"
+  ]
diff --git a/test/utils.hs b/test/utils.hs
new file mode 100644
--- /dev/null
+++ b/test/utils.hs
@@ -0,0 +1,53 @@
+-- Copyright (c) 2017-2018 Rudy Matela.  -- Distributed under the 3-Clause BSD licence (see the file LICENSE).
+import Test
+
+import Data.Express.Utils.List
+import Data.Express.Utils.String
+import Data.Express.Utils.Typeable
+
+main :: IO ()
+main = mainTest tests 5040
+
+tests :: Int -> [Bool]
+tests n =
+  [ True
+
+  , holds n $ \xs -> nubSort xs == nub (sort xs :: [Int])
+  , holds n $ \xs -> nubSort xs == sort (nub xs :: [Int])
+
+  , holds n $ isSubsetOf ==== (\xs ys -> all (`elem` ys) (xs :: [Int]))
+
+  , elementTy (typeOf [True]) == boolTy
+  , elementTy (elementTy (typeOf [[False]])) == boolTy
+  , show (mkComparisonTy boolTy) == "Bool -> Bool -> Bool"
+  , show (mkComparisonTy intTy)  == "Int -> Int -> Bool"
+  , show (mkCompareTy boolTy) == "Bool -> Bool -> Ordering"
+  , show (mkCompareTy intTy)  == "Int -> Int -> Ordering"
+
+  , primeCycle [] == []
+  , ["x", "y", "z", "x'", "y'", "z'", "x''"] `isPrefixOf` primeCycle ["x","y","z"]
+  , ["x","x'","x''","x'''","x''''","x'''''"] `isPrefixOf` primeCycle ["x"]
+  , ["i", "j", "k", "i'", "j'", "k'", "i''"] `isPrefixOf` primeCycle ["i","j","k"]
+  , ["xy", "zw", "xy'", "zw'", "xy''"]       `isPrefixOf` primeCycle ["xy","zw"]
+
+  , ["x","y","z","x'","y'"]    `isPrefixOf` variableNamesFromTemplate "x"
+  , ["xs","ys","zs","xs'"]     `isPrefixOf` variableNamesFromTemplate "xs"
+  , ["xss","yss","zss","xss'"] `isPrefixOf` variableNamesFromTemplate "xss"
+  , ["c","d","e","c'","d'"]    `isPrefixOf` variableNamesFromTemplate "c"
+  , ["s","t","u","s'","t'"]    `isPrefixOf` variableNamesFromTemplate "s"
+  , ["0","1","2","3","4"]      `isPrefixOf` variableNamesFromTemplate "0"
+  , ["1","2","3","4","5"]      `isPrefixOf` variableNamesFromTemplate "1"
+  , ["z","z1","z2","z3","z4"]  `isPrefixOf` variableNamesFromTemplate "z"
+  , ["y","y1","y2","y3","y4"]  `isPrefixOf` variableNamesFromTemplate "y"
+  , ["x1","x2","x3","x4"]      `isPrefixOf` variableNamesFromTemplate "x1"
+  , ["a0","a1","a2","a3"]      `isPrefixOf` variableNamesFromTemplate "a0"
+  , ["e1","e2","e3","e4"]      `isPrefixOf` variableNamesFromTemplate "e1"
+  , ["xs1","xs2","xs3"]        `isPrefixOf` variableNamesFromTemplate "xs1"
+  , ["xy","zw","xy'","zw'"]    `isPrefixOf` variableNamesFromTemplate "xy"
+  , ["ab","cd","ab'","cd'"]    `isPrefixOf` variableNamesFromTemplate "ab"
+  , ["xys","zws","xys'"]       `isPrefixOf` variableNamesFromTemplate "xys"
+  , ["xyz","uvw","xyz'","uvw'"]    `isPrefixOf` variableNamesFromTemplate "xyz"
+  , ["thing1", "thing2", "thing3"] `isPrefixOf` variableNamesFromTemplate "thing1"
+  , ["thingAndThing", "thingAndThing'", "thingAndThing''", "thingAndThing'''"]
+    `isPrefixOf` variableNamesFromTemplate "thingAndThing"
+  ]
