diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,165 @@
+5 May 2015
+0.4 Release
+
+16 Apr 2015
+Make tests run (and pass) when called by "runghc Setup test"
+
+Add hNot, and add HNotFD which is injective (unlike the type family)
+
+Replace uses of `HLength xs ~ n` with `HLengthEq xs n`.
+
+15 Mar 2015
+Add HCurry.hs.
+
+Redefine Arity such that `Arity f (HSucc HZero)` will refine the
+type of `f` to `x -> y`.
+
+SameLabels is generalized so that functions like 'asLabelsOf' do
+not need to explicitly convert a that specifies the ordering of
+labels. Additional constraints might be needed to restore the old
+behavior:
+
+> type SameLabelsOld x y = (HAllTaggedLV x, HAllTaggedLV y, SameLabels x y)
+
+Similarly HExtend instances Proxy intended for making Proxies
+used to disambiguate labels (see 'asLabelsOf' again).
+
+Add hMapOutV, zipVR, extendsVariant. Rename the previous
+splitVariant to splitVariant1, and use the name splitVariant for
+a function that splits a Variant into two Variants.
+
+Add projected to Labelable.hs which allows working on a smaller
+Record or Variant.
+
+Add HasFieldM for lookups that return a default value if the
+field is missing.
+
+Split HZip into two classes: class HUnzip r x y xy => HZip r x y xy.
+This allows instance HUnzip Variant x y xy.
+
+Add Data,Typeable,Enum,Bounded,Ord,Monoid instances for Variant and TIC
+
+Implement HRLabelSet in terms of HLabelSet, which simplifies
+inferred types that would otherwise have redundancy (HLabelSet
+(LabelsOf r), HRLabelSet r)
+
+Move definitions into FakePrelude, and split up
+examples/Properties.hs into separate modules to help with
+compilation times when adding new tests.
+
+15 Feb 2015
+Change HList to a data family (see comments in HList.hs).
+
+Change the Show instance for TIP and HList to use "," not ", " as the
+separator for consistency with Record and ordinary lists.
+
+Parameterize HZip over the collection type to work on Record, TIP as well as
+HList.
+
+Add and use HProxies for building the spine of a HList from the type, to avoid
+having HLists of undefined/error values somewhere.
+
+Build with ghc-7.10 RC1. RecordU/RecordUS is moved to broken/ instead of
+updating it.
+
+9 Sep 2014
+Change the ordering of the list produced by HLeftUnion / (.<++.)
+to better match hAppend / ++ suggested by the name.
+
+25 Aug 2014
+Add HSort, which provides a merge sort and a quick sort.
+
+Support _ (wildcard) with the pun quasiquoter, and make
+patterns such as [pun| x y _ |] constrain the Record or Variant
+to have at least 3 elements.
+
+Reduce the number of parameters for Labelable
+
+Allow operations with different label kinds (ie.
+Record [Tagged 1 x, Tagged "y" Int]) to proceed as expected.
+
+13 Jul 2014
+Start RecordU and RecordUS, a variation on Record where the values
+are stored in unboxed array(s).
+
+Add Partition, GroupBy and Span.
+
+Add list2HList and isos sameLength and sameLabels.
+
+23 Jun 2014
+TIPTransform and TIPTransformM become part of the distribution,
+and missing fields are reported with the Fail superclass
+technique.
+
+Projection of a TIP to a tuple is reimplemented without an explicit
+type signature. The functions are exported as tipyTuple,
+tipyTuple3 etc.
+
+Conversion between HLists and up to 6-tuples done with HTuple.
+
+Add ZipVariant, Unvariant, splitVariant, extendVariant,
+an instance Eq (Variant v) and an instance Labelable TIC.
+
+Add quickcheck in examples/Properties.hs. Coverage measured with
+HPC is about 40%.
+
+5 Jun 2014
+Start to parameterize operations on the collection type. This
+means that where we previously had HMapCxt f x y another
+type parameter with kind [*] -> * is added. This means
+previous uses of HMapCxt f x y become HMap HList f x y.
+This allows hMap to be used with Record and Variant.
+HUpdateAtLabel is similarly generalized.
+
+Rework Variant: the implementation is now similar to Dynamic
+and TIC. Likewise, TIP is implemented in terms of Record.
+
+28 May 2014
+Add tipyLens and ticPrism.
+
+Add a HExtend instance for Variant.
+
+Add functional dependencies to Labelable (and corresponding
+superclasses) to avoid ambiguous types.
+
+26 May 2014
+Add prisms for Variant. This adds a dependency on "profunctors".
+
+Parameterize Labelable on the collection type. This allows
+labelable labels to be used with RecordS or VariantS, where
+the resulting Optic is a Lens or Prism respectively.
+
+Remove recordLabels in favor of labelsOf: a kind variable which
+only appears on the RHS required a lengthy type annotation to fix
+that variable. Pattern matching to convert a `Label (a :: k)` to
+`a :: k` happens later on when another value with kind `k` is a
+available on the LHS.
+
+Improve type errors when accessing missing fields when using
+Labelable labels. Except for HPrism, the error message contains
+`Fail (FieldNotFound "x")`. This involved adding a HUpdateAtLabel
+class and HTPupdateAtLabel type which hides the `n` type
+variable.
+
+Reduce the number of parameters to HMapCxt. The old version could
+be defined in terms of the new version as:
+
+	type HMapCxt_old f a b ha hb = (HMapCxt f a b,
+																	HList a ~ ha,
+																	HList b ~ hb)
+
+Reimplement RecordValues in terms of HMap. The original
+implementation is kept because it avoids the need for
+-XAllowAmbiguousTypes.
+
+Add hMapR and hMapV to map over the values in a Record or the
+value in a Variant respectively. These functions are defined in
+terms of HFmap which may be useful on it's own.
+
+Add a typeable instance for Label3, and change examples/cmdargs.hs
+to use this label kind. This allows the example to work with
+ghc-7.8.2 which lacks an instance Typeable (x :: Symbol).
+
 28 Mar 2014
 	Release 0.3.4.1
 	Fix build with ghc-7.8 broken by changes in HList 0.3.4
diff --git a/Data/HList/CommonMain.hs b/Data/HList/CommonMain.hs
--- a/Data/HList/CommonMain.hs
+++ b/Data/HList/CommonMain.hs
@@ -11,31 +11,145 @@
 
 module Data.HList.CommonMain (
 
+ -- * Faking dependent types in Haskell
    module Data.HList.FakePrelude
+
+ -- * Functions for all collections
  , module Data.HList.HListPrelude
+ -- * Array-like access to HLists
  , module Data.HList.HArray
+ -- * Result-type-driven operations
  , module Data.HList.HOccurs
+ -- * Type-indexed operations
  , module Data.HList.HTypeIndexed
+
+ -- * Record
  , module Data.HList.Record
+ -- | quasiquoter 'pun' helps to avoid needing a proxy value with
+ -- type 'Label' in the first place: when you take values out of or into
+ -- records with pattern matching, the variable name determines the label
+ -- name.
+ , module Data.HList.RecordPuns
+
+
+ -- * HList
+ -- | A subset of "Data.HList.HList" is re-exported.
  , module Data.HList.HList
- , module Data.HList.TypeEqO
- , module Data.HList.TIP
- , module Data.HList.TIC
  , module Data.HList.HZip
- , module Data.HList.Variant
+ -- ** A subset of "Data.HList.HSort"
+ , hSort
+ , HSort
+ , HSortBy(..)
+ , HLeFn, HDown
+ , HSet, HSetBy
+ , HIsSet, HIsSetBy
+ , HAscList, HIsAscList
 
+ -- ** A subset of "Data.HList.HCurry"
+ , HCurry'(..)
+ , hCurry, hUncurry
+ , hCompose
+
+ -- * TIP
+ -- | Public interface of "Data.HList.TIP"
+ , TIP
+ , emptyTIP
+ , tipyUpdate
+ , tipyLens
+ , tipyLens'
+ -- ** projection
+ , tipyProject
+ , tipyProject2
+ , tipyTuple
+ , tipyTuple3
+ , tipyTuple4
+ , tipyTuple5
+ , TagUntag, TagUntagFD(..)
+ , TagR
+
+ -- ** TIP transform
+ , TransTIP(..)
+ , TransTIPM(..)
+
+ -- * TIC
+ -- | Public interface of "Data.HList.TIC"
+ , TIC
+ -- ** creating TIC
+ , mkTIC
+ , mkTIC1
+ , mkTIC'
+
+ -- ** get,set,modify
+ , ticPrism, ticPrism'
+
+ -- * Variant
+ -- | Public interface of "Data.HList.Variant"
+ , Variant
+ , mkVariant
+ , mkVariant1
+ , castVariant
+ , HMapV(..), hMapV
+ , hMapOutV
+ , ZipVariant(..)
+ , ZipVR(..), zipVR
+ -- ** projection
+ -- *** many
+ , SplitVariant(splitVariant)
+ , ProjectVariant(..)
+ , ExtendsVariant(..)
+ , ProjectExtendVariant(..)
+ -- *** one
+ , HPrism(..)
+ , unvarianted, unvarianted'
+
+ , splitVariant1
+ , splitVariant1'
+ , extendVariant
+ -- **** implementation
+ , Unvariant(..)
+ , Unvariant'(..)
+
+
+
+ -- * Conversions between collections
+ -- $convention the foo' optic has the same type as
+ -- @Control.Lens.simple . foo@. 'hLens'' is an exception to this rule.
+
+ , TypeIndexed(..)
+ , typeIndexed'
+ -- ** HList and Record
+ -- | 'unlabeled' 'unlabeled''
+
+ -- ** HList and TIP
+ , tipHList, tipHList'
+
+ -- ** Newtype wrappers
+ -- $convention these isos unwrap/wrap the newtypes 'TIP' 'TIC' and
+ -- 'Record'. Names follow the pattern @fromTo :: Iso' From To@.
+
+ -- | 'hListRecord' 'hListRecord'' are exported under "Data.HList.Record"
+ , ticVariant, ticVariant'
+ , tipRecord, tipRecord'
+
+ -- ** Record and Variant
+ , hMaybied, hMaybied'
+ -- *** implementation
+ , VariantToHMaybied(variantToHMaybied)
+ , HMaybiedToVariantFs
+ , hMaybiedToVariants
+
  -- * "Data.HList.Keyword"
  -- | the \"public\" parts. More examples are in the module documentation.
  , Kw(..), recToKW, IsKeyFN, K,  ErrReqdArgNotFound,  ErrUnexpectedKW
 
  -- * Labels
- {- | there are really only two options for now, but there are
+ {- | there are three options for now. However, there are
    a couple different styles for the first option here:
 
    GHC supports type-level strings ('GHC.TypeLits.Symbol'), and these can be
-   labels. You can refer to these strings using an unwieldy syntax. For
-   example if you want to store a value @5@ in a record @rec@ with a field
-   called @\"x\"@, and then get it out again:
+   labels. You can refer to these strings using an unwieldy syntax described
+   below. For example if you want to store a value @5@ in a record @rec@
+   with a field called @\"x\"@, and then get it out again:
 
    let rec = ('Label' :: Label \"x\") '.=.' 5 '.*.' 'emptyRecord'
 
@@ -47,7 +161,7 @@
 
    > rec .!. x
 
-   See 'makeLabels6' for automating the @x = Label :: Label \"x\"@.
+   'makeLabels6' automates definitions like @x = Label :: Label \"x\"@.
 
  -}
  -- $label6demo
@@ -58,18 +172,23 @@
  -- ** namespaced labels
  , module Data.HList.Label3
 
- -- | template haskell for automating different types of labels
+ -- ** labels as any instance of Typeable
+ --  | "Data.HList.Label5"
+
+ -- ** template haskell
  , module Data.HList.MakeLabels
- -- | quasiquoter 'pun' helps to avoid needing a proxy value with
- -- type 'Label' in the first place: when you take values out of or into
- -- records with pattern matching, the variable name determines the label
- -- name.
- , module Data.HList.RecordPuns
 
 
  -- * "Data.HList.Data"
  -- | This modules provide useful instances. A useful application can be
  -- found in @examples/cmdargs.hs@
+
+ -- | Overlapping instances are restricted to here
+ , module Data.HList.TypeEqO
+
+ -- * Internals
+ -- | internals exported for type signature purposes
+ , HAllTaggedEq
 ) where
 
 import Data.HList.FakePrelude
@@ -78,16 +197,32 @@
 import Data.HList.HOccurs
 import Data.HList.HTypeIndexed
 import Data.HList.Record
-import Data.HList.HList
+-- import Data.HList.RecordU
+-- import Data.HList.RecordOrd
+import Data.HList.HList hiding (append',
+                                hAppend',
+                                FHCons(..),
+                                hMapAux,
+                                MapCar(..),
+                                hMapMapCar,
+                                hSequence2,
+                                )
+import Data.HList.HCurry
+import Data.HList.HSort
 import Data.HList.MakeLabels
-import Data.HList.TypeEqO
+import Data.HList.TypeEqO hiding (IsKeyFN)
 import Data.HList.TIP
 import Data.HList.TIC
 
 import Data.HList.HZip
-import Data.HList.Label3
+import Data.HList.Label3 hiding (MapLabel)
+import Data.HList.Label5 () -- only instances
 import Data.HList.Label6 () -- only instances
-import Data.HList.Labelable
+import Data.HList.Labelable (Labelable(..),
+                             Projected(..), projected',
+                             toLabel,
+                             (.==.),
+                             LabeledOptic)
 
 import Data.HList.Variant
 
@@ -114,12 +249,14 @@
 {- $labelable #labelabledemo#
 
 Rather than having the @x = Label :: Label \"x\"@, the labels
-generated by 'makeLabelable' also double lenses for "Control.Lens".
+generated by 'makeLabelable' also double as lenses for "Control.Lens".
 Here is an example of how much better that is:
 
 >>> :set -XNoMonomorphismRestriction -XDataKinds -XPolyKinds
 >>> import Control.Lens
+>>> import Data.HList.Labelable
 >>> let x = hLens' (Label :: Label "x")
+>>> let y = hLens' (Label :: Label "y")
 
 The Label6 method:
 
@@ -137,6 +274,13 @@
 
 >>> r & x .~ ()
 Record{x=()}
+
+When a field is missing, the error names that field:
+
+>>> :t r^.y
+...
+...No instance for (Fail (FieldNotFound "y"))
+...
 
 -}
 
diff --git a/Data/HList/Data.hs b/Data/HList/Data.hs
--- a/Data/HList/Data.hs
+++ b/Data/HList/Data.hs
@@ -1,11 +1,15 @@
-{-# LANGUAGE CPP, DeriveDataTypeable #-}
+{-# LANGUAGE CPP #-}
 
-{- | 'Data.Data.Data' instances for 'HListFlat' and 'Record' which pretend
+{- | Description: Data instances
+
+
+'Data.Data.Data' instances for 'HListFlat' and 'Record' which pretend
 to be flat data structures. The @Data@ instance for 'HList' gives a nested
 structure.
 
-NOTE: these instances do not work with ghc-7.8 because of
-<http://ghc.haskell.org/trac/ghc/ticket/8486>
+NOTE: these instances do not work with ghc-7.8 with promoted
+string (Symbol) labels because of
+<https://ghc.haskell.org/trac/ghc/ticket/9111>
 
 [@HList@]
 
@@ -48,50 +52,53 @@
     -- * exports for type signatures/ haddock usage
     DataHListFlatCxt,
     DataRecordCxt,
-    TypeRepsList,
+    TypeRepsList(..),
 
     -- ** less likely to be used
     RecordLabelsStr(..),
     GfoldlK(..),
     GunfoldK(..),
     HListFlat(..),
+    TypeablePolyK,
     ) where
 
 import Data.HList.FakePrelude
 import Data.HList.HList
 import Data.HList.Record
-import GHC.TypeLits
+import Data.HList.Variant
 import Data.Data
-import Data.List
-import GHC.Exts (Constraint)
-
-import Unsafe.Coerce
+import Data.HList.TIC
+import Data.HList.TIP
 
 
-instance (Data x, Data (HList xs), Typeable (HList (x ': xs)),
-        TypeablePolyK (x ': xs))
-        => Data (HList (x ': xs)) where
-    gfoldl k z (HCons a b) = (z HCons `k` a) `k` b
-    gunfold k z _ = k (k (z HCons))
+#if OLD_TYPEABLE
+import Data.List
+#endif
 
-    dataTypeOf _ = hListDataRep
-    toConstr _   = hConsConRep
+import Unsafe.Coerce
 
 
-instance (TypeablePolyK ('[] :: [*])) => Data (HList '[]) where
-    gfoldl _k z HNil = z HNil
-    gunfold _k z _ = z HNil
-    dataTypeOf _ = hListDataRep
-    toConstr _   = hNilConRep
+deriving instance Typeable (HList '[]) => Data (HList '[])
+deriving instance
+    (Data x,
+     Data (HList xs),
+     TypeablePolyK (x ': xs), -- for new typeable
+     Typeable (HList (x ': xs) -- for old typeable
+     )) => Data (HList (x ': xs))
 
-hListDataRep = mkDataType "Data.HList.HList" [hConsConRep, hNilConRep]
-hConsConRep = mkConstr hListDataRep "HCons" [] Prefix
-hNilConRep = mkConstr hListDataRep "HNil" [] Prefix
+deriving instance
+    (TypeablePolyK xs,
+     Typeable (HList xs),
+     Data (HList xs)) => Data (TIP xs)
+deriving instance
+    (TypeablePolyK xs,
+     Typeable (Variant xs),
+     Data (Variant xs)) => Data (TIC xs)
 
 -- | this data type only exists to have Data instance
 newtype HListFlat a = HListFlat (HList a)
 
-type DataHListFlatCxt g a = (HBuild' '[] g,
+type DataHListFlatCxt na g a = (HBuild' '[] g,
         Typeable (HListFlat a),
         TypeablePolyK a,
         HFoldl (GfoldlK  C) (C g) a (C (HList a)),
@@ -99,12 +106,14 @@
         HFoldr
             (GunfoldK C)
             (C g)
-            (HReplicateR (HLength a) ())
+            (HReplicateR na ())
             (C (HList a)),
 
-        HReplicate (HLength a) ())
+        HLengthEq a na,
+        HReplicate na ()
+        )
 
-instance DataHListFlatCxt g a => Data (HListFlat a) where
+instance DataHListFlatCxt na g a => Data (HListFlat a) where
     gfoldl k z (HListFlat xs) = c3 $
                     hFoldl
                         (c1 (GfoldlK k))
@@ -182,7 +191,7 @@
     recordLabelsStr _ = []
 instance (RecordLabelsStr xs,
           ShowLabel x) => RecordLabelsStr (Tagged x t ': xs) where
-    recordLabelsStr _ = showLabel (undefined :: Label x) :
+    recordLabelsStr _ = showLabel (Label :: Label x) :
                             recordLabelsStr (undefined :: Record xs)
 
 {- |
@@ -202,8 +211,8 @@
     recordLabelsStr2 _ = []
 instance (RecordLabelsStr2 xs,
           ShowLabel x) => RecordLabelsStr2 (x ': xs) where
-    recordLabelsStr2 _ = showLabel (undefined :: Label x) :
-                            recordLabelsStr2 (undefined :: proxy xs)
+    recordLabelsStr2 _ = showLabel (Label :: Label x) :
+                            recordLabelsStr2 (Proxy :: Proxy xs)
 
 
 -- | use only with @instance Data (HList a)@. This is because the HFoldl
@@ -212,13 +221,23 @@
 data C a
 
 -- typeable isntances... either hand written or derived when possible
-#if MIN_VERSION_base(4,7,0)
+#if !OLD_TYPEABLE
 deriving instance Typeable Record
 deriving instance Typeable HList
 deriving instance Typeable HListFlat
--- deriving instance Typeable Tagged
+deriving instance Typeable Variant
+deriving instance Typeable TIC
+deriving instance Typeable TIP
 
-type TypeablePolyK (a :: k) = (Typeable a)
+-- orphans
+deriving instance Typeable 'HZero
+deriving instance Typeable 'HSucc
+#if !MIN_VERSION_base(4,8,0)
+-- GHC 7.9 adds these instances
+deriving instance Typeable '[]
+deriving instance Typeable '(:)
+#endif
+
 #else
 instance TypeRepsList (Record xs) => Typeable (HList xs) where
    typeOf x = mkTyConApp (mkTyCon3 "HList" "Data.HList.HList" "HList")
@@ -228,48 +247,59 @@
   typeOf x = mkTyConApp (mkTyCon3 "HList" "Data.HList.Record" "Record")
                 [ tyConList (typeRepsList x) ]
 
+instance TypeRepsList (Record xs) => Typeable (Variant xs) where
+  typeOf _ = mkTyConApp (mkTyCon3 "HList" "Data.HList.Variant" "Variant")
+                [ tyConList (typeRepsList (error "Data.HList.Data:Typeable Variant" :: Record xs)) ]
+
+instance Typeable (Variant xs) => Typeable (TIC xs) where
+  typeOf (TIC xs) = mkTyConApp (mkTyCon3 "HList" "Data.HList.TIC" "TIC")
+                      [typeOf xs]
+
+instance Typeable (HList xs) => Typeable (TIP xs) where
+  typeOf (TIP xs) = mkTyConApp (mkTyCon3 "HList" "Data.HList.TIP" "TIP")
+                      [typeOf xs]
+
 instance ShowLabel sy => Typeable1 (Tagged sy) where
   typeOf1 _ = mkTyConApp
-        (mkTyCon3 "HList" "Data.HList.Data" (showLabel (undefined :: Label sy)))
+        (mkTyCon3 "HList" "Data.HList.Data" (showLabel (Label :: Label sy)))
         []
 
 instance (ShowLabel sy, Typeable x) => Typeable (Tagged sy x) where
   typeOf _ = mkTyConApp
-            (mkTyCon3 "GHC" "GHC.TypeLits" (showLabel (undefined :: Label sy)))
+            (mkTyCon3 "GHC" "GHC.TypeLits" (showLabel (Label :: Label sy)))
             [mkTyConApp (mkTyCon3 "HList" "Data.HList.Record" "=") [],
-                    typeOf (undefined :: x)
+                    typeOf (error "Data.HList.Data:Typeable Tagged" :: x)
                     ]
 
-type TypeablePolyK a = (() :: Constraint)
 
-
 instance Typeable (HList a) => Typeable (HListFlat a) where
     typeOf _ = mkTyConApp (mkTyCon3 "HList" "Data.HList.Data" "HListFlat")
             [typeOf (error "Typeable HListFlat" :: HList a)]
-#endif
 
-
-
 -- pretty-prints sort of like a real list
 tyConList xs = mkTyConApp open ( intersperse comma xs ++ [close] )
     where
     open = mkTyCon3 "GHC" "GHC.TypeLits" "["
     close = mkTyConApp (mkTyCon3 "GHC" "GHC.TypeLits" "]") []
     comma = mkTyConApp (mkTyCon3 "GHC" "GHC.TypeLits" ",") []
+#endif
 
 
+
+
+
 class TypeRepsList a where
   typeRepsList :: a -> [TypeRep]
 
 
-instance (TypeRepsList (Prime xs), ConvHList xs) => TypeRepsList (Record xs) where
-  typeRepsList (Record xs) = typeRepsList (prime xs)
+instance (TypeRepsList (HList xs)) => TypeRepsList (Record xs) where
+  typeRepsList (Record xs) = typeRepsList xs
 
-instance (TypeRepsList xs, Typeable x) => TypeRepsList (HCons' x xs) where
-  typeRepsList (~(x `HCons'` xs))
+instance (TypeRepsList (HList xs), Typeable x) => TypeRepsList (HList (x ': xs)) where
+  typeRepsList (~(x `HCons` xs))
         = typeOf x : typeRepsList xs
 
-instance TypeRepsList HNil' where
+instance TypeRepsList (HList '[]) where
   typeRepsList _ = []
 
 
diff --git a/Data/HList/FakePrelude.hs b/Data/HList/FakePrelude.hs
--- a/Data/HList/FakePrelude.hs
+++ b/Data/HList/FakePrelude.hs
@@ -1,5 +1,4 @@
-{-# LANGUAGE OverlappingInstances #-}
--- just for the proxy
+{-# LANGUAGE CPP #-}
 
 {- |
    The HList library
@@ -11,31 +10,70 @@
 
 module Data.HList.FakePrelude
     (module Data.HList.FakePrelude,
-     module Data.Proxy) where
+     module Data.Proxy,
+     module Data.Tagged,
+     Monoid(..)) where
 
 import Data.Proxy
+import Data.Tagged
 import GHC.Prim (Constraint)
 import GHC.TypeLits
+import Control.Applicative
+#if NEW_TYPE_EQ
+import Data.Type.Equality (type (==))
+#endif
 
+#if !OLD_TYPEABLE
+import Data.Typeable
+#endif
+
+#if __GLASGOW_HASKELL__ < 709
+import Data.Monoid
+#endif
+
+
 -- --------------------------------------------------------------------------
 -- * A heterogeneous apply operator
 
 -- | simpler/weaker version where type information only propagates forward
--- with this one. 'app' defined below, is more complicated / verbose to define,
+-- with this one. 'applyAB' defined below, is more complicated / verbose to define,
 -- but it offers better type inference. Most uses have been converted to
--- 'app', so there is not much that can be done with 'Apply'.
+-- 'applyAB', so there is not much that can be done with 'Apply'.
 class Apply f a where
   type ApplyR f a :: *
   apply :: f -> a -> ApplyR f a
-  apply = undefined                     -- In case we use Apply for
-                                        -- type-level computations only
 
 {- $note
 
- Polymorphic functions are not first-class in haskell. One solution is to
- write an instance of 'ApplyAB' for a data type that takes the place of
- the original function. In other words,
+ Polymorphic functions are not first-class in haskell. An example of this
+ is:
 
+ > f op = (op (1 :: Double), op (1 :: Int))
+
+ [@RankNTypes@]
+
+ One solution is to enable `-XRankNTypes` and then write a type
+ signature which might be `f :: (forall a. Num a => a -> a)`. This
+ does not work in the context of HList, since we want to use functions
+ that do not necessarily fall into the pattern of (forall a. c a => a -> a).
+
+ [@MultipleArguments@]
+
+ Another solution is to rewrite @op@ to look like
+
+ > f op1 op2 = (op1 (1:: Double), op2 (1 :: Int))
+
+ In some sense this approach works (see HZip), but the result
+ is constrained to as many function applications as you are willing to
+ write (ex. a function that works for records of six entries would
+ look like @hBuild f f f f f f@).
+
+
+ [@Defunctionalization@]
+
+ Therefore the selected solution is to write an instance of 'ApplyAB' for a data
+ type that takes the place of the original function. In other words,
+
  > data Fn = Fn
  > instance ApplyAB Fn a b where applyAB Fn a = actual_fn a
 
@@ -56,15 +94,54 @@
  The first instance allows types to be inferred as if we had
  @class ApplyAB a b c | a -> b c@, while the second instance
  only matches if ghc already knows that it needs
- @ApplyAB Fn Int Double@. Additional explanation can be found
+ @ApplyAB Fn Int Double@. Since @applyAB Fn :: Int -> Double@
+ has a monomorphic type, this trimmed down example does not
+ really make sense because @applyAB (fromIntegral :: Int -> Double)@
+ is exactly the same. Nontheless, the other uses of @ApplyAB@
+ follow this pattern, and the benefits are seen when the type of
+ @applyAB Fn@ has at least one type variable.
+
+ Additional explanation can be found
  in <http://okmij.org/ftp/Haskell/typecast.html#local-fd local functional dependencies>
 
+
+ [@AmbiguousTypes@]
+
+ Note that ghc only allows AllowAmbiguousTypes when a type
+ signature is provided. Thus expressions such as:
+
+ > data AddJust = AddJust
+ > instance (y ~ Maybe x) => ApplyAB AddJust x y where
+ >    applyAB _ x = Just x
+ >
+ > twoJustsBad = hMap AddJust . hMap AddJust -- ambiguous type
+
+ Are not accepted without a type signature that references the
+ intermediate \"b\":
+
+ > twoJusts :: forall r a b c. (HMapCxt r AddJust a b, HMapCxt r AddJust b c) =>
+ >        r a -> r c
+ > twoJusts a = hMap AddJust (hMap AddJust a :: r b)
+
+ An apply class with functional dependencies
+
+ > class ApplyAB' f a b | f a -> b, f b -> a
+
+ Or with equivalent type families
+
+ > class (GetB f a ~ b, GetA f b ~ a) => ApplyAB' f a b
+
+ would not require an annotation for @twoJusts@. However,
+ not all instances of ApplyAB will satisfy those functional
+ dependencies, and thus the number of classes would proliferate.
+ Furthermore, inference does not have to be in one direction
+ only, as the example of 'Data.HList.HList.HMap' shows.
+
 -}
 
 -- | No constraints on result and argument types
 class ApplyAB f a b where
   applyAB :: f -> a -> b
-  applyAB = undefined -- In case we use Apply for type-level computations only
 
 
 {- $fun
@@ -84,10 +161,15 @@
  constraints on the argument type:
 
  >>> :set -XDataKinds
- >>> let plus1 = Fun (\x -> if x < 5 then x+1 else 5) :: Fun '[Num, Ord] '()
+ >>> let plus1f x = if x < 5 then x+1 else 5
+ >>> let plus1 = Fun plus1f :: Fun '[Num, Ord] '()
  >>> :t applyAB plus1
- applyAB plus1 :: (Num a, Ord a) => a -> a
+ applyAB plus1 :: (Num b, Ord b) => b -> b
 
+ >>> let xs = [1 .. 8]
+ >>> map (applyAB plus1) xs == map plus1f xs
+ True
+
  Also note the use of @'()@ to signal that the result
  type is the same as the argument type.
 
@@ -96,7 +178,7 @@
 
  >>> let succ1 = Fun succ :: Fun Enum '()
  >>> :t applyAB succ1
- applyAB succ1 :: Enum a => a -> a
+ applyAB succ1 :: Enum b => b -> b
 
 
  >>> let just = Fun Just :: Fun '[] Maybe
@@ -230,7 +312,7 @@
 
 -- | (\(a,b) -> f a >> b)
 newtype HSeq x = HSeq x
-instance (Monad m, ApplyAB f x fx, fx ~ m (), pair ~ (x,m ()), 
+instance (Monad m, ApplyAB f x fx, fx ~ m (), pair ~ (x,m ()),
           ApplyAB f x (m ()) ) => ApplyAB (HSeq f) pair fx where
   applyAB (HSeq f) (x,c) = do asVoid (applyAB f x); c
     where asVoid :: m () -> m ()
@@ -249,17 +331,45 @@
 instance (f1 ~ (a -> b -> c), f2 ~ (b -> a -> c))  => ApplyAB HFlip f1 f2 where
     applyAB _ = flip
 
+
+-- | 'fmap'
+newtype HFmap f = HFmap f
+
+instance (x ~ t a,
+          y ~ t b,
+          Functor t,
+          ApplyAB f a b) =>
+  ApplyAB (HFmap f) x y where
+    applyAB (HFmap f) = fmap (applyAB f)
+
+
+-- | 'liftA2'
+newtype LiftA2 f = LiftA2 f
+
+instance (ApplyAB f (x,y) z,
+          mz ~ m z,
+          mxy ~ (m x, m y),
+          Applicative m) => ApplyAB (LiftA2 f) mxy mz where
+    applyAB (LiftA2 f) xy = liftA2 (curry (applyAB f)) `uncurry` xy
+
+
+-- | 'untag'
+data HUntag = HUntag
+instance (Tagged t x ~ tx) => ApplyAB HUntag tx x where
+    applyAB _ (Tagged x) = x
+
+
 -- --------------------------------------------------------------------------
 -- * Proxy
 --
 
--- $note see "Data.HList.Proxy"
+-- $note see "Data.Proxy"
 
 -- | A special 'Proxy' for record labels, polykinded
 data Label l = Label
 
 labelToProxy :: Label l -> Proxy l
-labelToProxy = undefined
+labelToProxy _ = Proxy
 
 class ShowLabel l where
   showLabel :: Label l -> String
@@ -290,11 +400,8 @@
 -}
 
 -- ** Value-level proxies
-hTrue  :: Proxy True ; hTrue  = undefined
-hFalse :: Proxy False; hFalse = undefined
-
-instance Show (Proxy True)  where show _ = "HTrue"
-instance Show (Proxy False) where show _ = "HFalse"
+hTrue  :: Proxy True ; hTrue  = Proxy
+hFalse :: Proxy False; hFalse = Proxy
 
 
 -- **  Conjunction
@@ -305,7 +412,7 @@
 
 -- | `demote' to values
 hAnd :: Proxy t1 -> Proxy t2 -> Proxy (HAnd t1 t2)
-hAnd = undefined
+hAnd _ _ = Proxy
 
 
 -- ** Disjunction
@@ -316,7 +423,7 @@
 
 -- | `demote' to values
 hOr :: Proxy t1 -> Proxy t2 -> Proxy (HOr t1 t2)
-hOr = undefined
+hOr _ _ = Proxy
 
 {- $boolHistoricalNote
 
@@ -343,7 +450,19 @@
 >   hOr _ _ = hTrue
 -}
 
+type family HNot (x :: Bool) :: Bool
+type instance HNot True = False
+type instance HNot False = True
 
+-- | as compared with 'HNot' this version is injective
+class HNotFD (b :: Bool) (nb :: Bool) | b -> nb, nb -> b
+instance HNotFD True False
+instance HNotFD False True
+
+hNot :: HNotFD a notA => Proxy a -> Proxy notA
+hNot _ = Proxy
+
+
 class HCond (t :: Bool) x y z | t x y -> z
  where
   hCond :: Proxy t -> x -> y -> z
@@ -376,9 +495,9 @@
 data HNat = HZero | HSucc HNat
 
 
-hZero :: Proxy HZero; hZero = undefined
-hSucc :: Proxy (n :: HNat) -> Proxy (HSucc n); hSucc _ = undefined
-hPred :: Proxy (HSucc n) -> Proxy n; hPred _ = undefined
+hZero :: Proxy HZero; hZero = Proxy
+hSucc :: Proxy (n :: HNat) -> Proxy (HSucc n); hSucc _ = Proxy
+hPred :: Proxy (HSucc n) -> Proxy n; hPred _ = Proxy
 
 class HNat2Integral (n::HNat) where
     hNat2Integral :: Integral i => Proxy n -> i
@@ -389,10 +508,21 @@
 instance HNat2Integral n => HNat2Integral (HSucc n) where
     hNat2Integral n = hNat2Integral (hPred n) + 1
 
-instance HNat2Integral n => Show (Proxy (n :: HNat)) where 
-    show n = "H" ++ show (hNat2Integral n :: Integer)
 
+class HNats2Integrals (ns :: [HNat]) where
+    hNats2Integrals :: Integral i => Proxy ns -> [i]
 
+instance HNats2Integrals '[] where
+    hNats2Integrals _ = []
+
+instance (HNats2Integrals ns,
+          HNat2Integral n)
+  => HNats2Integrals (n ': ns) where
+    hNats2Integrals _ = hNat2Integral (Proxy :: Proxy n) :
+                        hNats2Integrals (Proxy :: Proxy ns)
+
+
+
 -- | Equality on natural numbers
 -- (eventually to be subsumed by the universal polykinded HEq)
 type family HNatEq (t1 :: HNat) (t2 :: HNat) :: Bool
@@ -412,9 +542,26 @@
 type instance HLt (HSucc n) (HSucc n') = HLt  n n'
 
 hLt :: Proxy x -> Proxy y -> Proxy (HLt x y)
-hLt = undefined
+hLt _ _ = Proxy
 
 
+-- | Less than or equal to
+type family HLe (x :: HNat) (y :: HNat) :: Bool
+
+type instance HLe HZero HZero          = True
+type instance HLe (HSucc x) y          = HLt x y
+
+hLe :: Proxy x -> Proxy y -> Proxy (HLe x y)
+hLe _ _ = Proxy
+
+-- | @HDiv2 x@ behaves like @x `div` 2@
+type family HDiv2 (x :: HNat) :: HNat
+type instance HDiv2 HZero = HZero
+type instance HDiv2 (HSucc HZero) = HZero
+type instance HDiv2 (HSucc (HSucc a)) = HSucc (HDiv2 a)
+
+
+
 -- --------------------------------------------------------------------------
 -- * Maybies
 -- $maybiesNote We cannot use lifted Maybe since the latter are not populated
@@ -430,17 +577,49 @@
 -- for the sake of the generic equality.
 class HEq (x :: k) (y :: k) (b :: Bool) | x y -> b
 
--- Equality instances for naturals
+-- | Equality for types that may have different kinds. This definition
+-- allows operations on @Record [Tagged \"x\" a, Tagged 2 b]@ to work
+-- as expected.
+type HEqK (x :: k1) (y :: k2) (b :: Bool) = HEq (Proxy x) (Proxy y) b
 
-instance HEq HZero HZero     True
-instance HEq HZero (HSucc n) False
-instance HEq (HSucc n) HZero False
-instance HEq  n n' b => HEq (HSucc n) (HSucc n') b
+#if NEW_TYPE_EQ
+-- | Uses @(==) :: * -> * -> Bool@ because
+-- there is no polykinded instance of (==),
+-- since that one overlaps "more productive"
+-- instances that pattern match on types.
+instance ((Proxy x == Proxy y) ~ b) => HEq x y b
+#endif
 
 hEq :: HEq x y b => x -> y -> Proxy b
-hEq =  undefined
+hEq _ _ = Proxy
 
 
+-- | this class generalizes HEq by allowing the choice of @f@ to allow
+-- equating only part of x and y
+class HEqByFn f => HEqBy (f :: t) (x :: k) (y :: k) (b :: Bool) | f x y -> b
+
+
+
+
+-- | Every instance of this class should have an instance of 'HEqBy'
+class HEqByFn f
+
+-- * Arity
+
+type Arity f n = (ArityFwd f n, ArityRev f n)
+
+-- | calculate the number of arguments a function can take
+class ArityFwd (f :: *) (n :: HNat) | f -> n
+
+
+-- | given the number of arguments a function can take, make sure
+-- the function type actually matches
+class ArityRev (f :: *) (n :: HNat) -- n -> f -- if we had -XDysfunctionalDependencies
+
+instance ArityRev f HZero
+instance (xf ~ (x -> f), ArityRev f n) => ArityRev xf (HSucc n)
+
+
 -- --------------------------------------------------------------------------
 
 -- * Staged equality
@@ -451,45 +630,37 @@
 --  * Establish remaining value-level equality dynamically
 --
 -- removed: use typeable
-{-
 
-class HStagedEq x y
- where
-  hStagedEq :: x -> y -> Bool
--}
 
-
-{-
 -- --------------------------------------------------------------------------
+-- * Type-safe cast -- no longer need. We use a a ~ b
 
--- | A predicate for type equality
---
--- There are different implementations: see TypeEq*.hs
 
-class HBool b => TypeEq x y b | x y -> b
+-- * Cast
 
+-- | Named after 'Data.Typeable.cast', which behaves the same at runtime.
+-- One difference is that there is a HCast instance for every type, while
+-- 'Typeable' instances can be missing sometimes.
 
--- Rely on lazy show for type-level Booleans
-typeEq :: TypeEq t t' b => t -> t' -> b
-typeEq = undefined
+class HCast x y where
+    hCast :: x -> Maybe y
 
+instance (HEq x y b, HCast1 b x y) => HCast x y where
+    hCast = hCast1 (Proxy :: Proxy b)
 
--- A more disciplined version: based on proxies
-proxyEq :: TypeEq t t' b => Proxy t -> Proxy t' -> b
-proxyEq _ _ = undefined
+-- | helper for 'HCast'
+class HCast1 (b :: Bool) x y where
+    hCast1 :: Proxy b -> x -> Maybe y
 
--}
+instance (x ~ y) => HCast1 True x y where
+    hCast1 _ x = Just x
 
--- --------------------------------------------------------------------------
--- * Type-safe cast -- no longer need. We use a a ~ b
+instance HCast1 False x y where
+    hCast1 _ _ = Nothing
 
-{-
-class TypeCast x y | x -> y, y -> x
- where
-  typeCast :: x -> y
--}
 
 
+
 -- --------------------------------------------------------------------------
 
 -- * Error messages
@@ -498,3 +669,101 @@
 class Fail x
 
 
+-- ** Uses of fail
+-- $note these could be replaced by `'("helpful message", l)`,
+-- but these look better.
+data ExtraField l
+data FieldNotFound l
+
+
+#if OLD_TYPEABLE
+type TypeablePolyK a = (() :: Constraint)
+#else
+type TypeablePolyK (a :: k) = Typeable a
+#endif
+
+-- * Constraining Lists
+-- ** Length
+
+-- | Ensure two lists have the same length. We do case analysis on the
+-- first one (hence the type must be known to the type checker).
+-- In contrast, the second list may be a type variable.
+class SameLength' (es1 :: [k]) (es2 :: [m])
+instance (es2 ~ '[]) => SameLength' '[] es2
+instance (SameLength' xs ys, es2 ~ (y ': ys)) => SameLength' (x ': xs) es2
+
+{- | symmetrical version of 'SameLength''. Written as a class instead of
+
+ > type SameLength a b = (SameLength' a b, SameLength' b a)
+
+since ghc expands type synonyms, but not classes (and it seems to have the same
+result)
+
+-}
+class (SameLength' x y, SameLength' y x) =>
+        SameLength (x :: [k]) (y :: [m]) where
+
+  {- | @SameLength x y => Equality (r x) (q y) (r x) (q y)@
+
+  used like 'Control.Lens.simple', except it restricts
+  the type-level lists involved to have the same length,
+  without fixing the type of container or the elements
+  in the list.
+  -}
+  sameLength :: r x `p` f (q y) -> r x `p` f (q y)
+  sameLength = id
+
+asLengthOf :: SameLength x y => r x -> s y -> r x
+asLengthOf = const
+
+
+instance (SameLength' x y, SameLength' y x) => SameLength x y
+
+type family SameLengths (xs :: [[k]]) :: Constraint
+type instance SameLengths (x ': y ': ys) = (SameLength x y, SameLengths (y ': ys))
+type instance SameLengths '[] = ()
+type instance SameLengths '[x] = ()
+
+-- ** Labels
+
+class SameLabels (x :: k) (y :: m)
+
+{- | @sameLabels@ constrains the type of an optic, such that the labels
+   (@t@ in @Tagged t a@) are the same. @x@ or @y@ may have more elements
+   than the other, in which case the elements at the end
+   of the longer list do not have their labels constrained.
+
+   see also 'sameLength'
+-}
+sameLabels :: SameLabels x y => p (r x) (f (q y)) -> p (r x) (f (q y))
+sameLabels = id
+
+-- instances for [*] kind
+instance SameLabels '[] '[]
+instance SameLabels '[] (x ': xs)
+instance SameLabels (x ': xs) '[]
+instance (SameLabels x y, SameLabels xs ys) =>
+  SameLabels (x ': xs) (y ': ys)
+
+
+instance (Label t ~ Label t') => SameLabels (Label t) (Tagged t' a)
+instance (Label t ~ Label t') => SameLabels (Label t) (Label t')
+instance (Label t ~ Label t') => SameLabels (Label t) (t' :: Symbol)
+
+instance SameLabels (Label t) s => SameLabels (t :: Symbol) s
+instance SameLabels (Label t) s => SameLabels (Tagged t a) s
+
+-- ** A list has only Tagged values
+
+-- | The 'Record', 'Variant', 'TIP', 'TIC' type constructors only make
+-- sense when they are applied to an instance of this class
+class HAllTaggedLV (ps :: [*])
+instance HAllTaggedLV '[]
+instance (HAllTaggedLV xs, x ~ Tagged t v) => HAllTaggedLV (x ': xs)
+
+
+-- | see Data.HList.Record.'zipTagged'
+type family ZipTagged (ts :: [k]) (vs :: [*]) :: [*]
+type instance ZipTagged (Label t ': ts) (v ': vs) = Tagged t v ': ZipTagged ts vs
+type instance ZipTagged ((t :: Symbol) ': ts) (v ': vs) = Tagged t v ': ZipTagged ts vs
+type instance ZipTagged '[] '[] = '[]
diff --git a/Data/HList/HArray.hs b/Data/HList/HArray.hs
--- a/Data/HList/HArray.hs
+++ b/Data/HList/HArray.hs
@@ -60,6 +60,9 @@
   type HUpdateAtHNatR  (HSucc n) e1 (e ': l) = e ': (HUpdateAtHNatR n e1 l)
   hUpdateAtHNat n e1 (HCons e l) = HCons e (hUpdateAtHNat (hPred n) e1 l)
 
+instance Fail (FieldNotFound (Proxy n, e1)) => HUpdateAtHNat n e1 '[] where
+  type HUpdateAtHNatR n e1 '[] = '[]
+  hUpdateAtHNat _ _ _ = error "Data.HList.HArray.HUpdateAtHNat: Fail must have no instances"
 
 -- --------------------------------------------------------------------------
 -- * Projection
@@ -70,7 +73,7 @@
 
 newtype FHLookupByHNat (l :: [*]) = FHLookupByHNat (HList l)
 
-instance HLookupByHNat n l => 
+instance HLookupByHNat n l =>
     Apply (FHLookupByHNat l) (Proxy (n :: HNat)) where
   type ApplyR (FHLookupByHNat l) (Proxy n) = HLookupByHNatR n l
   apply (FHLookupByHNat l) n               = hLookupByHNat  n l
@@ -80,7 +83,7 @@
 -- repeated traversals of the HList l at run-time.
 
 -- Here is a more optimal version with a better separation of
--- compile-time and run-time computation. 
+-- compile-time and run-time computation.
 -- The list of labels to project is type-level only.
 -- We treat this list of labels as a set -- that is, we will
 -- ignore duplicates.
@@ -90,11 +93,11 @@
 -- We unify hProjectByHNats and hProjectAwayByHNats in one
 -- function, distinguished by the sel :: Bool in
 -- FHUProj below. The operation hProjectByHNats corresponds
--- to sel = True (that is, elements of l whose indices are found in 
+-- to sel = True (that is, elements of l whose indices are found in
 -- ns are to be included in the result), whereas hProjectByHNats
 -- corresponds to set = False.
 
-hProjectByHNats (_ :: Proxy (ns :: [HNat])) l = 
+hProjectByHNats (_ :: Proxy (ns :: [HNat])) l =
     hUnfold (FHUProj :: FHUProj True ns) (l,hZero)
 
 data FHUProj (sel :: Bool) (ns :: [HNat]) = FHUProj
@@ -103,25 +106,25 @@
     type ApplyR (FHUProj sel ns) (HList '[],n) = HNothing
     apply _ _ = HNothing
 
-instance (ch ~ Proxy (HBoolEQ sel (KMember n ns)), 
-	  Apply (ch, FHUProj sel ns) (HList (e ': l),Proxy (n :: HNat))) =>
+instance (ch ~ Proxy (HBoolEQ sel (KMember n ns)),
+          Apply (ch, FHUProj sel ns) (HList (e ': l),Proxy (n :: HNat))) =>
     Apply (FHUProj sel ns) (HList (e ': l),Proxy (n :: HNat)) where
-    type ApplyR (FHUProj sel ns) (HList (e ': l),Proxy n) = 
+    type ApplyR (FHUProj sel ns) (HList (e ': l),Proxy n) =
        ApplyR (Proxy (HBoolEQ sel (KMember n ns)), FHUProj sel ns)
-	      (HList (e ': l),Proxy n)
-    apply fn s = apply (undefined::ch,fn) s
+              (HList (e ': l),Proxy n)
+    apply fn s = apply (Proxy::ch,fn) s
 
-instance Apply (Proxy True, FHUProj sel ns) 
+instance Apply (Proxy True, FHUProj sel ns)
                (HList (e ': l),Proxy (n::HNat)) where
-    type ApplyR (Proxy True, FHUProj sel ns) (HList (e ': l),Proxy n) = 
-	(HJust (e, (HList l,Proxy (HSucc n))))
+    type ApplyR (Proxy True, FHUProj sel ns) (HList (e ': l),Proxy n) =
+        (HJust (e, (HList l,Proxy (HSucc n))))
     apply _ (HCons e l,n) = (HJust (e,(l,hSucc n)))
 
 instance (Apply (FHUProj sel ns) (HList l, Proxy (HSucc n))) =>
-    Apply (Proxy False, FHUProj sel ns) 
+    Apply (Proxy False, FHUProj sel ns)
           (HList (e ': l),Proxy (n::HNat)) where
-    type ApplyR (Proxy False, FHUProj sel ns) (HList (e ': l),Proxy n) = 
-	ApplyR (FHUProj sel ns) (HList l, Proxy (HSucc n))
+    type ApplyR (Proxy False, FHUProj sel ns) (HList (e ': l),Proxy n) =
+        ApplyR (FHUProj sel ns) (HList l, Proxy (HSucc n))
     apply (_,fn) (HCons _ l,n) = apply fn (l,hSucc n)
 
 
@@ -131,13 +134,14 @@
 type instance KMember n (n1 ': l) = HOr (HNatEq n n1) (KMember n l)
 
 -- Useful abbreviations for complex types (which are inferred)
-type HProjectByHNatsR (ns :: [HNat]) (l :: [*]) = 
+type HProjectByHNatsR (ns :: [HNat]) (l :: [*]) =
     HUnfold (FHUProj True ns) (HList l, Proxy 'HZero)
 
 type HProjectByHNatsCtx ns l =
   (Apply (FHUProj True ns) (HList l, Proxy 'HZero),
-      HUnfold' (FHUProj True ns) 
-       (ApplyR (FHUProj True ns) (HList l, Proxy 'HZero)))
+      HUnfold' (FHUProj True ns)
+       (HList l, Proxy 'HZero)
+    )
 
 -- * Complement of Projection
 
@@ -146,20 +150,20 @@
 -- Instead, we compute the complement of indices to project away
 -- to obtain the indices to project to, and then use hProjectByHNats.
 -- Only the latter requires run-time computation. The rest
--- are done at compile-time only. 
+-- are done at compile-time only.
 
-hProjectAwayByHNats (_ :: Proxy (ns :: [HNat])) l = 
+hProjectAwayByHNats (_ :: Proxy (ns :: [HNat])) l =
     hUnfold (FHUProj :: FHUProj False ns) (l,hZero)
 
 
 -- Useful abbreviations for complex types (which are inferred)
-type HProjectAwayByHNatsR (ns :: [HNat]) (l :: [*]) = 
+type HProjectAwayByHNatsR (ns :: [HNat]) (l :: [*]) =
     HUnfold (FHUProj False ns) (HList l, Proxy 'HZero)
 
 type HProjectAwayByHNatsCtx ns l =
   (Apply (FHUProj False ns) (HList l, Proxy 'HZero),
-      HUnfold' (FHUProj False ns) (ApplyR (FHUProj False ns) 
-				   (HList l, Proxy 'HZero)))
+      HUnfold' (FHUProj False ns) (HList l, Proxy 'HZero)
+  )
 
 -- * Splitting
 -- | Splitting an array according to indices
@@ -167,7 +171,7 @@
 -- The following is not optimal; we'll optimize later if needed
 
 hSplitByHNats ns l = (hProjectByHNats ns l,
-		      hProjectAwayByHNats ns l)
+                      hProjectAwayByHNats ns l)
 {-
 hSplitByHNats ns l = hSplitByHNats' ns (hFlag l)
 
diff --git a/Data/HList/HCurry.hs b/Data/HList/HCurry.hs
new file mode 100644
--- /dev/null
+++ b/Data/HList/HCurry.hs
@@ -0,0 +1,72 @@
+{- |
+
+ Description : curry / uncurry
+
+ Convert between functions taking HLists and functions taking many arguments
+
+-}
+module Data.HList.HCurry where
+
+import Data.HList.FakePrelude
+import Data.HList.HList
+import Data.HList.TypeEqO () -- Arity instance
+
+{- | 'curry'/'uncurry' for many arguments and HLists instead of tuples
+
+XXX the last FD @xs -> n@ is needed to make hCompose infer the right types:
+arguably it shouldn't be needed
+
+-}
+class HLengthEq xs n => HCurry' (n :: HNat) f xs r
+          | f xs -> r, r xs -> f, n f -> xs, xs -> n where
+    hUncurry' :: Proxy n -> f -> HList xs -> r
+    hCurry' :: Proxy n -> (HList xs -> r) -> f
+
+instance HCurry' HZero b '[] b where
+    hUncurry' _ b _ = b
+    hCurry' _ f = f HNil
+
+instance (HCurry' n b xs r) => HCurry' (HSucc n) (x -> b) (x ': xs) r where
+    hUncurry' n f (HCons x xs) = hUncurry' (hPred n) (f x) xs
+    hCurry' n f x = hCurry' (hPred n) (f . HCons x)
+
+hUncurry f = hUncurry' (arityOf f) f
+
+-- | Note: with ghc-7.10 the Arity constraint added here does not work
+-- properly with hCompose, so it is possible that other uses of 'hCurry'
+-- are better served by @hCurry' Proxy@.
+hCurry f = let f' = hCurry' (arityOf f') f
+           in f'
+
+{- | compose two functions that take multiple arguments. The result of the
+second function is the first argument to the first function. An example is
+probably clearer:
+
+>>> let f = hCompose (,,) (,)
+>>> :t f
+f :: ... -> ... -> ... -> ... -> ((..., ...), ..., ...)
+
+>>> f 1 2 3 4
+((1,2),3,4)
+
+Note: polymorphism can make it confusing as to how many parameters a function
+actually takes. For example, the first two ids are @id :: (a -> b) -> (a -> b)@ in
+
+>>> (.) id id id 'y'
+'y'
+
+>>> hCompose id id id 'y'
+'y'
+
+still typechecks, but in that case @hCompose i1 i2 i3 x == i1 ((i2 i3) x)@
+has id with different types than @(.) i1 i2 i3 x == (i1 (i2 i3)) x
+
+Prompted by <http://stackoverflow.com/questions/28932054/can-hlistelim-be-composed-with-another-function>
+
+-}
+hCompose f g = hCurry' Proxy $ \xs -> case hSplitAt Proxy xs of
+        (xg,xf) -> hUncurry f (hUncurry g xg `HCons` xf)
+
+
+arityOf :: Arity f n => f -> Proxy n
+arityOf _ = Proxy
diff --git a/Data/HList/HList.hs b/Data/HList/HList.hs
--- a/Data/HList/HList.hs
+++ b/Data/HList/HList.hs
@@ -15,1023 +15,1573 @@
 import Data.HList.FakePrelude
 import Data.HList.HListPrelude
 
-import Control.Applicative (Applicative, liftA2, pure)
-
-
--- --------------------------------------------------------------------------
--- * Heterogeneous type sequences
--- $note
---
--- The easiest way to ensure that sequences can only be formed with Nil
--- and Cons is to use GADTs
--- The kind [*] is list kind (lists lifted to types)
-
-data HList (l::[*]) where
-    HNil  :: HList '[]
-    HCons :: e -> HList l -> HList (e ': l)
-
--- | this comparison is two traversals
-instance (ConvHList l, Eq (Prime l)) => Eq (HList l) where
-    x == y = prime x == prime y
-
--- ** Alternative representation
-{- $note
-
-HNil' and HCons' are the older ADT-style. This has some advantages
-over the GADT:
-
-* lazy pattern matches are allowed
-
-* type inference is better if you want to directly pattern match
-<http://stackoverflow.com/questions/19077037/is-there-any-deeper-type-theoretic-reason-ghc-cant-infer-this-type see stackoverflow post here>
-
--}
-data HNil' = HNil' deriving (Eq)
-data HCons' a b = HCons' a b deriving (Eq)
-
-
--- | conversion between GADT ('HList') and ADT ('HNil'' 'HCons'')
--- representations
-class (UnPrime (Prime a) ~ a) => ConvHList (a :: [*]) where
-    type Prime a :: *
-    type UnPrime b :: [*]
-    prime :: HList a -> Prime a
-    unPrime :: Prime a -> HList a
-
-instance ConvHList as => ConvHList (a ': as) where
-    type Prime   (a ': as) = a `HCons'` Prime as
-    type UnPrime (b `HCons'` bs) = (b ': UnPrime bs)
-    prime (a `HCons` as) = a `HCons'` prime as
-    unPrime ~(a `HCons'` as) = a `HCons` unPrime as
-
-instance ConvHList '[] where
-    type Prime '[] = HNil'
-    type UnPrime HNil' = '[]
-    prime _ = HNil'
-    unPrime _ = HNil
-
-
-
-
-instance Show (HList '[]) where
-    show _ = "H[]"
-
-instance (Show e, Show (HList l)) => Show (HList (e ': l)) where
-    show (HCons x l) = let 'H':'[':s = show l
-		       in "H[" ++ show x ++ 
-			          (if s == "]" then s else ", " ++ s)
-
-infixr 2 `HCons`
-
-
--- --------------------------------------------------------------------------
--- * Basic list functions
-
--- | 'head'
-hHead :: HList (e ': l) -> e
-hHead (HCons x _) = x
-
--- | 'tail'
-hTail :: HList (e ': l) -> HList l
-hTail (HCons _ l) = l
-
--- | Length
-type family HLength (x :: [k]) :: HNat
-type instance HLength '[] = HZero
-type instance HLength (x ': xs) = HSucc (HLength xs)
-
-hLength   :: HList l -> Proxy (HLength l)
-hLength _ =  undefined
-
--- ** Append
-instance HExtend e (HList l) where
-  type HExtendR e (HList l) = HList (e ': l)
-  (.*.) = HCons
-
-instance HAppend (HList l1) (HList l2) where
-  hAppend = hAppendList
-type instance HAppendR (HList l1) (HList l2) = HList (HAppendList l1 l2)
-
-type family HAppendList (l1 :: [k]) (l2 :: [k]) :: [k]
-type instance HAppendList '[] l = l
-type instance HAppendList (e ': l) l' = e ': HAppendList l l'
-
--- | the same as 'hAppend'
-hAppendList :: HList l1 -> HList l2 -> HList (HAppendList l1 l2)
-hAppendList HNil l = l
-hAppendList (HCons x l) l' = HCons x (hAppend l l')
-
--- --------------------------------------------------------------------------
-
--- ** Alternative append
-
-
--- | 'hAppend'' below is implemented using the same idea
-append' :: [a] -> [a] -> [a]
-append' l l' = foldr (:) l' l
-
--- | Alternative implementation of 'hAppend'. Demonstrates 'HFoldr'
-hAppend' :: (HFoldr FHCons v l r) => HList l -> v -> r
-hAppend' l l' = hFoldr FHCons l' l
-
-data FHCons = FHCons
-
-instance ( x ~ (e,HList l), y ~ (HList (e ': l))) => ApplyAB FHCons x y  where
-    applyAB _ (e,l) = HCons e l
-
-
--- ** Historical append
-
-{- $
-
-The original HList code is included below. In both cases
-we had to program the algorithm twice, at the term and the type levels.
-
-[@The class HAppend@]
-
-> class HAppend l l' l'' | l l' -> l''
->  where
->   hAppend :: l -> l' -> l''
->
-
-[@The instance following the normal append@]
-
-> instance HList l => HAppend HNil l l
->  where
->   hAppend HNil l = l
->
-> instance (HList l, HAppend l l' l'')
->       => HAppend (HCons x l) l' (HCons x l'')
->  where
->   hAppend (HCons x l) l' = HCons x (hAppend l l')
-
--}
-
--- --------------------------------------------------------------------------
--- * Reversing HLists
-
--- Append the reversed l1 to l2
-type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
-type instance HRevApp '[] l = l
-type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
-
-hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
-hRevApp HNil l = l
-hRevApp (HCons x l) l' = hRevApp l (HCons x l')
-
-hReverse l = hRevApp l HNil
-
-
-
--- --------------------------------------------------------------------------
-
---
--- * A nicer notation for lists
---
-
-
--- | List termination
-hEnd :: HList l -> HList l
-hEnd = id
-
-{- ^
-   Note:
-
-        [@x :: HList a@] means: @forall a. x :: HList a@
-
-        [@hEnd x@] means: @exists a. x :: HList a@
--}
-
-
--- |  Building lists
-
-hBuild :: (HBuild' '[] r) => r
-hBuild =  hBuild' HNil
-
-class HBuild' l r where
-    hBuild' :: HList l -> r
-
-instance (l' ~ HRevApp l '[])
-      => HBuild' l (HList l') where
-  hBuild' l = hReverse l
-
-instance HBuild' (a ': l) r
-      => HBuild' l (a->r) where
-  hBuild' l x = hBuild' (HCons x l)
-
--- ** examples
-{- $examplesNote
-
-The classes above allow the third (shortest) way to make a list
-(containing a,b,c) in this case
-
-> list = a `HCons` b `HCons` c `HCons` HNil
-> list = a .*. b .*. c .*. HNil
-> list = hEnd $ hBuild a b c
-
->>> let x = hBuild True in hEnd x
-H[True]
-
->>> let x = hBuild True 'a' in hEnd x
-H[True, 'a']
-
->>> let x = hBuild True 'a' "ok" in hEnd x
-H[True, 'a', "ok"]
-
--}
-
--- *** historical
-{- $hbuild the show instance has since changed, but these uses of
-'hBuild'/'hEnd' still work
-
-> HList> let x = hBuild True in hEnd x
-> HCons True HNil
-
-> HList> let x = hBuild True 'a' in hEnd x
-> HCons True (HCons 'a' HNil)
-
-> HList> let x = hBuild True 'a' "ok" in hEnd x
-> HCons True (HCons 'a' (HCons "ok" HNil))
-
-> HList> hEnd (hBuild (Key 42) (Name "Angus") Cow (Price 75.5))
-> HCons (Key 42) (HCons (Name "Angus") (HCons Cow (HCons (Price 75.5) HNil)))
-
-> HList> hEnd (hBuild (Key 42) (Name "Angus") Cow (Price 75.5)) == angus
-> True
-
--}
-
--- --------------------------------------------------------------------------
-
--- * folds
--- ** foldr
--- $foldNote  Consume a heterogenous list. GADTs and type-classes mix well
-
-
-class HFoldr f v (l :: [*]) r where
-    hFoldr :: f -> v -> HList l -> r
-
-instance (v ~ v') => HFoldr f v '[] v' where
-    hFoldr       _ v _   = v
-
--- | uses 'ApplyAB' not 'Apply'
-instance (ApplyAB f (e, r) r', HFoldr f v l r)
-    => HFoldr f v (e ': l) r' where
-    hFoldr f v (HCons x l)    = applyAB f (x, hFoldr f v l :: r)
-
-
-class HScanr f z ls rs where
-    hScanr :: f -> z -> HList ls -> HList rs
-
-instance HScanr f z '[] '[z] where
-    hScanr _ z _ = HCons z HNil
-
-instance (ApplyAB f (x,r) s, HScanr f z xs (r ': rs)) => HScanr f z (x ': xs) (s ': r ': rs) where
-    hScanr f z (HCons x xs) =
-        case hScanr f z xs :: HList (r ': rs) of
-            HCons r rs -> (applyAB f (x,r) :: s) `HCons` r `HCons` rs
-
-class HFoldr1 f (l :: [*]) r where
-    hFoldr1 :: f -> HList l -> r
-
-instance (v ~ v') => HFoldr1 f '[v] v' where
-    hFoldr1      _ (HCons v _)  = v
-
--- | uses 'ApplyAB' not 'Apply'
-instance (ApplyAB f (e, r) r', HFoldr1 f (e' ': l) r)
-    => HFoldr1 f (e ': e' ': l) r' where
-    hFoldr1 f (HCons x l)    = applyAB f (x, hFoldr1 f l :: r)
-
-
--- ** foldl
-
-{- | like 'foldl'
-
-
->>> hFoldl (uncurry $ flip (:)) [] (1 `HCons` 2 `HCons` HNil)
-[2,1]
-
-
--}
-class HFoldl f (z :: *) xs (r :: *) where
-    hFoldl :: f -> z -> HList xs -> r
-
-instance forall f z z' r x zx xs. (zx ~ (z,x), ApplyAB f zx z', HFoldl f z' xs r)
-    => HFoldl f z (x ': xs) r where
-    hFoldl f z (x `HCons` xs) = hFoldl f (applyAB f (z,x) :: z') xs
-
-instance (z ~ z') => HFoldl f z '[] z' where
-    hFoldl _ z _ = z
-
-
-
-
-
--- * unfold
--- $unfoldNote Produce a heterogenous list. Uses the more limited
--- 'Apply' instead of 'App' since that's all that is needed for uses of this
--- function downstream. Those could in principle be re-written.
-
-hUnfold :: (Apply p s, HUnfold' p (ApplyR p s)) => p -> s -> HList (HUnfold p s)
-hUnfold p s = hUnfold' p (apply p s)
-
-type HUnfold p s = HUnfoldR p (ApplyR p s)
-
-class HUnfold' p res where
-    type HUnfoldR p res :: [*]
-    hUnfold' :: p -> res -> HList (HUnfoldR p res)
-
-instance HUnfold' p HNothing where
-    type HUnfoldR p HNothing = '[]
-    hUnfold' _ _ = HNil
-
-instance (Apply p s, HUnfold' p (ApplyR p s)) => HUnfold' p (HJust (e,s)) where
-    type HUnfoldR p (HJust (e,s)) = e ': HUnfold p s
-    hUnfold' p (HJust (e,s)) = HCons e (hUnfold p s)
-
-
--- * replicate
-
-class (HLength (HReplicateR n e) ~ n) =>
-      HReplicate (n :: HNat) e where
-    hReplicate :: Proxy n -> e -> HList (HReplicateR n e)
-
-instance HReplicate HZero e where
-    hReplicate _ _ = HNil
-
-instance HReplicate n e => HReplicate (HSucc n) e where
-    hReplicate n e = e `HCons` hReplicate (hPred n) e
-
--- | would be associated with 'HReplicate' except we want
--- it to work with `e` of any kind, not just `*` that you can
--- put into a HList. An \"inverse\" of 'HLength'
-type family HReplicateR (n :: HNat) (e :: k) :: [k]
-type instance HReplicateR HZero e = '[]
-type instance HReplicateR (HSucc n) e = e ': HReplicateR n e
-
--- * concat
-
-{- |
-
-Like 'concat' but for HLists of HLists.
-
-Works in ghci... puzzling as what is different in doctest (it isn't
-@-XExtendedDefaultRules@)
-
-> hConcat $ hBuild (hBuild 1 2 3) (hBuild 'a' "abc")
-
-H[1, 2, 3, 'a', "abc"]
-
-
--}
-class HConcat (a :: [*]) where
-    type HConcatR a :: [*]
-    hConcat :: HList a -> HList (HConcatR a)
-
-instance HConcat '[] where
-    type HConcatR '[] = '[]
-    hConcat _ = HNil
-
-instance (x ~ HList t, HConcat xs) => HConcat (x ': xs) where
-    type HConcatR (x ': xs) = HAppendList (UnHList x) (HConcatR xs)
-    hConcat (x `HCons` xs) = x `hAppendList` hConcat xs
-
-
-type family UnHList a :: [*]
-type instance UnHList (HList a) = a
-
--- --------------------------------------------------------------------------
--- * traversing HLists
-
--- ** producing HList
--- *** map
--- $mapNote It could be implemented with 'hFoldr', as we show further below
-
-{- | hMap is written such that the length of the result list
-can be determined from the length of the argument list (and
-the other way around). Similarly, the type of the elements
-of the list is propagated in both directions too.
-
-Excuse the ugly types printed. Unfortunately ghc (still?)
-shows types like @'[a,b]@ using the actual constructors involved
-@(':) a ((':) b '[])@ (or even worse when the kind variables are printed).
-
->>> :set -XNoMonomorphismRestriction
->>> let xs = 1 .*. 'c' .*. HNil
->>> :t hMap (HJust ()) xs
-hMap (HJust ()) xs
-  :: Num y => HList ((':) * (HJust y) ((':) * (HJust Char) ('[] *)))
-
-
-These 4 examples show that the constraint on the length (2 in this cae)
-can be applied before or after the 'hMap'. That inference is independent of the
-direction that type information is propagated for the individual elements.
-
-
->>> let asLen2 xs = xs `asTypeOf` (undefined :: HList '[a,b])
-
->>> let lr xs = asLen2 (applyAB (HMap HRead) xs)
->>> let ls xs = asLen2 (applyAB (HMap HShow) xs)
->>> let rl xs = applyAB (HMap HRead) (asLen2 xs)
->>> let sl xs = applyAB (HMap HShow) (asLen2 xs)
-
-
->>> :t lr
-lr
-  :: (Read y, Read y1) =>
-     HList ((':) * String ((':) * String ('[] *)))
-     -> HList ((':) * y ((':) * y1 ('[] *)))
-
->>> :t rl
-rl
-  :: (Read y, Read y1) =>
-     HList ((':) * String ((':) * String ('[] *)))
-     -> HList ((':) * y ((':) * y1 ('[] *)))
-
-
->>> :t ls
-ls
-  :: (Show y, Show y1) =>
-     HList ((':) * y ((':) * y1 ('[] *)))
-     -> HList ((':) * String ((':) * String ('[] *)))
-
->>> :t sl
-sl
-  :: (Show y, Show y1) =>
-     HList ((':) * y ((':) * y1 ('[] *)))
-     -> HList ((':) * String ((':) * String ('[] *)))
-
--}
-
-hMap f xs = applyAB (HMap f) xs
-
-newtype HMap f = HMap f
-
-instance (HMapCxt f as bs as' bs') => ApplyAB (HMap f) as bs where
-    applyAB (HMap f) = hMapAux f
-
-type HMapCxt f as bs as' bs' = (HMapAux f as' bs', as ~ HList as', bs ~ HList bs',
-    SameLength as' bs')
-
-
--- | Ensure two lists have the same length. We do case analysis on the
--- first one (hence the type must be known to the type checker).
--- In contrast, the second list may be a type variable.
-class SameLength' (es1 :: [k]) (es2 :: [m])
-instance (es2 ~ '[]) => SameLength' '[] es2
-instance (SameLength' xs ys, es2 ~ (y ': ys)) => SameLength' (x ': xs) es2
-
-{- | symmetrical version of 'SameLength''. Written as a class instead of
-
- > type SameLength a b = (SameLength' a b, SameLength' b a)
-
-since ghc expands type synonyms, but not classes (and it seems to have the same
-result)
-
--}
-class (SameLength' x y, SameLength' y x) =>
-        SameLength (x :: [k]) (y :: [m])
-instance (SameLength' x y, SameLength' y x) => SameLength x y
-
-
-
-class HMapAux f (l :: [*]) (r :: [*]) where
-  hMapAux :: SameLength l r => f -> HList l -> HList r
-
-instance HMapAux f '[] '[] where
-  hMapAux       _  _  = HNil
-
-instance (ApplyAB f e e', HMapAux f l l', SameLength l l')
-    => HMapAux f (e ': l) (e' ': l') where
-  hMapAux f (HCons x l)    = applyAB f x `HCons` hMapAux f l
-
-
-
-
--- --------------------------------------------------------------------------
-
--- **** alternative implementation
--- $note currently broken
-
-newtype MapCar f = MapCar f
-
--- | Same as 'hMap' only a different implementation.
-hMapMapCar :: (HFoldr (MapCar f) (HList '[]) l l') =>
-    f -> HList l -> l'
-hMapMapCar f = hFoldr (MapCar f) HNil
-
-instance ApplyAB f e e' => ApplyAB (MapCar f) (e,HList l) (HList (e' ': l)) where
-    applyAB (MapCar f) (e,l) = HCons (applyAB f e) l
-
-
--- --------------------------------------------------------------------------
-
--- *** @appEndo . mconcat . map Endo@
-{- |
-
->>> let xs = length .*. (+1) .*. (*2) .*. HNil
->>> hComposeList xs "abc"
-8
-
-
--}
-hComposeList
-  :: (HFoldr Comp (a -> a) l (t -> a)) => HList l -> t -> a
-hComposeList fs v0 = let r = hFoldr (undefined :: Comp) (\x -> x `asTypeOf` r) fs v0 in r
-
-
--- --------------------------------------------------------------------------
-
--- *** sequence
-{- |
-   A heterogeneous version of
-
-   > sequenceA :: (Applicative m) => [m a] -> m [a]
-
-   Only now we operate on heterogeneous lists, where different elements
-   may have different types 'a'.
-   In the argument list of monadic values (m a_i),
-   although a_i may differ, the monad 'm' must be the same for all
-   elements. That's why we needed "Data.HList.TypeCastGeneric2" (currently (~)).
-   The typechecker will complain
-   if we attempt to use hSequence on a HList of monadic values with different
-   monads.
-
-   The 'hSequence' problem was posed by Matthias Fischmann
-   in his message on the Haskell-Cafe list on Oct 8, 2006
-
-   <http://www.haskell.org/pipermail/haskell-cafe/2006-October/018708.html>
-
-   <http://www.haskell.org/pipermail/haskell-cafe/2006-October/018784.html>
- -}
-
-class (Applicative m, SameLength a b) => HSequence m a b | a -> b, m b -> a where
-    hSequence :: HList a -> m (HList b)
-{- ^
-
-[@Maybe@]
-
->>> hSequence $ Just (1 :: Integer) `HCons` (Just 'c') `HCons` HNil
-Just H[1, 'c']
-
->>> hSequence $  return 1 `HCons` Just  'c' `HCons` HNil
-Just H[1, 'c']
-
-
-[@List@]
-
->>> hSequence $ [1] `HCons` ['c'] `HCons` HNil
-[H[1, 'c']]
-
-
--}
-
-instance Applicative m => HSequence m '[] '[] where
-    hSequence _ = pure HNil
-
-instance (m1 ~ m, Applicative m, HSequence m as bs) =>
-    HSequence m (m1 a ': as) (a ': bs) where
-    hSequence (HCons a b) = liftA2 HCons a (hSequence b)
-
--- data ConsM = ConsM
--- consM = LiftA2 FHCons
-newtype LiftA2 f = LiftA2 f
-
-instance (ApplyAB f (x,y) z, mz ~ m z, mxy ~ (m x, m y), Applicative m) => ApplyAB (LiftA2 f) mxy mz where
-    applyAB (LiftA2 f) xy = liftA2 (curry (applyAB f)) `uncurry` xy
-
-{-
-instance (m1 ~ m, Applicative m) => ApplyAB ConsM (m a, m1 (HList l)) (m (HList (a ': l)))  where
-{-
-    type ApplyB ConsM (m a, m1 (HList l)) = Just (m (HList (a ': l)))
-    type ApplyA ConsM (m (HList (a ': l))) = Just (m a, m (HList l))
-    -}
-    applyAB _ (me,ml) = liftA2 HCons me ml
-    -}
-
-
--- **** alternative implementation
-
--- | 'hSequence2' is not recommended over 'hSequence' since it possibly doesn't
--- allow inferring argument types from the result types. Otherwise this version
--- should do exactly the same thing.
---
--- The DataKinds version needs a little help to find the type of the
--- return HNil, unlike the original version, which worked just fine as
---
---  > hSequence l = hFoldr ConsM (return HNil) l
-
-
-{-
-hSequence2 :: HSequence2 l f a => HList l -> f a
-hSequence2 l =
-    let rHNil = pure HNil `asTypeOf` (liftA undefined x)
-        x = hFoldr ConsM rHNil l
-    in x
-
-
--- | abbreviation for the constraint on 'hSequence2'
-type HSequence2 l f a = (Applicative f, HFoldr ConsM (f (HList ('[]))) l (f a))
--}
-
-
--- --------------------------------------------------------------------------
-
-
--- --------------------------------------------------------------------------
--- ** producing homogenous lists
-
--- *** map (no sequencing)
--- $mapOut This one we implement via hFoldr
-
-newtype Mapcar f = Mapcar f
-
-instance (l ~ [e'], ApplyAB f e e', el ~ (e,l)) => ApplyAB (Mapcar f) el l where
-    applyAB (Mapcar f) (e, l) = applyAB f e : l
-
--- A synonym for the complex constraint
-type HMapOut f l e = (HFoldr (Mapcar f) [e] l [e])
-
-hMapOut :: forall f e l. HMapOut f l e => f -> HList l -> [e]
-hMapOut f l = hFoldr (Mapcar f) ([] :: [e]) l
-
-
--- --------------------------------------------------------------------------
--- *** mapM
-
--- |
---
--- > mapM :: forall b m a. (Monad m) => (a -> m b) -> [a] -> m [b]
---
--- Likewise for mapM_.
---
--- See 'hSequence' if the result list should also be heterogenous.
-
-hMapM   :: (Monad m, HMapOut f l (m e)) => f -> HList l -> [m e]
-hMapM f =  hMapOut f
-
--- | GHC doesn't like its own type.
--- hMapM_  :: forall m a f e. (Monad m, HMapOut f a (m e)) => f -> a -> m ()
--- Without explicit type signature, it's Ok. Sigh.
--- Anyway, Hugs does insist on a better type. So we restrict as follows:
---
-hMapM_   :: (Monad m, HMapOut f l (m ())) => f -> HList l -> m ()
-hMapM_ f =  sequence_ .  disambiguate . hMapM f
- where
-  disambiguate :: [q ()] -> [q ()]
-  disambiguate =  id
-
-
-
-
-
--- --------------------------------------------------------------------------
--- * Type-level equality for lists ('HEq')
-
-instance HEq '[] '[]      True
-instance HEq '[] (e ': l) False
-instance HEq (e ': l) '[] False
-instance (HEq e1 e2 b1, HEq l1 l2 b2, br ~ HAnd b1 b2)
-      => HEq (e1 ': l1) (e2 ': l2) br
-
--- --------------------------------------------------------------------------
--- * Ensure a list to contain HNats only
--- | We do so constructively, converting the HList whose elements
--- are Proxy HNat to [HNat]. The latter kind is unpopulated and
--- is present only at the type level.
-
-type family HNats (l :: [*]) :: [HNat]
-type instance HNats '[] = '[]
-type instance HNats (Proxy n ': l) = n ': HNats l
-
-hNats :: HList l -> Proxy (HNats l)
-hNats = undefined
-
-
--- --------------------------------------------------------------------------
--- * Membership tests
-
--- | Check to see if an HList contains an element with a given type
--- This is a type-level only test
-
-class HMember (e1 :: k) (l :: [k]) (b :: Bool) | e1 l -> b
-instance HMember e1 '[] False
-instance (HEq e1 e b, HMember' b e1 l br) => HMember  e1 (e ': l) br
-class HMember' (b0 :: Bool) (e1 :: k) (l :: [k]) (b :: Bool) | b0 e1 l -> b
-instance HMember' True e1 l True
-instance (HMember e1 l br) => HMember' False e1 l br
-
--- The following is a similar type-only membership test
--- It uses the user-supplied curried type equality predicate pred
-type family HMemberP pred e1 (l :: [*]) :: Bool
-type instance HMemberP pred e1 '[] = False
---type instance HMemberP pred e1 (e ': l) = HMemberP' pred e1 l (ApplyR pred (e1,e))
-
-type family HMemberP' pred e1 (l :: [*]) pb :: Bool
-type instance HMemberP' pred e1 l (Proxy True) = True
-type instance HMemberP' pred e1 l (Proxy False) = HMemberP pred e1 l
- 
-
-hMember :: HMember e l b => Proxy e -> Proxy l -> Proxy b
-hMember = undefined
-
--- ** Another type-level membership test
---
--- | Check to see if an element e occurs in a list l
--- If not, return 'Nothing
--- If the element does occur, return 'Just l1
--- where l1 is a type-level list without e
-class HMemberM (e1 :: k) (l :: [k]) (r :: Maybe [k]) | e1 l -> r
-instance HMemberM e1 '[] 'Nothing
-instance (HEq e1 e b, HMemberM1 b e1 (e ': l) res)
-      =>  HMemberM e1 (e ': l) res
-
-class HMemberM1 (b::Bool) (e1 :: k) (l :: [k]) (r::Maybe [k]) | b e1 l -> r
-instance HMemberM1 True e1 (e ': l) ('Just l)
-instance (HMemberM e1 l r, HMemberM2 r e1 (e ': l) res)
-    => HMemberM1 False e1 (e ': l) res
-
-class HMemberM2 (b::Maybe [k]) (e1 :: k) (l :: [k]) (r::Maybe [k]) | b e1 l -> r
-instance HMemberM2 Nothing e1 l Nothing
-instance HMemberM2 (Just l1) e1 (e ': l) (Just (e ': l1))
-
--- --------------------------------------------------------------------------
-
--- * Staged equality for lists
--- $note removed. use Typeable instead
-
-{-
-instance HStagedEq (HList '[]) (HList '[])
- where
-  hStagedEq _ _ = True
-
-instance HStagedEq (HList '[]) (HList (e ': l))
- where
-  hStagedEq _ _ = False
-
-instance HStagedEq (HList (e ': l)) (HList '[])
- where
-  hStagedEq _ _ = False
-
-instance ( HEq e e' b
-         , HStagedEq (HList l) (HList l')
-         , HStagedEq' b e e'
-         )
-      =>   HStagedEq (HList (e ': l)) (HList (e' ': l'))
- where
-  hStagedEq (HCons e l) (HCons e' l') = (hStagedEq' b e e') && b'
-   where
-    b  = proxy :: Proxy b
-    b' = hStagedEq l l'
-
-class HStagedEq' (b :: Bool) e e'
- where
-  hStagedEq' :: Proxy b -> e -> e' -> Bool
-
-instance HStagedEq' False e e'
- where
-  hStagedEq' _ _ _ = False
-
-instance Eq e => HStagedEq' True e e
- where
-  hStagedEq' _ = (==)
--}
-
-{-
-
-
-
--- * Static set property based on HEq
-class HSet l
-instance HSet HNil
-instance (HMember e l HFalse, HSet l) => HSet (HCons e l)
--}
-
--- * Find an element in a set based on HEq
--- | It is a pure type-level operation
-class HFind (e :: k) (l :: [k]) (n :: HNat) | e l -> n
-
-instance (HEq e1 e2 b, HFind' b e1 l n) => HFind e1 (e2 ': l) n
-
-class HFind' (b::Bool) (e :: k) (l::[k]) (n::HNat) | b e l -> n
-instance HFind' True e l HZero
-instance HFind e l n => HFind' False e l (HSucc n)
-
-
-
--- ** Membership test based on type equality
-
--- | could be an associated type if HEq had one
-class HTMember e (l :: [*]) (b :: Bool) | e l -> b
-instance HTMember e '[] False
-instance (HEq e e' b, HTMember e l b', HOr b b' ~ b'')
-      =>  HTMember e (e' ': l) b''
-
-hTMember :: HTMember e l b => e -> HList l -> Proxy b
-hTMember _ _ = Proxy
-
-
--- * Intersection based on HTMember
-
-class HTIntersect l1 l2 l3 | l1 l2 -> l3
- where
-  -- | Like 'Data.List.intersect'
-  hTIntersect :: HList l1 -> HList l2 -> HList l3
-
-instance HTIntersect '[] l '[]
- where
-  hTIntersect _ _ = HNil
-
-instance ( HTMember h l1 b
-         , HTIntersectBool b h t l1 l2
-         )
-         => HTIntersect (h ': t) l1 l2
- where
-  hTIntersect (HCons h t) l1 = hTIntersectBool b h t l1
-   where
-    b = hTMember h l1
-
-class HTIntersectBool (b :: Bool) h t l1 l2 | b h t l1 -> l2
- where
- hTIntersectBool :: Proxy b -> h -> HList t -> HList l1 -> HList l2
-
-instance HTIntersect t l1 l2
-      => HTIntersectBool True h t l1 (h ': l2)
- where
-  hTIntersectBool _ h t l1 = HCons h (hTIntersect t l1)
-
-instance HTIntersect t l1 l2
-      => HTIntersectBool False h t l1 l2
- where
-  hTIntersectBool _ _ t l1 = hTIntersect t l1
-
-
--- * Turn a heterogeneous list into a homogeneous one
-
--- | Same as @hMapOut Id@
-class HList2List l e
- where
-  hList2List :: HList l -> [e]
-
-instance HList2List '[] e
- where
-  hList2List HNil = []
-
-instance HList2List l e
-      => HList2List (e ': l) e
- where
-  hList2List (HCons e l) = e:hList2List l
-
-
-
-
--- --------------------------------------------------------------------------
--- * With 'HMaybe'
-
--- ** Turn list in a list of justs
--- | the same as @map Just@
---
--- >>> toHJust (2 .*. 'a' .*. HNil)
--- H[HJust 2, HJust 'a']
---
--- >>> toHJust2 (2 .*. 'a' .*. HNil)
--- H[HJust 2, HJust 'a']
-
-class ToHJust l l' | l -> l', l' -> l
- where
-  toHJust :: HList l -> HList l'
-
-instance ToHJust '[] '[]
- where
-  toHJust HNil = HNil
-
-instance ToHJust l l' => ToHJust (e ': l) (HJust e ': l')
- where
-  toHJust (HCons e l) = HCons (HJust e) (toHJust l)
-
--- | alternative implementation. The Apply instance is in "Data.HList.FakePrelude".
--- A longer type could be inferred.
--- toHJust2 :: (HMap' (HJust ()) a b) => HList a -> HList b
-toHJust2 xs = hMap (HJust ()) xs
-
--- --------------------------------------------------------------------------
--- ** Extract justs from list of maybes
---
--- >>> let xs = 2 .*. 'a' .*. HNil
--- >>> fromHJust (toHJust xs) == xs
--- True
-
-class FromHJust l
- where
-  type FromHJustR l :: [*]
-  fromHJust :: HList l -> HList (FromHJustR l)
-
-instance FromHJust '[]
- where
-  type FromHJustR '[] = '[]
-  fromHJust HNil = HNil
-
-instance FromHJust l => FromHJust (HNothing ': l)
- where
-  type FromHJustR (HNothing ': l) = FromHJustR l
-  fromHJust (HCons _ l) = fromHJust l
-
-instance FromHJust l => FromHJust (HJust e ': l)
- where
-  type FromHJustR (HJust e ': l) = e ': FromHJustR l
-  fromHJust (HCons (HJust e) l) = HCons e (fromHJust l)
-
--- *** alternative implementation
-
--- | A longer type could be inferred.
--- fromHJust2 :: (HMap' HFromJust a b) => HList a -> HList b
-fromHJust2 xs = hMap HFromJust xs
-
-data HFromJust = HFromJust
-instance (hJustA ~ HJust a) => ApplyAB HFromJust hJustA a where
-    applyAB _ (HJust a) = a
-
-
--- --------------------------------------------------------------------------
--- * Annotated lists
-
-data HAddTag t = HAddTag t
-data HRmTag    = HRmTag
-
--- hAddTag :: HMap' (HAddTag t) l r => t -> HList l -> HList r
-hAddTag t l = hMap (HAddTag t) l
-
--- hRmTag ::  HMap HRmTag l => HList l -> HList (HMapR HRmTag l)
-hRmTag l    = hMap HRmTag l
-
-instance (et ~ (e,t)) => ApplyAB (HAddTag t) e et
- where
-  applyAB (HAddTag t) e = (e,t)
-
-
-instance (e' ~ e) => ApplyAB HRmTag (e,t) e'
- where
-  applyAB _ (e,_) = e
-
-
--- | Annotate list with a type-level Boolean
--- hFlag :: HMap' (HAddTag (Proxy True)) l r => HList l -> HList r
-hFlag l = hAddTag hTrue l
-
-
--- --------------------------------------------------------------------------
--- * Splitting by HTrue and HFalse
-
--- | Analogus to Data.List.'Data.List.partition' 'snd'
---
--- >>> hSplit $ (2,hTrue) .*. (3,hTrue) .*. (1,hFalse) .*. HNil
--- (H[2, 3],H[1])
---
--- it might make more sense to instead have @LVPair Bool e@
--- instead of @(e, Proxy Bool)@ since the former has the same
--- runtime representation as @e@
-
-class HSplit l
- where
-  type HSplitT l :: [*]
-  type HSplitF l :: [*]
-  hSplit :: HList l -> (HList (HSplitT l), HList (HSplitF l))
-
-instance HSplit '[]
- where
-  type HSplitT '[] = '[]
-  type HSplitF '[] = '[]
-  hSplit HNil = (HNil,HNil)
-
-instance HSplit l => HSplit ((e, Proxy True) ': l)
- where
-
-  type HSplitT ((e,Proxy True) ': l) = e ': HSplitT l
-  type HSplitF ((e,Proxy True) ': l) = HSplitF l
-
-  hSplit (HCons (e,_) l) = (HCons e l',l'')
-   where
-    (l',l'') = hSplit l
-
-instance HSplit l => HSplit ((e,Proxy False) ': l)
- where
-  type HSplitT ((e,Proxy False) ': l) = HSplitT l
-  type HSplitF ((e,Proxy False) ': l) = e ': HSplitF l
-
-  hSplit (HCons (e,_) l) = (l',HCons e l'')
-   where
-    (l',l'') = hSplit l
-
-{-
-
-Let expansion makes a difference to Hugs:
-
-HListPrelude> let x = (hFlag (HCons "1" HNil)) in hSplit x
-(HCons "1" HNil,HNil)
-HListPrelude> hSplit (hFlag (HCons "1" HNil))
-ERROR - Unresolved overloading
-*** Type       : HSplit (HCons ([Char],HTrue) HNil) a b => (a,b)
-*** Expression : hSplit (hFlag (HCons "1" HNil))
-
-
--}
+import Text.ParserCombinators.ReadP
+import Data.List
+
+import LensDefs
+
+import Data.Array (Ix)
+
+-- --------------------------------------------------------------------------
+-- * Heterogeneous type sequences
+{- $note
+
+There are three sensible ways to define HLists:
+
+@
+data HList (l::[*]) where
+    HNil  :: HList '[]
+    HCons :: e -> HList l -> HList (e ': l)
+@
+
+This ensures that sequences can only be formed with Nil
+and Cons. The argument to HList is a promoted lists (kind @[*]@),
+which has a more attractive syntax.
+
+
+Earlier versions of HList used an algebraic data type:
+
+@
+data HCons a b = HCons a b
+data HNil = HNil
+@
+
+Disadvantages:
+
+* values with types like @HCons Int Double@ to be created,
+  which are nonsense to the functions in HList
+
+* some recursive functions do not need a class with the GADT. For example:
+
+  @
+    hInit :: HListGADT (x ': xs) -> HListGADT (HInit (x ': xs))
+    hInit (HCons x xs@(HCons _ _)) = HCons x (hInit xs)
+    hInit (HCons _ HNil) = HNil
+
+    type family HInit (xs :: [k]) :: [k]
+  @
+
+  but without the GADT, 'hInit' is written as in a class,
+  which complicates inferred types
+
+
+Advantages
+
+* lazy pattern matches are allowed, so lazy pattern matching
+  on a value @undefined :: HList [a,b,c]@ can create the
+  spine of the list. 'hProxies' avoids the use of 'undefined',
+  but a slightly more complicated class context has to be written
+  or inferred.
+
+* type inference is better if you want to directly pattern match
+<http://stackoverflow.com/questions/19077037/is-there-any-deeper-type-theoretic-reason-ghc-cant-infer-this-type see stackoverflow post here>
+
+* better pattern exhaustiveness checking (as of ghc-7.8)
+
+* standalone deriving works
+
+* Data.Coerce.coerce works because the parameters have role representational,
+  not nominal as they are for the GADT and data family. Probably the GADT/type
+  family actually do have a representational role:
+  <http://stackoverflow.com/questions/24222552/does-this-gadt-actually-have-type-role-representational>
+
+
+
+The data family version (currently used) gives the same type constructor
+@HList :: [*] -> *@ as the GADT, while pattern matching behaves
+like the algebraic data type. Furthermore, nonsense values like
+@HCons 1 2 :: HCons Int Int@ cannot be written with the data family.
+
+-}
+
+
+data family HList (l::[*])
+
+data instance HList '[] = HNil
+data instance HList (x ': xs) = x `HCons` HList xs
+
+deriving instance Eq (HList '[])
+deriving instance (Eq x, Eq (HList xs)) => Eq (HList (x ': xs))
+
+deriving instance Ord (HList '[])
+deriving instance (Ord x, Ord (HList xs)) => Ord (HList (x ': xs))
+
+deriving instance Ix (HList '[])
+deriving instance (Ix x, Ix (HList xs)) => Ix (HList (x ': xs))
+
+deriving instance Bounded (HList '[])
+deriving instance (Bounded x, Bounded (HList xs)) => Bounded (HList (x ': xs))
+
+
+-- Enum cannot be derived
+
+
+-- | creates a HList of Proxies
+
+class HProxiesFD (xs :: [*]) pxs | pxs -> xs -- DropProxy pxs ~ xs
+                      , xs -> pxs -- AddProxy xs ~ pxs
+      where hProxies :: HList pxs
+
+{- Ideally we could write:
+
+> class DropProxy (AddProxy xs) ~ xs => HProxies xs where
+>     hProxies :: HList (AddProxy xs)
+
+See https://ghc.haskell.org/trac/ghc/ticket/10009 -}
+type HProxies xs = HProxiesFD xs (AddProxy xs)
+
+
+{- | Add 'Proxy' to a type
+
+>>> let x = undefined :: HList (AddProxy [Char,Int])
+>>> :t x
+x :: HList '[Proxy Char, Proxy Int]
+
+
+-}
+type family AddProxy (xs :: k) :: k
+type instance AddProxy '[] = '[]
+type instance AddProxy (x ': xs) = AddProxy x ': AddProxy xs
+type instance AddProxy (x :: *) = Proxy x
+
+-- | inverse of 'AddProxy'
+type family DropProxy (xs :: k) :: k
+type instance DropProxy (x ': xs) = DropProxy x ': DropProxy xs
+type instance DropProxy '[] = '[]
+type instance DropProxy (Proxy x) = x
+
+instance HProxiesFD '[] '[] where
+    hProxies = HNil
+
+instance (HProxiesFD xs pxs) => HProxiesFD (x ': xs) (Proxy x ': pxs) where
+    hProxies = Proxy `HCons` hProxies
+
+
+
+instance Show (HList '[]) where
+    show _ = "H[]"
+
+instance (Show e, Show (HList l)) => Show (HList (e ': l)) where
+    show (HCons x l) = let 'H':'[':s = show l
+                       in "H[" ++ show x ++
+                                  (if s == "]" then s else "," ++ s)
+
+
+instance Read (HList '[]) where
+    readsPrec _ str = case stripPrefix "H[]" str of
+                        Nothing -> []
+                        Just rest -> [(HNil, rest)]
+
+instance
+   (HProxies l, Read e,
+    HSequence ReadP (ReadP e ': readP_l) (e ': l),
+    HMapCxt HList ReadElement (AddProxy l) readP_l)  =>
+      Read (HList (e ': l)) where
+  readsPrec _ = readP_to_S $ do
+    _ <- string "H["
+    l <- return (hProxies :: HList (AddProxy l))
+    let parsers = readS_to_P reads `HCons` hMap ReadElement l
+    hlist <- hSequence parsers
+    _ <- string "]"
+    return hlist
+
+
+-- similar to ReadComponent used to define instance Read Record
+data ReadElement = ReadElement
+
+instance (y ~ ReadP x, Read x) => ApplyAB ReadElement (Proxy x) y where
+    applyAB ReadElement _ = do
+      _ <- string ","
+      readS_to_P reads
+
+
+infixr 2 `HCons`
+
+
+-- --------------------------------------------------------------------------
+-- * Basic list functions
+
+-- | 'head'
+hHead :: HList (e ': l) -> e
+hHead (HCons x _) = x
+
+-- | 'tail'
+hTail :: HList (e ': l) -> HList l
+hTail (HCons _ l) = l
+
+-- | 'last'
+hLast xs = hHead (hReverse_ xs)
+
+
+class HInit xs where
+    type HInitR xs :: [*]
+    hInit :: HList xs -> HList (HInitR xs)
+
+instance HInit '[x] where
+    type HInitR '[x] = '[]
+    hInit _ = HNil
+
+instance HInit (b ': c) => HInit (a ': b ': c) where
+    type HInitR (a ': b ': c) = a ': HInitR (b ': c)
+    hInit (a `HCons` bc) = a `HCons` hInit bc
+
+
+-- | Length, but see 'HLengthEq' instead
+type family HLength (x :: [k]) :: HNat
+type instance HLength '[] = HZero
+type instance HLength (x ': xs) = HSucc (HLength xs)
+
+hLength   :: HLengthEq l n => HList l -> Proxy n
+hLength _ =  Proxy
+
+-- ** Append
+instance HExtend e (HList l) where
+  type HExtendR e (HList l) = HList (e ': l)
+  (.*.) = HCons
+
+instance HAppendList l1 l2 => HAppend (HList l1) (HList l2) where
+  hAppend = hAppendList
+type instance HAppendR (HList l1) (HList l2) = HList (HAppendListR l1 l2)
+
+type family HAppendListR (l1 :: [k]) (l2 :: [k]) :: [k]
+type instance HAppendListR '[] l = l
+type instance HAppendListR (e ': l) l' = e ': HAppendListR l l'
+
+
+class HAppendList l1 l2 where
+  -- | the same as 'hAppend'
+  hAppendList :: HList l1 -> HList l2 -> HList (HAppendListR l1 l2)
+
+instance HAppendList '[] l2 where
+  hAppendList HNil l = l
+
+instance HAppendList l l' => HAppendList (x ': l) l' where
+  hAppendList (HCons x l) l' = HCons x (hAppendList l l')
+
+-- --------------------------------------------------------------------------
+
+-- ** Alternative append
+
+
+-- | 'hAppend'' below is implemented using the same idea
+append' :: [a] -> [a] -> [a]
+append' l l' = foldr (:) l' l
+
+-- | Alternative implementation of 'hAppend'. Demonstrates 'HFoldr'
+hAppend' :: (HFoldr FHCons v l r) => HList l -> v -> r
+hAppend' l l' = hFoldr FHCons l' l
+
+data FHCons = FHCons
+
+instance ( x ~ (e,HList l), y ~ (HList (e ': l))) => ApplyAB FHCons x y  where
+    applyAB _ (e,l) = HCons e l
+
+
+-- ** Historical append
+
+{- $
+
+The original HList code is included below. In both cases
+we had to program the algorithm twice, at the term and the type levels.
+
+[@The class HAppend@]
+
+> class HAppend l l' l'' | l l' -> l''
+>  where
+>   hAppend :: l -> l' -> l''
+>
+
+[@The instance following the normal append@]
+
+> instance HList l => HAppend HNil l l
+>  where
+>   hAppend HNil l = l
+>
+> instance (HList l, HAppend l l' l'')
+>       => HAppend (HCons x l) l' (HCons x l'')
+>  where
+>   hAppend (HCons x l) l' = HCons x (hAppend l l')
+
+-}
+
+-- --------------------------------------------------------------------------
+-- * Reversing HLists
+
+-- Append the reversed l1 to l2
+type family HRevAppR (l1 :: [k]) (l2 :: [k]) :: [k]
+type instance HRevAppR '[] l = l
+type instance HRevAppR (e ': l) l' = HRevAppR l (e ': l')
+
+
+class HRevApp l1 l2 l3 | l1 l2 -> l3 where
+    hRevApp :: HList l1 -> HList l2 -> HList l3
+
+instance HRevApp '[] l2 l2 where
+    hRevApp _ l = l
+
+instance HRevApp l (x ': l') z => HRevApp (x ': l) l' z where
+    hRevApp (HCons x l) l' = hRevApp l (HCons x l')
+
+
+
+class HReverse xs sx | xs -> sx, sx -> xs where
+    hReverse :: HList xs -> HList sx
+
+instance (HRevApp xs '[] sx,
+          HRevApp sx '[] xs) => HReverse xs sx where
+    hReverse l = hRevApp l HNil
+
+-- | a version of 'hReverse' that does not allow the type
+-- information to flow backwards
+hReverse_ l = hRevApp l HNil
+
+-- --------------------------------------------------------------------------
+
+--
+-- * A nicer notation for lists
+--
+
+
+-- | List termination
+hEnd :: HList l -> HList l
+hEnd = id
+
+{- ^
+   Note:
+
+        [@x :: HList a@] means: @forall a. x :: HList a@
+
+        [@hEnd x@] means: @exists a. x :: HList a@
+-}
+
+
+-- |  Building lists
+
+hBuild :: (HBuild' '[] r) => r
+hBuild =  hBuild' HNil
+
+class HBuild' l r where
+    hBuild' :: HList l -> r
+
+instance HReverse l l'
+      => HBuild' l (HList l') where
+  hBuild' l = hReverse l
+
+instance HBuild' (a ': l) r
+      => HBuild' l (a->r) where
+  hBuild' l x = hBuild' (HCons x l)
+
+-- ** examples
+{- $examplesNote
+
+The classes above allow the third (shortest) way to make a list
+(containing a,b,c) in this case
+
+> list = a `HCons` b `HCons` c `HCons` HNil
+> list = a .*. b .*. c .*. HNil
+> list = hEnd $ hBuild a b c
+
+>>> let x = hBuild True in hEnd x
+H[True]
+
+>>> let x = hBuild True 'a' in hEnd x
+H[True,'a']
+
+>>> let x = hBuild True 'a' "ok" in hEnd x
+H[True,'a',"ok"]
+
+hBuild can also produce a Record, such that
+
+> hBuild x y ^. from unlabeled
+
+can also be produced using
+
+@
+'hEndR' $ hBuild x y
+@
+
+-}
+
+-- *** historical
+{- $hbuild the show instance has since changed, but these uses of
+'hBuild'/'hEnd' still work
+
+> HList> let x = hBuild True in hEnd x
+> HCons True HNil
+
+> HList> let x = hBuild True 'a' in hEnd x
+> HCons True (HCons 'a' HNil)
+
+> HList> let x = hBuild True 'a' "ok" in hEnd x
+> HCons True (HCons 'a' (HCons "ok" HNil))
+
+> HList> hEnd (hBuild (Key 42) (Name "Angus") Cow (Price 75.5))
+> HCons (Key 42) (HCons (Name "Angus") (HCons Cow (HCons (Price 75.5) HNil)))
+
+> HList> hEnd (hBuild (Key 42) (Name "Angus") Cow (Price 75.5)) == angus
+> True
+
+-}
+
+-- --------------------------------------------------------------------------
+
+-- * folds
+-- ** foldr
+-- $foldNote  Consume a heterogenous list.
+
+
+class HFoldr f v (l :: [*]) r where
+    hFoldr :: f -> v -> HList l -> r
+
+instance (v ~ v') => HFoldr f v '[] v' where
+    hFoldr       _ v _   = v
+
+-- | uses 'ApplyAB' not 'Apply'
+instance (ApplyAB f (e, r) r', HFoldr f v l r)
+    => HFoldr f v (e ': l) r' where
+    hFoldr f v (HCons x l)    = applyAB f (x, hFoldr f v l :: r)
+
+
+class HScanr f z ls rs where
+    hScanr :: f -> z -> HList ls -> HList rs
+
+instance lz ~ '[z] => HScanr f z '[] lz where
+    hScanr _ z _ = HCons z HNil
+
+instance (ApplyAB f (x,r) s, HScanr f z xs (r ': rs),
+          srrs ~ (s ': r ': rs)) => HScanr f z (x ': xs) srrs where
+    hScanr f z (HCons x xs) =
+        case hScanr f z xs :: HList (r ': rs) of
+            HCons r rs -> (applyAB f (x,r) :: s) `HCons` r `HCons` rs
+
+class HFoldr1 f (l :: [*]) r where
+    hFoldr1 :: f -> HList l -> r
+
+instance (v ~ v') => HFoldr1 f '[v] v' where
+    hFoldr1      _ (HCons v _)  = v
+
+-- | uses 'ApplyAB' not 'Apply'
+instance (ApplyAB f (e, r) r', HFoldr1 f (e' ': l) r)
+    => HFoldr1 f (e ': e' ': l) r' where
+    hFoldr1 f (HCons x l)    = applyAB f (x, hFoldr1 f l :: r)
+
+
+-- ** foldl
+
+{- | like 'foldl'
+
+
+>>> hFoldl (uncurry $ flip (:)) [] (1 `HCons` 2 `HCons` HNil)
+[2,1]
+
+
+-}
+class HFoldl f (z :: *) xs (r :: *) where
+    hFoldl :: f -> z -> HList xs -> r
+
+instance forall f z z' r x zx xs. (zx ~ (z,x), ApplyAB f zx z', HFoldl f z' xs r)
+    => HFoldl f z (x ': xs) r where
+    hFoldl f z (x `HCons` xs) = hFoldl f (applyAB f (z,x) :: z') xs
+
+instance (z ~ z') => HFoldl f z '[] z' where
+    hFoldl _ z _ = z
+
+
+
+
+
+-- * unfolds
+
+-- ** unfold
+-- $unfoldNote Produce a heterogenous list. Uses the more limited
+-- 'Apply' instead of 'App' since that's all that is needed for uses of this
+-- function downstream. Those could in principle be re-written.
+
+-- hUnfold :: (Apply p s, HUnfold' p s) => p -> s -> HList (HUnfold p s)
+hUnfold p s = hUnfold' p (apply p s)
+
+type HUnfold p s = HUnfoldR p (ApplyR p s)
+
+type family HUnfoldR p res :: [*]
+type instance HUnfoldR p HNothing = '[]
+type instance HUnfoldR p (HJust (e,s)) = e ': HUnfoldR p (ApplyR p s)
+
+type HUnfold' p res = HUnfoldFD p (ApplyR p res) (HUnfold p res)
+
+class HUnfoldFD p res z | p res -> z where
+    hUnfold' :: p -> res -> HList z
+
+instance HUnfoldFD p HNothing '[] where
+    hUnfold' _ _ = HNil
+
+instance (Apply p s, HUnfoldFD p (ApplyR p s) z) => HUnfoldFD p (HJust (e,s)) (e ': z) where
+    hUnfold' p (HJust (e,s)) = HCons e (hUnfold p s)
+
+
+-- ** replicate
+
+{- |
+
+Sometimes the result type can fix the type of the
+first argument:
+
+>>> hReplicate Proxy () :: HList '[ (), (), () ]
+H[(),(),()]
+
+However, with HReplicate all elements must have the same type, so it may be
+easier to use 'HList2List':
+
+>>> list2HList (repeat 3) :: Maybe (HList [Int, Int, Int])
+Just H[3,3,3]
+
+-}
+class HLengthEq es n => HReplicateFD (n :: HNat) e es
+          | n e -> es, es -> n where
+    hReplicate :: Proxy n -> e -> HList es
+
+instance HReplicateFD HZero e '[] where
+    hReplicate _ _ = HNil
+
+instance (HReplicateFD n e es, e ~ e') => HReplicateFD (HSucc n) e (e' ': es) where
+    hReplicate n e = e `HCons` hReplicate (hPred n) e
+
+type HReplicate n e = HReplicateFD n e (HReplicateR n e)
+
+-- | would be associated with 'HReplicate' except we want
+-- it to work with `e` of any kind, not just `*` that you can
+-- put into a HList. An \"inverse\" of 'HLength'
+type family HReplicateR (n :: HNat) (e :: k) :: [k]
+type instance HReplicateR HZero e = '[]
+type instance HReplicateR (HSucc n) e = e ': HReplicateR n e
+
+{- | HReplicate produces lists that can be converted to ordinary
+lists
+
+>>> let two = hSucc (hSucc hZero)
+>>> let f = Fun' fromInteger :: Fun' Num Integer
+
+>>> :t applyAB f
+applyAB f :: Num b => Integer -> b
+
+>>> hReplicateF two f 3
+H[3,3]
+
+>>> hReplicateF Proxy f 3 :: HList [Int, Double, Integer]
+H[3,3.0,3]
+
+-}
+class HLengthEq r n => HReplicateF (n :: HNat) f z r | r -> n where
+    hReplicateF :: HLengthEq r n => Proxy n -> f -> z -> HList r
+
+instance HReplicateF HZero f z '[] where
+    hReplicateF _ _ _ = HNil
+
+instance (ApplyAB f z fz,
+          HReplicateF n f z r')
+  => HReplicateF (HSucc n) f z (fz ': r') where
+    hReplicateF n f z = applyAB f z `HCons` hReplicateF (hPred n) f z
+
+-- ** iterate
+{- |
+
+This function behaves like 'iterate', with an extra
+argument to help figure out the result length
+
+>>> let three = hSucc (hSucc (hSucc hZero))
+>>> let f = Fun Just :: Fun '() Maybe
+
+>>> :t applyAB f
+applyAB f :: a -> Maybe a
+
+f is applied to different types:
+>>> hIterate three f ()
+H[(),Just (),Just (Just ())]
+
+It is also possible to specify the length later on,
+as done with Prelude.'iterate'
+
+>>> let take3 x | _ <- hLength x `asTypeOf` three = x
+>>> take3 $ hIterate Proxy f ()
+H[(),Just (),Just (Just ())]
+
+-}
+class HLengthEq r n => HIterate n f z r where
+    hIterate :: HLengthEq r n => Proxy n -> f -> z -> HList r
+
+instance HIterate HZero f z '[] where
+    hIterate _ _ _ = HNil
+
+instance (ApplyAB f z z',
+      HIterate n f z' r',
+      z ~ z_)
+     => HIterate (HSucc n) f z (z_ ': r') where
+    hIterate n f z = z `HCons` hIterate (hPred n) f (applyAB f z :: z')
+
+-- * concat
+
+{- |
+
+Like 'concat' but for HLists of HLists.
+
+Works in ghci... puzzling as what is different in doctest (it isn't
+@-XExtendedDefaultRules@)
+
+> hConcat $ hBuild (hBuild 1 2 3) (hBuild 'a' "abc")
+
+H[1, 2, 3, 'a', "abc"]
+
+
+-}
+class HConcat (a :: [*]) where
+    type HConcatR a :: [*]
+    hConcat :: HList a -> HList (HConcatR a)
+
+instance HConcat '[] where
+    type HConcatR '[] = '[]
+    hConcat _ = HNil
+
+instance (x ~ HList t, HConcat xs, HAppendList t (HConcatR xs)) => HConcat (x ': xs) where
+    type HConcatR (x ': xs) = HAppendListR (UnHList x) (HConcatR xs)
+    hConcat (x `HCons` xs) = x `hAppendList` hConcat xs
+
+
+type family UnHList a :: [*]
+type instance UnHList (HList a) = a
+
+-- --------------------------------------------------------------------------
+-- * traversing HLists
+
+-- ** producing HList
+-- *** map
+-- $mapNote It could be implemented with 'hFoldr', as we show further below
+
+{- | hMap is written such that the length of the result list
+can be determined from the length of the argument list (and
+the other way around). Similarly, the type of the elements
+of the list is propagated in both directions too.
+
+>>> :set -XNoMonomorphismRestriction
+>>> let xs = 1 .*. 'c' .*. HNil
+>>> :t hMap (HJust ()) xs
+hMap (HJust ()) xs :: Num y => HList '[HJust y, HJust Char]
+
+
+These 4 examples show that the constraint on the length (2 in this case)
+can be applied before or after the 'hMap'. That inference is independent of the
+direction that type information is propagated for the individual elements.
+
+
+>>> let asLen2 xs = xs `asTypeOf` (undefined :: HList '[a,b])
+
+>>> let lr xs = asLen2 (applyAB (HMap HRead) xs)
+>>> let ls xs = asLen2 (applyAB (HMap HShow) xs)
+>>> let rl xs = applyAB (HMap HRead) (asLen2 xs)
+>>> let sl xs = applyAB (HMap HShow) (asLen2 xs)
+
+
+>>> :t lr
+lr
+  :: (Read ..., Read ...) => HList '[String, String] -> HList '[..., ...]
+
+>>> :t rl
+rl
+  :: (Read ..., Read ...) => HList '[String, String] -> HList '[..., ...]
+
+
+>>> :t ls
+ls
+  :: (Show ..., Show ...) => HList '[..., ...] -> HList '[String, String]
+
+>>> :t sl
+sl
+  :: (Show ..., Show ...) => HList '[..., ...] -> HList '[String, String]
+
+-}
+
+newtype HMap f = HMap f
+
+hMap f xs = applyAB (HMap f) xs
+
+instance (HMapCxt r f a b, as ~ r a, bs ~ r b)
+    => ApplyAB (HMap f) as bs where
+    applyAB (HMap f) = hMapAux f
+
+
+-- | hMap constrained to HList
+hMapL f xs = applyAB (HMapL f) xs
+
+newtype HMapL f = HMapL f
+
+instance (HMapCxt HList f a b, as ~ HList a, bs ~ HList b) => ApplyAB (HMapL f) as bs where
+    applyAB (HMapL f) = hMapAux f
+
+
+class (SameLength a b, HMapAux r f a b) => HMapCxt r f a b
+
+instance (SameLength a b, HMapAux r f a b) => HMapCxt r f a b
+
+
+
+class HMapAux (r :: [*] -> *) f (x :: [*]) (y :: [*]) where
+  hMapAux :: SameLength x y => f -> r x -> r y
+
+instance HMapAux HList f '[] '[] where
+  hMapAux       _  _  = HNil
+
+instance (ApplyAB f e e', HMapAux HList f l l', SameLength l l')
+    => HMapAux HList f (e ': l) (e' ': l') where
+  hMapAux f (HCons x l)    = applyAB f x `HCons` hMapAux f l
+
+
+
+
+-- --------------------------------------------------------------------------
+
+-- **** alternative implementation
+-- $note currently broken
+
+newtype MapCar f = MapCar f
+
+-- | Same as 'hMap' only a different implementation.
+hMapMapCar :: (HFoldr (MapCar f) (HList '[]) l l') =>
+    f -> HList l -> l'
+hMapMapCar f = hFoldr (MapCar f) HNil
+
+instance ApplyAB f e e' => ApplyAB (MapCar f) (e,HList l) (HList (e' ': l)) where
+    applyAB (MapCar f) (e,l) = HCons (applyAB f e) l
+
+
+-- --------------------------------------------------------------------------
+
+-- *** @appEndo . mconcat . map Endo@
+{- |
+
+>>> let xs = length .*. (+1) .*. (*2) .*. HNil
+>>> hComposeList xs "abc"
+8
+
+
+-}
+hComposeList
+  :: (HFoldr Comp (a -> a) l (t -> a)) => HList l -> t -> a
+hComposeList fs v0 = let r = hFoldr (Comp :: Comp) (\x -> x `asTypeOf` r) fs v0 in r
+
+
+-- --------------------------------------------------------------------------
+
+-- *** sequence
+{- |
+   A heterogeneous version of
+
+   > sequenceA :: (Applicative m) => [m a] -> m [a]
+
+   Only now we operate on heterogeneous lists, where different elements
+   may have different types 'a'.
+   In the argument list of monadic values (m a_i),
+   although a_i may differ, the monad 'm' must be the same for all
+   elements. That's why we needed "Data.HList.TypeCastGeneric2" (currently (~)).
+   The typechecker will complain
+   if we attempt to use hSequence on a HList of monadic values with different
+   monads.
+
+   The 'hSequence' problem was posed by Matthias Fischmann
+   in his message on the Haskell-Cafe list on Oct 8, 2006
+
+   <http://www.haskell.org/pipermail/haskell-cafe/2006-October/018708.html>
+
+   <http://www.haskell.org/pipermail/haskell-cafe/2006-October/018784.html>
+ -}
+
+class (Applicative m, SameLength a b) => HSequence m a b | a -> b, m b -> a where
+    hSequence :: HList a -> m (HList b)
+{- ^
+
+[@Maybe@]
+
+>>> hSequence $ Just (1 :: Integer) `HCons` (Just 'c') `HCons` HNil
+Just H[1,'c']
+
+>>> hSequence $  return 1 `HCons` Just  'c' `HCons` HNil
+Just H[1,'c']
+
+
+[@List@]
+
+>>> hSequence $ [1] `HCons` ['c'] `HCons` HNil
+[H[1,'c']]
+
+
+-}
+
+instance Applicative m => HSequence m '[] '[] where
+    hSequence _ = pure HNil
+
+instance (m1 ~ m, Applicative m, HSequence m as bs) =>
+    HSequence m (m1 a ': as) (a ': bs) where
+    hSequence (HCons a b) = liftA2 HCons a (hSequence b)
+
+-- **** alternative implementation
+
+-- | 'hSequence2' is not recommended over 'hSequence' since it possibly doesn't
+-- allow inferring argument types from the result types. Otherwise this version
+-- should do exactly the same thing.
+--
+-- The DataKinds version needs a little help to find the type of the
+-- return HNil, unlike the original version, which worked just fine as
+--
+--  > hSequence l = hFoldr ConsM (return HNil) l
+
+hSequence2 l =
+    let rHNil = pure HNil `asTypeOf` (fmap undefined x)
+        x = hFoldr (LiftA2 FHCons) rHNil l
+    in x
+
+
+
+-- --------------------------------------------------------------------------
+
+
+-- --------------------------------------------------------------------------
+-- ** producing homogenous lists
+
+-- *** map (no sequencing)
+-- $mapOut This one we implement via hFoldr
+
+newtype Mapcar f = Mapcar f
+
+instance (l ~ [e'], ApplyAB f e e', el ~ (e,l)) => ApplyAB (Mapcar f) el l where
+    applyAB (Mapcar f) (e, l) = applyAB f e : l
+
+-- A synonym for the complex constraint
+type HMapOut f l e = (HFoldr (Mapcar f) [e] l [e])
+
+-- | compare @hMapOut f@ with @'hList2List' . 'hMap' f@
+hMapOut :: forall f e l. HMapOut f l e => f -> HList l -> [e]
+hMapOut f l = hFoldr (Mapcar f) ([] :: [e]) l
+
+
+-- --------------------------------------------------------------------------
+-- *** mapM
+
+-- |
+--
+-- > mapM :: forall b m a. (Monad m) => (a -> m b) -> [a] -> m [b]
+--
+-- Likewise for mapM_.
+--
+-- See 'hSequence' if the result list should also be heterogenous.
+
+hMapM   :: (Monad m, HMapOut f l (m e)) => f -> HList l -> [m e]
+hMapM f =  hMapOut f
+
+-- | GHC doesn't like its own type.
+-- hMapM_  :: forall m a f e. (Monad m, HMapOut f a (m e)) => f -> a -> m ()
+-- Without explicit type signature, it's Ok. Sigh.
+-- Anyway, Hugs does insist on a better type. So we restrict as follows:
+--
+hMapM_   :: (Monad m, HMapOut f l (m ())) => f -> HList l -> m ()
+hMapM_ f =  sequence_ .  disambiguate . hMapM f
+ where
+  disambiguate :: [q ()] -> [q ()]
+  disambiguate =  id
+
+
+
+
+
+-- --------------------------------------------------------------------------
+-- * Ensure a list to contain HNats only
+-- | We do so constructively, converting the HList whose elements
+-- are Proxy HNat to [HNat]. The latter kind is unpopulated and
+-- is present only at the type level.
+
+type family HNats (l :: [*]) :: [HNat]
+type instance HNats '[] = '[]
+type instance HNats (Proxy n ': l) = n ': HNats l
+
+hNats :: HList l -> Proxy (HNats l)
+hNats _ = Proxy
+
+
+-- --------------------------------------------------------------------------
+-- * Membership tests
+
+-- | Check to see if an HList contains an element with a given type
+-- This is a type-level only test
+
+class HMember (e1 :: k) (l :: [k]) (b :: Bool) | e1 l -> b
+instance HMember e1 '[] False
+instance (HEq e1 e b, HMember' b e1 l br) => HMember  e1 (e ': l) br
+class HMember' (b0 :: Bool) (e1 :: k) (l :: [k]) (b :: Bool) | b0 e1 l -> b
+instance HMember' True e1 l True
+instance (HMember e1 l br) => HMember' False e1 l br
+
+-- | The following is a similar type-only membership test
+-- It uses the user-supplied curried type equality predicate pred
+type family HMemberP pred e1 (l :: [*]) :: Bool
+type instance HMemberP pred e1 '[] = False
+type instance HMemberP pred e1 (e ': l) = HMemberP' pred e1 l (ApplyR pred (e1,e))
+
+type family HMemberP' pred e1 (l :: [*]) pb :: Bool
+type instance HMemberP' pred e1 l (Proxy True) = True
+type instance HMemberP' pred e1 l (Proxy False) = HMemberP pred e1 l
+
+
+hMember :: HMember e l b => Proxy e -> Proxy l -> Proxy b
+hMember _ _ = Proxy
+
+-- ** Another type-level membership test
+--
+-- | Check to see if an element e occurs in a list l
+-- If not, return 'Nothing
+-- If the element does occur, return 'Just l1
+-- where l1 is a type-level list without e
+class HMemberM (e1 :: k) (l :: [k]) (r :: Maybe [k]) | e1 l -> r
+instance HMemberM e1 '[] 'Nothing
+instance (HEq e1 e b, HMemberM1 b e1 (e ': l) res)
+      =>  HMemberM e1 (e ': l) res
+
+class HMemberM1 (b::Bool) (e1 :: k) (l :: [k]) (r::Maybe [k]) | b e1 l -> r
+instance HMemberM1 True e1 (e ': l) ('Just l)
+instance (HMemberM e1 l r, HMemberM2 r e1 (e ': l) res)
+    => HMemberM1 False e1 (e ': l) res
+
+class HMemberM2 (b::Maybe [k]) (e1 :: k) (l :: [k]) (r::Maybe [k]) | b e1 l -> r
+instance HMemberM2 Nothing e1 l Nothing
+instance HMemberM2 (Just l1) e1 (e ': l) (Just (e ': l1))
+
+-- --------------------------------------------------------------------------
+
+-- * Staged equality for lists
+-- $note removed. use Typeable instead
+
+
+{-
+-- * Static set property based on HEq
+class HSet l
+instance HSet HNil
+instance (HMember e l HFalse, HSet l) => HSet (HCons e l)
+-}
+
+-- * Find an element in a set based on HEq
+-- | It is a pure type-level operation
+class HFind1 e l n => HFind (e :: k) (l :: [k]) (n :: HNat) | e l -> n
+instance HFind1 e l n => HFind e l n
+
+class HFind1 (e :: k) (l :: [k]) (n :: HNat) | e l -> n
+
+instance (HEq e1 e2 b, HFind2 b e1 l n) => HFind1 e1 (e2 ': l) n
+instance Fail (FieldNotFound e1) => HFind1 e1 '[] HZero
+
+class HFind2 (b::Bool) (e :: k) (l::[k]) (n:: HNat) | b e l -> n
+instance HFind2 True e l HZero
+instance HFind1 e l n => HFind2 False e l (HSucc n)
+
+
+
+-- ** Membership test based on type equality
+
+-- | could be an associated type if HEq had one
+class HTMember e (l :: [*]) (b :: Bool) | e l -> b
+instance HTMember e '[] False
+instance (HEq e e' b, HTMember e l b', HOr b b' ~ b'')
+      =>  HTMember e (e' ': l) b''
+
+hTMember :: HTMember e l b => e -> HList l -> Proxy b
+hTMember _ _ = Proxy
+
+
+-- * Intersection based on HTMember
+
+class HTIntersect l1 l2 l3 | l1 l2 -> l3
+ where
+  -- | Like 'Data.List.intersect'
+  hTIntersect :: HList l1 -> HList l2 -> HList l3
+
+instance HTIntersect '[] l '[]
+ where
+  hTIntersect _ _ = HNil
+
+instance ( HTMember h l1 b
+         , HTIntersectBool b h t l1 l2
+         )
+         => HTIntersect (h ': t) l1 l2
+ where
+  hTIntersect (HCons h t) l1 = hTIntersectBool b h t l1
+   where
+    b = hTMember h l1
+
+class HTIntersectBool (b :: Bool) h t l1 l2 | b h t l1 -> l2
+ where
+ hTIntersectBool :: Proxy b -> h -> HList t -> HList l1 -> HList l2
+
+instance HTIntersect t l1 l2
+      => HTIntersectBool True h t l1 (h ': l2)
+ where
+  hTIntersectBool _ h t l1 = HCons h (hTIntersect t l1)
+
+instance HTIntersect t l1 l2
+      => HTIntersectBool False h t l1 l2
+ where
+  hTIntersectBool _ _ t l1 = hTIntersect t l1
+
+
+-- * Convert between heterogeneous lists and homogeneous ones
+
+-- | @hMapOut id@ is similar, except this function is restricted
+-- to HLists that actually contain a value (so the list produced
+-- will be nonempty). This restriction allows adding a functional
+-- dependency, which means that less type annotations can be necessary.
+class HList2List l e | l -> e
+ where
+  hList2List :: HList l -> [e]
+  list2HListSuffix :: [e] -> Maybe (HList l, [e])
+
+
+list2HList :: HList2List l e => [e] -> Maybe (HList l)
+list2HList = fmap fst . list2HListSuffix
+
+
+instance HList2List '[e] e
+ where
+  hList2List (HCons e HNil) = [e]
+
+  list2HListSuffix (e : es) = Just (HCons e HNil, es)
+  list2HListSuffix [] = Nothing
+
+
+instance HList2List (e' ': l) e
+      => HList2List (e ': e' ': l) e
+ where
+  hList2List (HCons e l) = e:hList2List l
+
+  list2HListSuffix (e : es) = (\(hl,rest) -> (HCons e hl, rest))
+                                  <$> list2HListSuffix es
+  list2HListSuffix [] = Nothing
+
+-- | @Prism [s] [t] (HList s) (HList t)@
+listAsHList x = prism hList2List (\l -> case list2HListSuffix l of
+                                 Just (hl,[])  -> Right hl
+                                 _ -> Left []) x
+
+-- | @Prism' [a] (HList s)@
+--
+-- where @s ~ HReplicateR n a@
+listAsHList' x = simple (listAsHList (simple x))
+
+
+-- --------------------------------------------------------------------------
+-- * With 'HMaybe'
+
+-- ** Turn list in a list of justs
+-- | the same as @map Just@
+--
+-- >>> toHJust (2 .*. 'a' .*. HNil)
+-- H[HJust 2,HJust 'a']
+--
+-- >>> toHJust2 (2 .*. 'a' .*. HNil)
+-- H[HJust 2,HJust 'a']
+
+class FromHJustR (ToHJustR l) ~ l => ToHJust l
+ where
+  type ToHJustR l :: [*]
+  toHJust :: HList l -> HList (ToHJustR l)
+
+instance ToHJust '[]
+ where
+  type ToHJustR '[] = '[]
+  toHJust HNil = HNil
+
+instance ToHJust l => ToHJust (e ': l)
+ where
+  type ToHJustR (e ': l) = HJust e ': ToHJustR l
+  toHJust (HCons e l) = HCons (HJust e) (toHJust l)
+
+-- | alternative implementation. The Apply instance is in "Data.HList.FakePrelude".
+-- A longer type could be inferred.
+toHJust2 :: (HMapCxt r (HJust ()) a b,
+             ToHJust a, b ~ ToHJustR a -- added to get equivalent inference
+             ) => r a -> r b
+toHJust2 xs = hMap (HJust ()) xs
+
+-- --------------------------------------------------------------------------
+-- ** Extract justs from list of maybes
+--
+-- >>> let xs = 2 .*. 'a' .*. HNil
+-- >>> fromHJust (toHJust xs) == xs
+-- True
+
+class (FromHJustR (ToHJustR l) ~ l) => FromHJust l
+ where
+  type FromHJustR l :: [*]
+  fromHJust :: HList l -> HList (FromHJustR l)
+
+instance FromHJust '[]
+ where
+  type FromHJustR '[] = '[]
+  fromHJust HNil = HNil
+
+instance FromHJust l => FromHJust (HNothing ': l)
+ where
+  type FromHJustR (HNothing ': l) = FromHJustR l
+  fromHJust (HCons _ l) = fromHJust l
+
+instance FromHJust l => FromHJust (HJust e ': l)
+ where
+  type FromHJustR (HJust e ': l) = e ': FromHJustR l
+  fromHJust (HCons (HJust e) l) = HCons e (fromHJust l)
+
+-- *** alternative implementation
+
+-- | This implementation is shorter.
+fromHJust2 :: (HMapCxt r HFromJust a b) => r a -> r b
+fromHJust2 xs = hMap HFromJust xs
+
+data HFromJust = HFromJust
+instance (hJustA ~ HJust a) => ApplyAB HFromJust hJustA a where
+    applyAB _ (HJust a) = a
+
+
+-- --------------------------------------------------------------------------
+-- * Annotated lists
+
+data HAddTag t = HAddTag t
+data HRmTag    = HRmTag
+
+-- hAddTag :: HMapCxt (HAddTag t) l r => t -> HList l -> HList r
+hAddTag t l = hMap (HAddTag t) l
+
+-- hRmTag ::  HMapCxt HRmTag l => HList l -> HList (HMapR HRmTag l)
+hRmTag l    = hMap HRmTag l
+
+instance (et ~ (e,t)) => ApplyAB (HAddTag t) e et
+ where
+  applyAB (HAddTag t) e = (e,t)
+
+
+instance (e' ~ e) => ApplyAB HRmTag (e,t) e'
+ where
+  applyAB _ (e,_) = e
+
+
+-- | Annotate list with a type-level Boolean
+-- hFlag :: HMapCxt (HAddTag (Proxy True)) l r => HList l -> HList r
+hFlag l = hAddTag hTrue l
+
+
+-- --------------------------------------------------------------------------
+-- * Splitting by HTrue and HFalse
+
+-- | Analogus to Data.List.'Data.List.partition' 'snd'. See also 'HPartition'
+--
+-- >>> let (.=.) :: p x -> y -> Tagged x y; _ .=. y = Tagged y
+-- >>> hSplit $ hTrue .=. 2 .*. hTrue .=. 3 .*. hFalse .=. 1 .*. HNil
+-- (H[2,3],H[1])
+--
+-- it might make more sense to instead have @LVPair Bool e@
+-- instead of @(e, Proxy Bool)@ since the former has the same
+-- runtime representation as @e@
+
+class HSplit l
+ where
+  type HSplitT l :: [*]
+  type HSplitF l :: [*]
+  hSplit :: HList l -> (HList (HSplitT l), HList (HSplitF l))
+
+instance HSplit '[]
+ where
+  type HSplitT '[] = '[]
+  type HSplitF '[] = '[]
+  hSplit HNil = (HNil,HNil)
+
+instance HSplit l => HSplit ((e, Proxy True) ': l)
+ where
+
+  type HSplitT ((e,Proxy True) ': l) = e ': HSplitT l
+  type HSplitF ((e,Proxy True) ': l) = HSplitF l
+
+  hSplit (HCons (e,_) l) = (HCons e l',l'')
+   where
+    (l',l'') = hSplit l
+
+instance HSplit l => HSplit ((e,Proxy False) ': l)
+ where
+  type HSplitT ((e,Proxy False) ': l) = HSplitT l
+  type HSplitF ((e,Proxy False) ': l) = e ': HSplitF l
+
+  hSplit (HCons (e,_) l) = (l',HCons e l'')
+   where
+    (l',l'') = hSplit l
+
+
+instance HSplit l => HSplit (Tagged True e ': l)
+ where
+
+  type HSplitT (Tagged True e ': l) = e ': HSplitT l
+  type HSplitF (Tagged True e ': l) = HSplitF l
+
+  hSplit (HCons (Tagged e) l) = (HCons e l',l'')
+   where
+    (l',l'') = hSplit l
+
+instance HSplit l => HSplit (Tagged False e ': l)
+ where
+  type HSplitT (Tagged False e ': l) = HSplitT l
+  type HSplitF (Tagged False e ': l) = e ': HSplitF l
+
+  hSplit (HCons (Tagged e) l) = (l',HCons e l'')
+   where
+    (l',l'') = hSplit l
+{-
+
+Let expansion makes a difference to Hugs:
+
+HListPrelude> let x = (hFlag (HCons "1" HNil)) in hSplit x
+(HCons "1" HNil,HNil)
+HListPrelude> hSplit (hFlag (HCons "1" HNil))
+ERROR - Unresolved overloading
+*** Type       : HSplit (HCons ([Char],HTrue) HNil) a b => (a,b)
+*** Expression : hSplit (hFlag (HCons "1" HNil))
+
+
+-}
+
+-- --------------------------------------------------------------------------
+-- * Splitting by Length
+
+{- | 'splitAt'
+
+setup
+
+>>> let two = hSucc (hSucc hZero)
+>>> let xsys = hEnd $ hBuild 1 2 3 4
+
+If a length is explicitly provided, the resulting lists are inferred
+
+>>> hSplitAt two xsys
+(H[1,2],H[3,4])
+
+>>> let sameLength_ :: SameLength a b => r a -> r b -> r a; sameLength_ = const
+>>> let len2 x = x `sameLength_` HCons () (HCons () HNil)
+
+If the first chunk of the list (a) has to be a certain length, the type of the
+Proxy argument can be inferred.
+
+>>> case hSplitAt Proxy xsys of (a,b) -> (len2 a, b)
+(H[1,2],H[3,4])
+
+-}
+class (HLengthEq xs n,
+       HAppendList1 xs ys xsys
+       )
+      => HSplitAt (n :: HNat) xsys xs ys
+                   | n xsys -> xs ys
+                      , xs ys -> xsys
+                      , xs -> n
+                     where
+    hSplitAt :: Proxy n -> HList xsys -> (HList xs, HList ys)
+
+instance (HSplitAt1 '[] n xsys xs ys,
+          HAppendList1 xs ys xsys,
+          HLengthEq xs n) =>
+    HSplitAt n xsys xs ys where
+      hSplitAt n xsys = hSplitAt1 HNil n xsys
+
+-- | helper for 'HSplitAt'
+class HSplitAt1 accum (n :: HNat) xsys xs ys | accum n xsys -> xs ys where
+    hSplitAt1 :: HList accum -> Proxy n -> HList xsys -> (HList xs, HList ys)
+
+instance HRevApp accum '[] xs => HSplitAt1 accum HZero ys xs ys where
+    hSplitAt1 xs _zero ys = (hReverse_ xs, ys)
+
+instance HSplitAt1 (b ': accum) n bs xs ys
+    => HSplitAt1 accum (HSucc n) (b ': bs) xs ys where
+    hSplitAt1 accum n (HCons b bs) = hSplitAt1 (HCons b accum) (hPred n) bs
+
+{- | a better way to write @HLength xs ~ n@ because:
+
+1. it works properly with ghc-7.10 (probably another example of ghc bug #10009)
+
+2. it works backwards a bit in that if @n@ is known, then @xs@ can be
+   refined:
+
+>>> undefined :: HLengthEq xs HZero => HList xs
+H[]
+
+-}
+class (SameLength' (HReplicateR n ()) xs,
+        HLengthEq1 xs n, HLengthEq2 xs n) => HLengthEq (xs :: [*]) (n :: HNat) | xs -> n
+
+instance (SameLength' (HReplicateR n ()) xs,
+          HLengthEq1 xs n, HLengthEq2 xs n) => HLengthEq xs n
+
+class HLengthEq1 (xs :: [*]) n -- pick the instance based on n's constructor
+instance (HLengthEq xs n, xxs ~ (x ': xs)) => HLengthEq1 xxs (HSucc n)
+instance (xxs ~ '[]) => HLengthEq1 xxs HZero
+
+class HLengthEq2 (xs :: [*]) n | xs -> n -- pick the instance based on xs' constructor
+instance (HLengthEq xs n, sn ~ HSucc n) => HLengthEq2 (x ': xs) sn
+instance zero ~ HZero => HLengthEq2 '[] zero
+
+
+-- | @HAppendList1 xs ys xsys@ is the type-level way of saying @xs ++ ys == xsys@
+--
+-- used by 'HSplitAt'
+class HStripPrefix xs xsys ys
+      => HAppendList1 (xs :: [k]) (ys :: [k]) (xsys :: [k])
+        | xs ys -> xsys,
+          xs xsys -> ys
+          -- , ys xsys -> xs
+          -- hard to prove
+
+instance HAppendList1 '[] ys ys
+instance (HAppendList1 xs ys zs) => HAppendList1 (x ': xs) ys (x ': zs)
+
+
+-- | analog of 'Data.List.stripPrefix'
+class HStripPrefix xs xsys ys | xs xsys -> ys
+instance (x' ~ x, HStripPrefix xs xsys ys) => HStripPrefix (x' ': xs) (x ': xsys) ys
+instance HStripPrefix '[] ys ys
+
+
+-- * Conversion to and from tuples
+
+class HTuple v t | v -> t, t -> v where
+    -- | alternatively: @hUncurry (,,,)@
+    hToTuple :: HList v -> t
+    hFromTuple :: t -> HList v
+
+-- | @Iso (HList v) (HList v') a b@
+hTuple x = iso hToTuple hFromTuple x
+
+-- | @Iso' (HList v) a@
+hTuple' x = simple (hTuple x)
+
+instance HTuple '[] () where
+    hToTuple HNil = ()
+    hFromTuple () = HNil
+
+instance HTuple '[a,b] (a,b) where
+    hToTuple (a `HCons` b `HCons` HNil) = (a,b)
+    hFromTuple (a,b) = (a `HCons` b `HCons` HNil)
+
+instance HTuple '[a,b,c] (a,b,c) where
+    hToTuple (a `HCons` b `HCons` c `HCons` HNil) = (a,b,c)
+    hFromTuple (a,b,c) = (a `HCons` b `HCons` c `HCons` HNil)
+
+instance HTuple '[a,b,c,d] (a,b,c,d) where
+    hToTuple (a `HCons` b `HCons` c `HCons` d `HCons` HNil) = (a,b,c,d)
+    hFromTuple (a,b,c,d) = (a `HCons` b `HCons` c `HCons` d `HCons` HNil)
+
+instance HTuple '[a,b,c,d,e] (a,b,c,d,e) where
+    hToTuple (a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` HNil) = (a,b,c,d,e)
+    hFromTuple (a,b,c,d,e) = (a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` HNil)
+
+instance HTuple '[a,b,c,d,e,f] (a,b,c,d,e,f) where
+    hToTuple (a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil) = (a,b,c,d,e,f)
+    hFromTuple (a,b,c,d,e,f) = (a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil)
+
+
+-- | 'tails'
+class HTails a b | a -> b, b -> a where
+    hTails :: HList a -> HList b
+
+instance HTails '[] '[HList '[]] where
+    hTails _ = HCons HNil HNil
+
+instance (HTails xs ys) => HTails (x ': xs) (HList (x ': xs) ': ys) where
+    hTails xxs@(HCons _x xs) = xxs `HCons` hTails xs
+
+
+-- | 'inits'
+class HInits a b | a -> b, b -> a where
+    hInits :: HList a -> HList b
+
+instance HInits1 a b => HInits a (HList '[] ': b) where
+    hInits xs = HNil `HCons` hInits1 xs
+
+
+-- | behaves like @tail . inits@
+class HInits1 a b | a -> b, b -> a where
+    hInits1 :: HList a -> HList b
+
+instance HInits1 '[] '[HList '[]] where
+    hInits1 _ = HCons HNil HNil
+
+instance (HInits1 xs ys,
+          HMapCxt HList (FHCons2 x) ys ys',
+          HMapCons x ys ~ ys',
+          HMapTail ys' ~ ys)
+  => HInits1 (x ': xs) (HList '[x] ':  ys') where
+    hInits1 (HCons x xs) = HCons x HNil `HCons` hMap (FHCons2 x) (hInits1 xs)
+
+
+-- | similar to 'FHCons'
+data FHCons2 x = FHCons2 x
+
+instance (hxs ~ HList xs,
+          hxxs ~ HList (x ': xs))
+  => ApplyAB (FHCons2 x) hxs hxxs where
+  applyAB (FHCons2 x) xs = HCons x xs
+
+
+-- | evidence to satisfy the fundeps in HInits
+type family HMapCons (x :: *) (xxs :: [*]) :: [*]
+type instance HMapCons x (HList a ': b) = HList (x ': a) ': HMapCons x b
+type instance HMapCons x '[] = '[]
+
+-- | evidence to satisfy the fundeps in HInits
+type family HMapTail (xxs :: [*]) :: [*]
+type instance HMapTail ( HList (a ': as) ': bs) = HList as ': HMapTail bs
+type instance HMapTail '[] = '[]
+
+
+-- * partition
+
+{- | @HPartitionEq f x1 xs xi xo@ is analogous to
+
+> (xi,xo) = partition (f x1) xs
+
+where @f@ is a \"function\" passed in using it's instance of 'HEqBy'
+-}
+class HPartitionEq f x1 xs xi xo | f x1 xs -> xi xo where
+    hPartitionEq :: Proxy f -> Proxy x1 -> HList xs -> (HList xi, HList xo)
+
+instance HPartitionEq f x1 '[] '[] '[] where
+    hPartitionEq _ _ _ = (HNil, HNil)
+
+instance
+   (HEqBy f x1 x b,
+    HPartitionEq1 b f x1 x xs xi xo) => HPartitionEq f x1 (x ': xs) xi xo where
+      hPartitionEq f x1 (HCons x xs) = hPartitionEq1 (Proxy :: Proxy b) f x1 x xs
+
+class HPartitionEq1 (b :: Bool) f x1 x xs xi xo | b f x1 x xs -> xi xo where
+    hPartitionEq1 :: Proxy b -> Proxy f -> Proxy x1 -> x -> HList xs -> (HList xi, HList xo)
+
+instance HPartitionEq f x1 xs xi xo =>
+    HPartitionEq1 True f x1 x xs (x ': xi) xo where
+      hPartitionEq1 _ f x1 x xs = case hPartitionEq f x1 xs of
+         (xi, xo) -> (x `HCons` xi, xo)
+
+instance HPartitionEq f x1 xs xi xo =>
+    HPartitionEq1 False f x1 x xs xi (x ': xo) where
+      hPartitionEq1 _ f x1 x xs = case hPartitionEq f x1 xs of
+         (xi, xo) -> (xi, x `HCons` xo)
+
+
+-- * groupBy
+
+{- | @HGroupBy f x y@ is analogous to @y = 'groupBy' f x@
+
+given that @f@ is used by 'HEqBy'
+-}
+class HGroupBy (f :: t) (as :: [*]) (gs :: [*]) | f as -> gs, gs -> as where
+    hGroupBy :: Proxy f -> HList as -> HList gs
+
+instance (HSpanEqBy f a as fst snd,
+          HGroupBy f snd gs) => HGroupBy f (a ': as) (HList (a ': fst) ': gs) where
+    hGroupBy f (HCons x xs) = case hSpanEqBy f x xs of
+                      (first, second) -> (x `HCons` first) `HCons` hGroupBy f second
+
+instance HGroupBy f '[] '[] where
+    hGroupBy _f HNil = HNil
+
+-- * span
+
+-- | @HSpanEq x y fst snd@ is analogous to @(fst,snd) = 'span' (== x) y@
+class HSpanEqBy (f :: t) (x :: *) (y :: [*]) (fst :: [*]) (snd :: [*])
+      | f x y -> fst snd, fst snd -> y where
+  hSpanEqBy :: Proxy f -> x -> HList y -> (HList fst, HList snd)
+
+instance (HSpanEqBy1 f x y revFst snd,
+          HReverse revFst fst,
+
+          HRevApp revFst snd y)
+    => HSpanEqBy f x y fst snd where
+  hSpanEqBy f x y =  case hSpanEqBy1 f x y of
+                      (revFst, second) -> (hReverse revFst, second)
+
+class HSpanEqBy1 (f :: t) (x :: *) (y :: [*]) (i :: [*]) (o :: [*])
+      | f x y -> i o where
+  hSpanEqBy1 :: Proxy f -> x -> HList y -> (HList i, HList o)
+
+class HSpanEqBy2 (b :: Bool) (f :: t) (x :: *) (y :: *) (ys :: [*]) (i :: [*]) (o :: [*])
+      | b f x y ys -> i o where
+  hSpanEqBy2 :: Proxy b -> Proxy f -> x -> y -> HList ys -> (HList i, HList o)
+
+
+instance (HEqBy f x y b,
+          HSpanEqBy2 b f x y ys i o) => HSpanEqBy1 f x (y ': ys) i o where
+  hSpanEqBy1 f x (HCons y ys) = hSpanEqBy2 (Proxy :: Proxy b) f x (y :: y) (ys :: HList ys)
+
+instance HSpanEqBy1 f x '[] '[] '[] where
+    hSpanEqBy1 _f _x _xs = (HNil, HNil)
+
+instance HSpanEqBy1 f x zs i o
+    => HSpanEqBy2 True f x y zs (y ': i) o where
+  hSpanEqBy2 _ f x y zs = case hSpanEqBy1 f x zs of
+                                      (i, o) -> (HCons y i, o)
+
+instance HSpanEqBy2 False f x y ys '[] (y ': ys) where
+  hSpanEqBy2 _b _f _x y ys = (HNil, HCons y ys)
+
+
+
+-- * zip
+
+-- $note see alternative implementations in "Data.HList.HZip"
+
+
+
+instance (SameLengths [x,y,xy], HZipList x y xy) => HUnzip HList x y xy where
+  hUnzip = hUnzipList
+
+instance (SameLengths [x,y,xy], HZipList x y xy) => HZip HList x y xy where
+  hZip = hZipList
+
+
+class HZipList x y l | x y -> l, l -> x y where
+  hZipList   :: HList x -> HList y -> HList l
+  hUnzipList :: HList l -> (HList x, HList y)
+
+instance HZipList '[] '[] '[] where
+  hZipList _ _ = HNil
+  hUnzipList _ = (HNil, HNil)
+
+instance ((x,y)~z, HZipList xs ys zs) => HZipList (x ': xs) (y ': ys) (z ': zs) where
+  hZipList (HCons x xs) (HCons y ys) = (x,y) `HCons` hZipList xs ys
+  hUnzipList (HCons ~(x,y) zs) = let ~(xs,ys) = hUnzipList zs in (x `HCons` xs, y `HCons` ys)
+
+-- * Monoid instance
+
+{- | Analogous to the Monoid instance for tuples
+
+>>> import Data.Monoid
+>>> mempty :: HList '[(), All, [Int]]
+H[(),All {getAll = True},[]]
+
+>>> mappend (hBuild "a") (hBuild "b") :: HList '[String]
+H["ab"]
+
+-}
+instance
+   (HProxies a,
+    HMapCxt HList ConstMempty (AddProxy a) a,
+    HZip HList a a aa,
+    HMapCxt HList UncurryMappend aa a) => Monoid (HList a) where
+  mempty = hMap ConstMempty
+            $ (hProxies :: HList (AddProxy a))
+  mappend a b = hMap UncurryMappend $ hZip a b
+
+
+-- ** helper functions
+
+data ConstMempty = ConstMempty
+instance (x ~ Proxy y, Monoid y) => ApplyAB ConstMempty x y where
+    applyAB _ _ = mempty
+
+data UncurryMappend = UncurryMappend
+instance (aa ~ (a,a), Monoid a) => ApplyAB UncurryMappend aa a where
+    applyAB _ = uncurry mappend
+
+
diff --git a/Data/HList/HListPrelude.hs b/Data/HList/HListPrelude.hs
--- a/Data/HList/HListPrelude.hs
+++ b/Data/HList/HListPrelude.hs
@@ -13,11 +13,30 @@
 
 import Data.HList.FakePrelude
 
+
 class HExtend e l where
   type HExtendR e l
   (.*.) :: e -> l -> HExtendR e l
+
 infixr 2 .*.
 
+
+-- | to keep types shorter, '.*.' used with Proxy avoids
+-- producing a @Proxy :: Proxy '[Label x,Label y,Label z]@
+-- if @Proxy :: Proxy '[x,y,z]@ is not a kind error (as it
+-- is when mixing Label6 and Label3 labels).
+--
+-- ghc-7.6 does not accept @Proxy ('[] :: [k])@ so for now
+-- require @k ~ *@
+instance HExtend (Label x) (Proxy ('[] :: [*])) where
+    type HExtendR (Label x) (Proxy ('[] :: [*])) = Proxy '[x]
+    (.*.) _ _ = Proxy
+
+
+-- | similar to 'emptyRecord', 'emptyTIP', emptyHList (actually called 'HNil'),
+-- except emptyProxy is the rightmost argument to '.*.'
+emptyProxy = Proxy :: Proxy ('[] :: [*])
+
 -- Poly-kinded
 class SubType l l'
 
@@ -38,14 +57,14 @@
 
 -- Class to test that a type is "free" in a type sequence
 -- polykinded
-class HOccursNot e l
+class HOccursNot (e :: k) (l :: [k])
 
 class HProject l l' where
   hProject :: l -> l'
 
 -- | Map a type (key) to a natural (index) within the collection
 -- This is a purely type-level computation
-class HType2HNat e l (n :: HNat) | e l -> n
+class HType2HNat (e :: k) (l :: [k]) (n :: HNat) | e l -> n
 
 --  | and lift to the list of types
 class HTypes2HNats es l (ns :: [HNat]) | es l -> ns
@@ -57,4 +76,21 @@
 
 class HDeleteMany e l l' | e l -> l' where
   hDeleteMany :: Proxy e -> l -> l'
+
+
+class HDeleteAtLabel (r :: [*] -> *) (l :: k) v v' | l v -> v' where
+    hDeleteAtLabel :: Label l -> r v -> r v'
+
+
+
+
+-- | 'unzip'
+class SameLengths [x,y,xy] => HUnzip (r :: [*] -> *) x y xy
+        | x y -> xy, xy -> x y where
+  hUnzip :: r xy -> (r x, r y)
+
+-- | 'zip'. Variant supports hUnzip, but not hZip ('hZipVariant' returns a Maybe)
+class HUnzip r x y xy => HZip (r :: [*] -> *) x y xy where
+  hZip :: r x -> r y -> r xy
+
 
diff --git a/Data/HList/HOccurs.hs b/Data/HList/HOccurs.hs
--- a/Data/HList/HOccurs.hs
+++ b/Data/HList/HOccurs.hs
@@ -20,18 +20,18 @@
 -- whose head has the type e. Return HNil if l does not have
 -- an element of type e.
 
-class HOccurrence e1 (l :: [*]) (l' :: [*]) | e1 l -> l' where
-    hOccurrence :: e1 -> HList l -> HList l'
+class HOccurrence (e1 :: *) (l :: [*]) (l' :: [*]) | e1 l -> l' where
+    hOccurrence :: Proxy e1 -> HList l -> HList l'
 
 instance HOccurrence e1 '[] '[] where
     hOccurrence _ = id
 
 instance (HEq e1 e b, HOccurrence' b e1 (e ': l) l')
     => HOccurrence e1 (e ': l) l' where
-    hOccurrence = hOccurrence' (undefined::Proxy b)
+    hOccurrence = hOccurrence' (Proxy::Proxy b)
 
-class HOccurrence' (b :: Bool) e1 (l :: [*]) (l' :: [*]) | b e1 l -> l' where
-    hOccurrence' :: Proxy b -> e1 -> HList l -> HList l'
+class HOccurrence' (b :: Bool) (e1 :: *) (l :: [*]) (l' :: [*]) | b e1 l -> l' where
+    hOccurrence' :: Proxy b -> Proxy e1 -> HList l -> HList l'
 
 instance HOccurrence' True e1 (e ': l) (e ': l) where
     hOccurrence' _ _ = id
@@ -46,10 +46,10 @@
 class HOccursMany e (l :: [*]) where
   hOccursMany :: HList l -> [e]
 
-instance (HOccurrence e l l', HOccursMany' e l') 
+instance (HOccurrence e l l', HOccursMany' e l')
     => HOccursMany e l
  where
-  hOccursMany l = hOccursMany' (hOccurrence (undefined::e) l)
+  hOccursMany l = hOccursMany' (hOccurrence (Proxy::Proxy e) l)
 
 class HOccursMany' e l where
   hOccursMany' :: HList l -> [e]
@@ -65,16 +65,16 @@
 -- One or more occurrences
 
 hOccursMany1 :: forall e l l'.
-		(HOccurrence e l (e ': l'), HOccursMany e l') =>
-		HList l -> (e,[e])
-hOccursMany1 l = case hOccurrence (undefined::e) l of
-		   (HCons e l') -> (e,hOccursMany (l'::HList l'))
+                (HOccurrence e l (e ': l'), HOccursMany e l') =>
+                HList l -> (e,[e])
+hOccursMany1 l = case hOccurrence (Proxy :: Proxy e) l of
+                   (HCons e l') -> (e,hOccursMany (l'::HList l'))
 
 -- --------------------------------------------------------------------------
 -- The first occurrence
 
 hOccursFst :: forall e l l'. HOccurrence e l (e ': l') => HList l -> e
-hOccursFst l = case hOccurrence (undefined::e) l of HCons e _ -> e
+hOccursFst l = case hOccurrence (Proxy::Proxy e) l of HCons e _ -> e
 
 -- --------------------------------------------------------------------------
 -- One occurrence and nothing is left
@@ -84,24 +84,31 @@
 
 instance (HOccurrence e (x ': y) l', HOccurs' e l')
     => HOccurs e (HList (x ': y)) where
-    hOccurs = hOccurs' . hOccurrence (undefined::e)
+    hOccurs = hOccurs' . hOccurrence (Proxy ::Proxy e)
 
 class HOccurs' e l where
     hOccurs' :: HList l -> e
 
 instance Fail (TypeNotFound e) => HOccurs' e '[] where
-    hOccurs' = undefined
+    hOccurs' = error "Data.HList.FakePrelude.Fail must have no instances"
 
 instance (e ~ e1, HOccursNot e l) => HOccurs' e (e ': l) where
     hOccurs' (HCons e _) = e
 
+-- | lookup a value in the collection (TIP usually) and return the TIP with that
+-- element deleted. Used to implement 'tipyTuple'.
+hOccursRest tip = case hOccurs tip of
+  x -> (x, hDeleteAtLabel (asLabel x) tip)
+  where asLabel :: x -> Label x
+        asLabel _ = Label
 
+
 -- --------------------------------------------------------------------------
 -- Zero or at least one occurrence
 
-hOccursOpt :: forall e l l'. 
-	      (HOccurrence e l l', HOccursOpt' e l') => HList l -> Maybe e
-hOccursOpt = hOccursOpt' . hOccurrence (undefined::e)
+hOccursOpt :: forall e l l'.
+              (HOccurrence e l l', HOccursOpt' e l') => HList l -> Maybe e
+hOccursOpt = hOccursOpt' . hOccurrence (Proxy :: Proxy e)
 
 class HOccursOpt' e l where
   hOccursOpt' :: HList l -> Maybe e
diff --git a/Data/HList/HSort.hs b/Data/HList/HSort.hs
new file mode 100644
--- /dev/null
+++ b/Data/HList/HSort.hs
@@ -0,0 +1,256 @@
+{-# LANGUAGE CPP #-}
+{- | Description: sorting
+
+Benchmarks for these functions can be found at
+<http://code.haskell.org/~aavogt/HList-nodup/Run.html>.
+
+See <Data-HList-CommonMain.html#v:hSort>
+for the public interface.
+
+-}
+module Data.HList.HSort where
+
+import Data.HList.HList
+import Data.HList.FakePrelude
+import Data.HList.Label3
+
+#if __GLASGOW_HASKELL__ > 707
+import GHC.TypeLits (type (<=?), CmpSymbol)
+-- | only in ghc >= 7.7
+instance ((x <=? y) ~ b) => HEqBy HLeFn x y b
+-- | only in ghc >= 7.7
+
+{- |
+
+>>> let b1 = Proxy :: HEqBy HLeFn "x" "y" b => Proxy b
+>>> :t b1
+b1 :: Proxy 'True
+
+>>> let b2 = Proxy :: HEqBy HLeFn "x" "x" b => Proxy b
+>>> :t b2
+b2 :: Proxy 'True
+
+>>> let b3 = Proxy :: HEqBy HLeFn "y" "x" b => Proxy b
+>>> :t b3
+b3 :: Proxy 'False
+
+-}
+instance (HEq (CmpSymbol x y) GT nb, HNot nb ~ b) => HEqBy HLeFn x y b
+#endif
+
+-- | the \"standard\" '<=' for types. Reuses 'HEqBy'
+--
+-- Note that ghc-7.6 is missing instances for Symbol and Nat, so that
+-- sorting only works 'HNat' (as used by "Data.HList.Label3").
+data HLeFn
+
+instance HEqByFn HLeFn
+
+instance (HLe x y ~ b) => HEqBy HLeFn x y b
+instance HEqBy HLeFn x y b => HEqBy HLeFn (Tagged x v) (Tagged y w) b
+instance HEqBy HLeFn x y b => HEqBy HLeFn (Label x) (Label y) b
+instance HEqBy HLeFn x y b => HEqBy HLeFn (Proxy x) (Proxy y) b
+
+-- | Data.HList.Label3 labels can only be compared if they belong
+-- to the same namespace.
+instance (HEqBy HLeFn n m b, ns ~ ns')
+     => HEqBy HLeFn (Lbl n ns desc) (Lbl m ns' desc') b
+
+
+-- | analogous to 'Data.Ord.Down'
+data HDown a
+instance HEqByFn a => HEqByFn (HDown a)
+instance HEqBy f y x b => HEqBy (HDown f) x y b
+
+-- | The HEqBy instances for @HNeq HLeFn@ gives '<'
+data HNeq le
+instance HEqByFn a => HEqByFn (HNeq a)
+instance (HEqBy le y x b1, HNot b1 ~ b2) => HEqBy (HNeq le) x y b2
+
+{- | @HIsAscList le xs b@ is analogous to
+
+> b = all (\(x,y) -> x `le` y) (xs `zip` tail xs)
+
+-}
+class HEqByFn le => HIsAscList le (xs :: [*]) (b :: Bool) | le xs -> b
+
+instance HEqByFn le => HIsAscList le '[x] True
+instance HEqByFn le => HIsAscList le '[] True
+instance (HEqBy le x y b1,
+         HIsAscList le (y ': ys) b2,
+         HAnd b1 b2 ~ b3)  => HIsAscList le (x ': y ': ys) b3
+
+
+-- | quick sort with a special case for sorted lists
+class (SameLength a b, HEqByFn le) => HSortBy le (a :: [*]) (b :: [*]) | le a -> b where
+    hSortBy :: Proxy le -> HList a -> HList b
+
+type HSort x y = HSortBy HLeFn x y
+
+hSort :: HSort x y => HList x -> HList y
+hSort xs = hSortBy (Proxy :: Proxy HLeFn) xs
+
+instance (SameLength a b,
+          HIsAscList le a ok,
+          HSortBy1 ok le a b) => HSortBy le a b where
+    hSortBy = hSortBy1 (Proxy :: Proxy ok)
+
+instance HSortBy1 True le a a where
+    hSortBy1 _ _ a = a -- already sorted
+
+instance HQSortBy le a b => HSortBy1 False le a b where
+    hSortBy1 _ = hQSortBy
+
+class HSortBy1 ok le (a :: [*]) (b :: [*]) | ok le a -> b where
+    hSortBy1 :: Proxy ok -> Proxy le -> HList a -> HList b
+
+-- * Merge Sort
+
+{- | HMSortBy is roughly a transcription of this merge sort
+
+> msort [] = []
+> msort [x] = [x]
+> msort [x,y] = hSort2 x y
+> msort xs = case splitAt (length xs `div` 2) xs of
+>              (a,b) -> msort a `merge` msort b
+
+> hSort2 x y
+>     | x <= y    = [x,y]
+>     | otherwise = [y,x]
+
+> merge (x : xs) (y : ys)
+>   | x > y     = y : merge (x : xs) ys
+>   | otherwise = x : merge xs (y : ys)
+
+-}
+class HEqByFn le => HMSortBy le (a :: [*]) (b :: [*]) | le a -> b where
+    hMSortBy :: Proxy le -> HList a -> HList b
+
+
+instance HEqByFn le => HMSortBy le '[] '[] where hMSortBy _ x = x
+instance HEqByFn le => HMSortBy le '[x] '[x] where hMSortBy _ x = x
+instance (HSort2 b x y ab, HEqBy le x y b) =>
+    HMSortBy le '[x,y] ab where
+      hMSortBy _ (a `HCons` b `HCons` HNil) = hSort2 (Proxy :: Proxy b) a b
+
+class HSort2 b x y ab | b x y -> ab where
+    hSort2 :: Proxy b -> x -> y -> HList ab
+
+instance HSort2 True x y '[x,y] where
+    hSort2 _ x y = x `HCons` y `HCons` HNil
+
+instance HSort2 False x y '[y,x] where
+    hSort2 _ x y = y `HCons` x `HCons` HNil
+
+instance (HMerge le xs' ys' sorted,
+          HMSortBy le ys ys',
+          HMSortBy le xs xs',
+          HLengthEq (a ': b ': c ': cs) n2,
+          HDiv2 n2 ~ n,
+          HSplitAt n (a ': b ': c ': cs) xs ys)
+  => HMSortBy le (a ': b ': c ': cs) sorted where
+  hMSortBy le abbs = case hSplitAt (Proxy :: Proxy n) abbs of
+      (xs, ys) -> hMerge le (hMSortBy le xs) (hMSortBy le ys)
+
+
+class HMerge le x y xy | le x y -> xy where
+    hMerge :: Proxy le -> HList x -> HList y -> HList xy
+
+instance HMerge le '[] '[] '[] where hMerge _ _ _ = HNil
+instance HMerge le (x ': xs) '[] (x ': xs) where hMerge _ x _ = x
+instance HMerge le '[] (x ': xs) (x ': xs) where hMerge _ _ x = x
+
+instance (HEqBy le x y b,
+          HMerge1 b (x ': xs) (y ': ys) (l ': ls) hhs,
+          HMerge le ls hhs srt)
+    => HMerge le (x ': xs) (y ': ys) (l ': srt) where
+  hMerge le xxs yys = case hMerge1 (Proxy :: Proxy b) xxs yys of
+        (HCons l ls, hhs) -> l `HCons` hMerge le ls hhs
+
+type HMerge1 b x y min max = (HCond b (HList x) (HList y) (HList min),
+                              HCond b (HList y) (HList x) (HList max))
+hMerge1 b x y = (hCond b x y, hCond b y x)
+
+-- * Quick sort
+{- | HQSortBy is this algorithm
+
+> qsort (x : xs @ (_ : _)) = case partition (<= x) xs of
+>                  (le, gt) -> qsort le ++ x : qsort gt
+> qsort xs = xs
+
+on random inputs that are not pathological (ie. not already sorted or reverse
+sorted) this turns out to be faster than HMSortBy, so it is used by default.
+
+-}
+class HQSortBy le (a :: [*]) (b :: [*]) | le a -> b where
+    hQSortBy :: Proxy le -> HList a -> HList b
+
+instance HQSortBy le '[] '[] where hQSortBy _ x = x
+instance HQSortBy le '[x] '[x] where hQSortBy _ x = x
+instance (HPartitionEq le a (b ': bs) bGeq bLt,
+        HQSortBy le bLt  sortedLt,
+        HQSortBy le bGeq sortedGeq,
+        HAppendListR sortedLt (a ': sortedGeq) ~ sorted,
+        HAppendList sortedLt (a ': sortedGeq)) =>
+    HQSortBy le (a ': b ': bs) sorted where
+    hQSortBy le (a `HCons` xs) = case hPartitionEq le (Proxy :: Proxy a) xs of
+                      (g,l) -> hQSortBy le l `hAppendList` (a `HCons` hQSortBy le g)
+
+
+
+
+-- * More efficient HRLabelSet / HLabelSet
+{- | Provided the labels involved have an appropriate instance of HEqByFn,
+it would be possible to use the following definitions:
+
+> type HRLabelSet = HSet
+> type HLabelSet  = HSet
+
+-}
+class HEqByFn lt => HSetBy lt (ps :: [*])
+instance (HSortBy lt ps ps', HAscList lt ps') => HSetBy lt ps
+
+class HSetBy (HNeq HLeFn) ps => HSet (ps :: [*])
+instance HSetBy (HNeq HLeFn) ps => HSet ps
+
+{- |
+
+>>> let xx = Proxy :: HIsSet [Label "x", Label "x"] b => Proxy b
+>>> :t xx
+xx :: Proxy 'False
+
+>>> let xy = Proxy :: HIsSet [Label "x", Label "y"] b => Proxy b
+>>> :t xy
+xy :: Proxy 'True
+
+-}
+class HIsSet (ps :: [*]) (b :: Bool) | ps -> b
+instance HIsSetBy (HNeq HLeFn) ps b => HIsSet ps b
+
+class HEqByFn lt => HIsSetBy lt (ps :: [*]) (b :: Bool) | lt ps -> b
+instance (HSortBy lt ps ps', HIsAscList lt ps' b) => HIsSetBy lt ps b
+
+
+-- | @HAscList le xs@ confirms that xs is in ascending order,
+-- and reports which element is duplicated otherwise.
+class HEqByFn le => HAscList le (ps :: [*])
+
+instance HAscList0 le ps ps => HAscList le ps
+
+class HEqByFn le => HAscList0 le (ps :: [*]) (ps0 :: [*])
+
+class HEqByFn le => HAscList1 le (b :: Bool) (ps :: [*]) (ps0 :: [*])
+instance (HAscList1 le b (y ': ys) ps0, HEqBy le x y b)
+  => HAscList0 le (x ': y ': ys) ps0
+instance HEqByFn le => HAscList0 le '[] ps0
+instance HEqByFn le => HAscList0 le '[x] ps0
+
+instance ( Fail '("Duplicated element", y, "using le", le, "in", ys0), HEqByFn le )
+    => HAscList1 le False (y ': ys) ys0
+instance HAscList0 le ys ys0 => HAscList1 le True ys ys0
+
+{- $setup
+
+>>> import Data.HList.TypeEqO
+
+-}
diff --git a/Data/HList/HTypeIndexed.hs b/Data/HList/HTypeIndexed.hs
--- a/Data/HList/HTypeIndexed.hs
+++ b/Data/HList/HTypeIndexed.hs
@@ -26,8 +26,8 @@
 instance HOccursNot e l => HType2HNatCase True e l HZero
 instance HType2HNat e l n => HType2HNatCase False e l (HSucc n)
 
-hType2HNat :: HType2HNat e l n => Proxy e -> l -> Proxy n
-hType2HNat _ _ = undefined
+hType2HNat :: HType2HNat e l n => proxy1 e -> proxy l -> Proxy n
+hType2HNat _ _ = Proxy
 
 -- | And lift to the list of types
 
@@ -36,9 +36,9 @@
 instance (HType2HNat e l n, HTypes2HNats es l ns)
       => HTypes2HNats (e ': es) (l :: [*]) (n ': ns)
 
-hTypes2HNats :: HTypes2HNats es l ns => 
-		Proxy (es :: [*]) -> HList l -> Proxy (ns :: [HNat])
-hTypes2HNats = undefined
+hTypes2HNats :: HTypes2HNats es l ns =>
+                Proxy (es :: [*]) -> hlist l -> Proxy (ns :: [HNat])
+hTypes2HNats _ _ = Proxy
 
 -- --------------------------------------------------------------------------
 -- Implementing the generic interfaces
@@ -48,8 +48,8 @@
 
 instance (HEq e1 e b, HDeleteManyCase b e1 e l l1)
       => HDeleteMany e1 (HList (e ': l)) (HList l1) where
-  hDeleteMany p (HCons e l) = 
-      hDeleteManyCase (undefined:: Proxy b) p e l
+  hDeleteMany p (HCons e l) =
+      hDeleteManyCase (Proxy :: Proxy b) p e l
 
 class HDeleteManyCase (b :: Bool) e1 e l l1 | b e1 e l -> l1 where
   hDeleteManyCase :: Proxy b -> Proxy e1 -> e -> HList l -> HList l1
@@ -66,24 +66,16 @@
 -- --------------------------------------------------------------------------
 -- Type-indexed operations in terms of the natural-based primitives
 
-hDeleteAt :: forall e l n. (HDeleteAtHNat n l, HType2HNat e l n) => 
-	   Proxy e -> HList l -> HList (HDeleteAtHNatR n l)
-hDeleteAt _p l = hDeleteAtHNat (undefined :: Proxy n) l
+hDeleteAt p l = hDeleteAtHNat (hType2HNat p l) l
 
-hUpdateAt :: forall n e l.
-		 (HUpdateAtHNat n e l, HType2HNat e l n) => 
-		 e -> HList l -> (HList (HUpdateAtHNatR n e l))
-hUpdateAt e l = hUpdateAtHNat (undefined:: Proxy n) e l
+hUpdateAt e l = hUpdateAtHNat (hType2HNat (Just e) l) e l
 
-hProjectBy :: forall (ns :: [HNat]) (ps :: [*]) (l :: [*]).
-	      (HProjectByHNatsCtx ns l, HTypes2HNats ps l ns,
-	      ps ~ (HProjectByHNatsR ns l)) =>
-	      Proxy ps -> HList l -> HList ps
-hProjectBy _ps l = hProjectByHNats (undefined::Proxy ns) l
+hProjectBy ps l = hProjectByHNats (hTypes2HNats ps l) l
 
-hSplitBy :: forall (ps :: [*]) l ns.
-	    (HProjectByHNatsCtx ns l, HProjectAwayByHNatsCtx ns l,
-	     HTypes2HNats ps l ns) =>
-	    Proxy ps -> HList l -> (HList (HProjectByHNatsR ns l), 
-				    HList (HProjectAwayByHNatsR ns l))
-hSplitBy _ps l = hSplitByHNats (undefined::Proxy ns) l
+hSplitBy ps l = hSplitByHNats (hTypes2HNats ps l) l
+
+
+-- | should this instead delete the first element of that type?
+instance (HDeleteAtHNat n l, HType2HNat e l n, l' ~ HDeleteAtHNatR n l)
+      => HDeleteAtLabel HList e l l' where
+    hDeleteAtLabel _ = hDeleteAtHNat (Proxy :: Proxy n)
diff --git a/Data/HList/HZip.hs b/Data/HList/HZip.hs
--- a/Data/HList/HZip.hs
+++ b/Data/HList/HZip.hs
@@ -17,17 +17,7 @@
 -- * zip
 -- ** functional dependency
 
-class HZip x y l | x y -> l, l -> x y where
-  hZip   :: HList x -> HList y -> HList l
-  hUnzip :: HList l -> (HList x, HList y)
-
-instance HZip '[] '[] '[] where
-  hZip _ _ = HNil
-  hUnzip _ = (HNil, HNil)
-
-instance ((x,y)~z, HZip xs ys zs) => HZip (x ': xs) (y ': ys) (z ': zs) where
-  hZip (HCons x xs) (HCons y ys) = (x,y) `HCons` hZip xs ys
-  hUnzip (HCons ~(x,y) zs) = let ~(xs,ys) = hUnzip zs in (x `HCons` xs, y `HCons` ys)
+-- $note1 moved to "Data.HList.HList" to avoid an orphan instance
 
 -- ** type family
 -- $note 'hZip2' can be written as a standalone function, with an appropriate
@@ -53,7 +43,6 @@
   type MapFst (z ': zs) = ( Fst z ': MapFst zs )
   type MapSnd (z ': zs) = ( Snd z ': MapSnd zs )
   hZip2 (HCons x xs) (HCons y ys) = HCons (x,y) (hZip2 xs ys)
-  hZip2 _ _ = error "Data.HList.HZip.hZip2: impossible case"
   hUnzip2 (HCons ~(x,y) zs) = let ~(xs,ys) = hUnzip2 zs in (x `HCons` xs, y `HCons` ys)
 
 
@@ -87,12 +76,12 @@
 The original list:
 
 >>> ex
-H[H[1, 2], H['a', 'b'], H[3, 5]]
+H[H[1,2],H['a','b'],H[3,5]]
 
 And transposed:
 
 >>> hTranspose ex
-H[H[1, 'a', 3], H[2, 'b', 5]]
+H[H[1,'a',3],H[2,'b',5]]
 
 -}
 hTranspose x = hFoldr HZipF (hReplicate (hLength (hHead x)) HNil) x
diff --git a/Data/HList/Keyword.hs b/Data/HList/Keyword.hs
--- a/Data/HList/Keyword.hs
+++ b/Data/HList/Keyword.hs
@@ -1,7 +1,8 @@
-{-# LANGUAGE OverlappingInstances, StandaloneDeriving #-}
 
-{- | keyword functions
+{- | Description: keyword arguments
 
+The public interface is exposed in <Data-HList-CommonMain.html#t:Kw CommonMain#Kw>
+
 -}
 module Data.HList.Keyword (
 
@@ -58,11 +59,13 @@
   -- * todo
   -- $todo
 
+  -- * internal for type signature prettiness
+  TaggedToKW,
   ) where
 
 import GHC.TypeLits
 import Data.HList.FakePrelude
-import Data.HList.TypeEqO ()
+import Data.HList.TypeEqO
 import Data.HList.HListPrelude
 import Data.HList.HList
 import Data.HList.Record
@@ -71,6 +74,7 @@
 
  >>> :set -XDataKinds -XFlexibleInstances -XMultiParamTypeClasses
  >>> :set -XScopedTypeVariables -XOverlappingInstances -XTypeFamilies
+ >>> :set -fcontext-stack=100
 
 We will be using an example inspired by a graphics toolkit -- the area
 which really benefits from keyword arguments. We first define our
@@ -201,7 +205,7 @@
 If we omit a required argument, we get a type error:
 
 > ] testse1 = let f x = kw make_square HNil Color Red x
-> ] 	    in "here: " ++ f Origin (0,10)
+> ]         in "here: " ++ f Origin (0,10)
 >
 >   Couldn't match `ErrReqdArgNotFound Size' against `[Char]'
 >       Expected type: ErrReqdArgNotFound Size
@@ -211,11 +215,11 @@
 message if we pass to a keyword function an argument it does not expect:
 
 > ] testse2 = let f x = kw make_square HNil Color Red x
-> ] 	    in "here: " ++ f Origin (0,10) Size (1::Int)
-> ]	                   RaisedBorder False
+> ]         in "here: " ++ f Origin (0,10) Size (1::Int)
+> ]                       RaisedBorder False
 >
 >   No instances for (Fail (ErrUnexpectedKW RaisedBorder),
-> 		    KWApply [Char] (HCons RaisedBorder (:*: Bool HNil)) [Char])
+>             KWApply [Char] (HCons RaisedBorder (:*: Bool HNil)) [Char])
 >       arising from use of `f' at ...
 >     In the second argument of `(++)', namely
 >   `f Origin (0,10) Size (1 :: Int) RaisedBorder False'
@@ -260,7 +264,7 @@
 > katest21 = kwapply f2 (Color .*. Red .*. Size .*. (1::Int) .*.  HNil)
 >
 > katest3  = kwapply f3 (Size .*. (1::Int) .*. Origin .*. (2.0::Float) .*.
-> 		         Color .*. Red .*. HNil)
+>                  Color .*. Red .*. HNil)
 
 -}
 
@@ -317,12 +321,7 @@
 data ErrReqdArgNotFound x
 data ErrUnexpectedKW x
 
--- | All our keywords must be registered
 
-
-class IsKeyFN   t (flag :: Bool) | t-> flag
--- | overlapping/fallback case
-instance (False ~ flag) => IsKeyFN t flag
 instance IsKeyFN (Label (s :: Symbol) -> a -> b) True
 {- ^ labels that impose no restriction on the type of the (single) argument
  which follows
@@ -365,7 +364,7 @@
     kwapply f _ = f
 
 instance (HEq kw kw' flag,
-	  KWApply' flag (kw ->a->f') (kw' ': a' ': tail) r)
+      KWApply' flag (kw ->a->f') (kw' ': a' ': tail) r)
     => KWApply (kw ->a->f') (kw' ': a' ': tail) r where
     kwapply = kwapply' (Proxy :: Proxy flag)
 
@@ -376,15 +375,14 @@
     => KWApply' True (kw->v->f') (kw ': v' ': tail) r where
     kwapply' _ f (HCons kw_ (HCons v' tl)) =
                    kwapply (f kw_ v') tl
-    kwapply' _ _ _ = error "Data.HList.Keyword.kwapply': impossible 1"
 
 -- | Rotate the arg list ...
-instance  (HAppendList tail '[kw , v] ~ l',
-	   KWApply f l' r)
+instance  (HAppendListR tail '[kw , v] ~ l',
+           HAppendList tail '[kw, v],
+       KWApply f l' r)
     => KWApply' False f (kw ': v ': tail) r where
     kwapply' _ f (HCons kw_ (HCons v tl)) =
-	kwapply f (hAppend tl (kw_ .*. v .*. HNil))
-    kwapply' _ _ _ = error "Data.HList.Keyword.kwapply': impossible 2"
+      kwapply f (hAppend tl (kw_ .*. v .*. HNil))
 
 {- |
 
@@ -404,11 +402,10 @@
 
 >>> :t reflect_fk (undefined::Size->Int->Color->CommonColor->String)
 reflect_fk (undefined::Size->Int->Color->CommonColor->String)
-  :: Arg [*] ((':) * Size ((':) * Color ('[] *))) ('[] *)
+  :: Arg '[Size, Color] '[]
 
 >>> :t reflect_fk (undefined::Size->Int->()->Int)
-reflect_fk (undefined::Size->Int->()->Int)
-  :: Arg [*] ((':) * Size ('[] *)) ('[] *)
+reflect_fk (undefined::Size->Int->()->Int) :: Arg '[Size] '[]
 
 
 -}
@@ -426,7 +423,7 @@
     kwdo :: f -> arg_desc -> HList arg_def -> r
 
 instance (IsKeyFN r rflag,
-	    KW' rflag f arg_desc arg_def r)
+        KW' rflag f arg_desc arg_def r)
     => KW f arg_desc arg_def r where
     kwdo = kw' (Proxy ::Proxy rflag)
 
@@ -465,8 +462,8 @@
 instance KWMerge' kw arg_def atail arg_values arg_def f r
     => KWMerge (kw ': atail) arg_values arg_def f r where
     kwmerge (Arg arg_values) arg_def =
-	kwmerge' (undefined :: kw) arg_def
-	         ((Arg arg_values)::Arg atail arg_values) arg_def
+      kwmerge' (undefined :: kw) arg_def
+             ((Arg arg_values)::Arg atail arg_values) arg_def
 
 class KWMerge' kw list atail arg_values arg_def f r where
     kwmerge':: kw -> HList list -> (Arg atail arg_values) -> HList arg_def -> f -> r
@@ -476,7 +473,7 @@
                 nff where
     kwmerge' = undefined
 instance (HEq kw kw' flag,
-	  KWMerge'' flag kw (kw' ': etc) atail arg_values arg_def f r)
+      KWMerge'' flag kw (kw' ': etc) atail arg_values arg_def f r)
     => KWMerge' kw (kw' ': etc) atail arg_values arg_def f r where
     kwmerge' = kwmerge'' (Proxy :: Proxy flag)
 
@@ -484,19 +481,17 @@
      where
     kwmerge'':: Proxy flag -> kw -> HList list
         -> Arg atail arg_values -> HList arg_def
-		-> f -> r
+        -> f -> r
 instance KWMerge atail (kw ': v ': arg_values) arg_def f r
     => KWMerge'' True kw (kw ': v ': tail)
                  atail arg_values arg_def f r where
     kwmerge'' _ _ (HCons kw_ (HCons v _)) (Arg arg_values) =
-	kwmerge ((Arg (kw_ .*. v .*. arg_values))::
-		 (Arg atail (kw ': v ': arg_values)))
-    kwmerge'' _ _ _ _ = error "Data.HList.kwmerge'': impossible"
+      kwmerge ((Arg (kw_ .*. v .*. arg_values))::
+         (Arg atail (kw ': v ': arg_values)))
 instance KWMerge' kw tail atail arg_values arg_def f r
     => KWMerge'' False kw (kw' ': v' ': tail)
                  atail arg_values arg_def f r where
     kwmerge'' _ kw_ (HCons _ (HCons _ tl)) = kwmerge' kw_ tl
-    kwmerge'' _ _ _ = error "Data.HList.kwmerge'': impossible 2"
 
 -- | Add the real argument to the Arg structure, and continue
 
@@ -505,11 +500,11 @@
 
 
 instance (HDelete kw arg_types arg_types',
-	  KW f (Arg arg_types' (kw ': a ': arg_values)) arg_def r)
+      KW f (Arg arg_types' (kw ': a ': arg_values)) arg_def r)
     => KWAcc (Arg arg_types arg_values) kw a f arg_def r  where
     kwaccum (Arg arg_values) kw_ a f =
-	kwdo f (Arg (kw_ .*. a .*. arg_values)::
-		Arg arg_types' (kw ': a ': arg_values))
+      kwdo f (Arg (kw_ .*. a .*. arg_values)::
+        Arg arg_types' (kw ': a ': arg_values))
 
 
 -- | Delete e from l to yield l' The element e must occur in l
@@ -581,8 +576,7 @@
 
 
 -}
-recToKW :: forall a b. (HMapAux TaggedToKW a b, SameLength a b,
-      SameLength b a, HConcat b) =>
+recToKW :: forall a b. (HMapCxt HList TaggedToKW a b, HConcat b) =>
      Record a -> HList (HConcatR b)
 recToKW (Record r) = hConcat (hMap TaggedToKW r :: HList b)
 
@@ -635,7 +629,7 @@
 
 Our implementation is a trivial extension of the strongly-typed
 polymorphic open records described in
-	<http://homepages.cwi.nl/~ralf/HList/>
+    <http://homepages.cwi.nl/~ralf/HList/>
 
 In fact, the implementation relies on the HList library.  To run the
 code (which this message is), one needs to download the HList library
diff --git a/Data/HList/Label3.hs b/Data/HList/Label3.hs
--- a/Data/HList/Label3.hs
+++ b/Data/HList/Label3.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 
 {- |
 
@@ -35,21 +36,41 @@
 module Data.HList.Label3 where
 
 import Data.HList.FakePrelude
+import Data.HList.HListPrelude
+import GHC.TypeLits
+import Data.Typeable
 
+{- $setup
 
-data Lbl (x :: HNat) ns desc  -- labels are exclusively type-level entities
+>>> let label3 = Label :: Label (Lbl HZero () ())
+>>> let label6 = Label :: Label "6"
 
+-}
 
--- Public constructors for labels
+data Lbl (x :: HNat) (ns :: *) (desc :: *)  -- labels are exclusively type-level entities
+#if !OLD_TYPEABLE
+  deriving Typeable
+#else
+instance (ShowLabel x) => Typeable2 (Lbl x) where
+  typeOf2 _ = mkTyConApp (mkTyCon3 "HList" "Data.HList.Label3" "Lbl")
+    [mkTyConApp (mkTyCon3 "GHC" "GHC.TypeLits" (showLabel (Label :: Label x)))
+      []]
+#endif
 
+type instance ZipTagged (Lbl ix ns n ': ts) (v ': vs) = Tagged (Lbl ix ns n) v ': ZipTagged ts vs
+
+instance (Label t ~ Label (Lbl ix ns n)) => SameLabels (Label t) (Lbl ix ns n)
+
+-- * Public constructors for labels
+
 -- | Construct the first label
 firstLabel :: ns -> desc -> Label (Lbl HZero ns desc)
-firstLabel = undefined
+firstLabel _ _ = Label
 
 
 -- | Construct the next label
 nextLabel :: Label (Lbl x ns desc) -> desc' -> Label (Lbl (HSucc x) ns desc')
-nextLabel = undefined
+nextLabel _ _ = Label
 
 
 -- | Equality on labels (descriptions are ignored)
@@ -57,7 +78,7 @@
 {-
 instance ( HEq x x' b
          , HEq ns ns' b'
-	 , bres ~ HAnd b b'
+         , bres ~ HAnd b b'
          )
       =>   HEq (Lbl x ns desc) (Lbl x' ns' desc') bres
 -}
@@ -67,11 +88,80 @@
 instance Show desc => ShowLabel (Lbl x ns desc) where
   showLabel = show . getd
       where getd :: Label (Lbl x ns desc) -> desc -- for the sake of Hugs
-            getd = undefined
+            getd = error "Data.HList.Label3 desc"
 
 instance Show desc => Show (Label (Lbl x ns desc))
  where
   show = show . getd
       where getd :: Label (Lbl x ns desc) -> desc -- for the sake of Hugs
-            getd = undefined
+            getd = error "Data.HList.Label3 desc"
 
+
+
+{- |
+
+If possible, Label is left off:
+
+>>> let q = label3 .*. label3 .*. emptyProxy
+>>> :t q
+q :: Proxy '[Lbl 'HZero () (), Lbl 'HZero () ()]
+
+-}
+instance HExtend (Label (Lbl n ns desc)) (Proxy (Lbl n' ns' desc' ': xs)) where
+    type HExtendR (Label (Lbl n ns desc)) (Proxy (Lbl n' ns' desc' ': xs))
+                = Proxy (Lbl n ns desc ': Lbl n' ns' desc' ': xs)
+    (.*.) _ _ = Proxy
+
+{- | Mixing two label kinds means we have to include 'Label':
+
+>>> let r = label3 .*. label6 .*. emptyProxy
+>>> :t r
+r :: Proxy '[Label (Lbl 'HZero () ()), Label "6"]
+
+-}
+instance HExtend (Label (Lbl n ns desc)) (Proxy (x ': xs :: [Symbol])) where
+    type HExtendR (Label (Lbl n ns desc)) (Proxy (x ': xs))
+              = Proxy (Label (Lbl n ns desc) ': MapLabel (x ': xs))
+    (.*.) _ _ = Proxy
+
+{- | Mixing two label kinds means we have to include 'Label':
+
+>>> let s = label6 .*. label3 .*. emptyProxy
+>>> :t s
+s :: Proxy '[Label "6", Label (Lbl 'HZero () ())]
+
+-}
+instance HExtend (Label (y :: Symbol)) (Proxy ((x :: *) ': xs)) where
+    type HExtendR (Label (y :: Symbol)) (Proxy (x ': xs))
+          = Proxy (Label y ':  (MapLabel (x ': xs)))
+    (.*.) _ _ = Proxy
+
+instance HExtend (Label (y :: Symbol)) (Proxy ((x :: Nat) ': xs)) where
+    type HExtendR (Label (y :: Symbol)) (Proxy (x ': xs))
+          = Proxy (Label y ':  (MapLabel (x ': xs)))
+    (.*.) _ _ = Proxy
+
+instance HExtend (Label (y :: Nat)) (Proxy ((x :: *) ': xs)) where
+    type HExtendR (Label (y :: Nat)) (Proxy (x ': xs))
+          = Proxy (Label y ':  (MapLabel (x ': xs)))
+    (.*.) _ _ = Proxy
+
+instance HExtend (Label (y :: Nat)) (Proxy ((x :: Symbol) ': xs)) where
+    type HExtendR (Label (y :: Nat)) (Proxy (x ': xs))
+          = Proxy (Label y ':  (MapLabel (x ': xs)))
+    (.*.) _ _ = Proxy
+
+-- | similar to Data.HList.Record.Labels1, but avoids producing Label (Label x)
+type family MapLabel (xs :: [k]) :: [*]
+type instance MapLabel '[] = '[]
+#if NO_CLOSED_TF
+-- if we can't have any overlap in the TF (ghc-7.6), cover the Label3/Label6 case
+type instance MapLabel ((x :: Symbol) ': xs) = Label x ': MapLabel xs
+type instance MapLabel (Lbl n ns desc ': xs) = Label (Lbl n ns desc) ': MapLabel xs
+type instance MapLabel (Label x ': xs) = Label x ': MapLabel xs
+#else
+type instance MapLabel (x ': xs) = AddLabel x ': MapLabel xs
+type family AddLabel (x :: k) :: * where
+  AddLabel (Label x) = Label x
+  AddLabel x = Label x
+#endif
diff --git a/Data/HList/Label5.hs b/Data/HList/Label5.hs
new file mode 100644
--- /dev/null
+++ b/Data/HList/Label5.hs
@@ -0,0 +1,45 @@
+{-# LANGUAGE CPP #-}
+#if (__GLASGOW_HASKELL__ < 709)
+{-# LANGUAGE OverlappingInstances #-}
+{-# OPTIONS_GHC -fno-warn-unrecognised-pragmas #-}
+#endif
+{- |
+   Description: labels are any instance of Typeable
+
+   The HList library
+
+   (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
+
+   Yet another model of labels.
+
+   This model allow us to use any type as label type.
+   As a result, we need some generic instances.
+
+   Also, type errors may be more confusing now.
+-}
+
+module Data.HList.Label5 where
+
+import Data.Typeable
+import Data.Char
+import Data.HList.FakePrelude
+
+
+-- | Equality on labels
+
+-- instance TypeEq x y b => HEq x y b
+
+
+-- | Show label
+instance {-# OVERLAPPABLE #-} Typeable (x :: *) => ShowLabel x
+ where
+  showLabel _ = (\(x:xs) -> toLower x:xs)
+            . reverse
+            . takeWhile (not . (==) '.')
+            . reverse
+            . show
+{-
+            . tyConString
+            . typeRepTyCon
+-}
+            . typeOf $ (error "Data.HList.Label5 has a strict typeOf" :: x)
diff --git a/Data/HList/Label6.hs b/Data/HList/Label6.hs
--- a/Data/HList/Label6.hs
+++ b/Data/HList/Label6.hs
@@ -12,7 +12,7 @@
    Labels are promoted Strings or Integers "GHC.TypeLits" inside the
    'Label'. Needs ghc7.6 or higher.
 
-   See "Data.HList.CommonMain#label6demo" for an example.
+   See <Data-HList-CommonMain.html#label6demo CommonMain#label6demo> for an example.
 
 -}
 
@@ -20,11 +20,36 @@
 
 import Data.HList.FakePrelude
 import GHC.TypeLits
+import Data.HList.HListPrelude
 
 #if MIN_VERSION_base(4,7,0)
 instance KnownSymbol x => ShowLabel (x :: Symbol) where
   showLabel _ =  symbolVal (Proxy :: Proxy x)
+instance KnownNat x => ShowLabel (x :: Nat) where
+  showLabel _ =  show $ natVal (Proxy :: Proxy x)
 #else
 instance SingI x => ShowLabel (x :: Symbol) where
   showLabel _ =  fromSing (sing :: Sing x)
+
+instance SingI x => ShowLabel (x :: Nat) where
+  showLabel _ =  show (fromSing (sing :: Sing x))
 #endif
+
+
+
+{- |
+
+>>> let labelX = Label :: Label "x"
+>>> let labelY = Label :: Label "y"
+>>> let p = labelX .*. labelY .*. emptyProxy
+>>> :t p
+p :: Proxy '["x", "y"]
+
+-}
+instance HExtend (Label (y :: Symbol)) (Proxy (x ': xs :: [Symbol])) where
+    type HExtendR (Label y) (Proxy (x ': xs)) = Proxy (y ': x ': xs)
+    (.*.) _ _ = Proxy
+
+instance HExtend (Label (y :: Nat)) (Proxy (x ': xs :: [Nat])) where
+    type HExtendR (Label y) (Proxy (x ': xs)) = Proxy (y ': x ': xs)
+    (.*.) _ _ = Proxy
diff --git a/Data/HList/Labelable.hs b/Data/HList/Labelable.hs
--- a/Data/HList/Labelable.hs
+++ b/Data/HList/Labelable.hs
@@ -1,11 +1,9 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE TemplateHaskell #-}
 {- |
 
-Description : labels which are also lenses
+Description : labels which are also lenses (or prisms)
 
 A simple problem is being solved here, but unfortunately it
-is a bit involved. The idea is to use the same haskell identifier 
+is a bit involved. The idea is to use the same haskell identifier
 for a lens and for other purposes. In other words, get the same
 behavior as:
 
@@ -20,125 +18,174 @@
 Elaboration of some ideas from edwardk.
 -}
 module Data.HList.Labelable
-    (makeLabelable,
-     Labelable(hLens'),
+    (Labelable(..),
+     LabeledOptic,
      (.==.),
 
+
+     -- * multiple lookups
+     Projected(..), projected',
+
     -- * comparison with 'hLens'
     -- $comparisonWithhLensFunction
 
     -- * likely unneeded (re)exports
-    -- $note needed to make a needed instance visible
-    Labeled(Labeled),
-    toLabel,
+    LabeledCxt1,
+    LabeledTo(LabeledTo),
+    LabeledR(LabeledR),
+    ToSym(toLabel),
     Identity,
-    ToSym,
+    LabelableTIPCxt,
+    LabeledOpticType(..),
+
+    LabeledOpticF,
+    LabeledOpticP,
+    LabeledOpticTo,
     ) where
 
 
+
+import Data.HList.HListPrelude
 import Data.HList.FakePrelude
-import Data.HList.HArray
-import Data.HList.HList
 import Data.HList.Record
+import Data.HList.Variant
+import Data.HList.TIP
+import Data.HList.TIC
+import Data.HList.Label3
 
 import Control.Monad.Identity
 import GHC.TypeLits
+import LensDefs
+import GHC.Exts (Constraint)
 
-import Language.Haskell.TH
+{- | This alias is the same as Control.Lens.Optic, except the (->) in Optic
+is a type parameter 'to' in LabeledOptic.
 
-{- | @f s t a b@ type parameters are the same as those that make
-"Control.Lens" work.
+Depending on the collection type (see instances of 'LabelableTy'),
+the type variables @to, p, f@ are constrained such that the resulting
+type is a @Lens (r s) (r t) a b@, @Prism (r s) (r t) a b@ or a
+@LabeledTo x _ _@. The latter can be used to recover the label (@x@) when
+used as an argument to '.==.' or equivalently 'toLabel'.
+-}
+type LabeledOptic (x :: k) (r :: [*] -> *) (s :: [*]) (t :: [*]) (a :: *) (b :: *)
+    = forall ty to p f.
+                     (ty ~ LabelableTy r,
+                      LabeledOpticF ty f,
+                      LabeledOpticP ty p,
+                      LabeledOpticTo ty x to) => (a `p` f b) `to` (r s `p` f (r t))
 
-[@n@] is the index in the HList at which the value will be found
+data LabeledOpticType = LabelableLens | LabelablePrism | LabelableLabel
 
-[@l@] is the label for the field (tends to be 'GHC.TypeLits.Symbol')
+type family LabeledOpticF (ty :: LabeledOpticType) :: (* -> *) -> Constraint
+type instance LabeledOpticF LabelableLens = Functor
+type instance LabeledOpticF LabelablePrism = Applicative
+type instance LabeledOpticF LabelableLabel = (~) Identity
 
-[@p@] is @->@ when the result is used as a lens, or 'Labeled' when used
-      as an argument to '.==.'
+type family LabeledOpticP (ty :: LabeledOpticType) :: (* -> * -> *) -> Constraint
+type instance LabeledOpticP LabelableLens = (~) (->)
+type instance LabeledOpticP LabelablePrism = Choice
+type instance LabeledOpticP LabelableLabel = (~) (->)
 
--}
-class Labelable l p f s t a b
-#if MIN_VERSION_base(4,7,0)
-     {- no fundeps in this case: they are potentially inconsistent
-        according to ghc-7.8
-        <http://ghc.haskell.org/trac/ghc/ticket/2247>
+type family LabeledOpticTo (ty :: LabeledOpticType) (x :: k) :: (* -> * -> *) -> Constraint
+type instance LabeledOpticTo LabelableLens x = (~) (->)
+type instance LabeledOpticTo LabelablePrism x = (~) (->)
+type instance LabeledOpticTo LabelableLabel x = (~) (LabeledTo x)
 
-        these fundeps are mostly documentation, since the two
-        instances have contexts that encode roughly the same
-        dependencies provided you choose a specific `p'
-     -}
-#else
-        | l s -> a, l t -> b,     -- lookup
-          l s b -> t, l t a -> s  -- update
-#endif
+
+{- |
+
+[@r@] is 'Record', 'Variant'. 'TIP' and 'TIC' also have instances, but generally
+'tipyLens'' and 'ticPrism'' are more appropriate.
+
+[@x@] is the label for the field. It tends to have kind 'GHC.TypeLits.Symbol',
+but others are supported in principle.
+
+-}
+class SameLength s t => Labelable (x :: k) (r :: [*] -> *) s t a b
+          | x s -> a, x t -> b,    -- lookup
+            x s b -> t, x t a -> s -- update
   where
-    hLens' :: Label l -> p (a -> f b) (Record s -> f (Record t))
+    type LabelableTy r :: LabeledOpticType
+    hLens' :: Label x -> LabeledOptic x r s t a b
 
-data Labeled (l :: k) (a :: *) (b :: *) = Labeled deriving (Show)
+data LabeledTo (x :: k) (a :: *) (b :: *) = LabeledTo deriving (Show)
 
--- | make a lens
-instance (Functor f,
-          HasField x (Record s) a,
-          HasField x (Record t) b,
-          HFind x (RecordLabels t) n,
-          HFind x (RecordLabels s) n,
-          HUpdateAtHNat n (Tagged x b) s,
-          t ~ HUpdateAtHNatR n (Tagged x b) s)
-        => Labelable x (->) f s t a b where
-            hLens' lab f rec = fmap (\v -> hUpdateAtLabel lab v rec) (f (rec .!. lab))
+data LabeledR (x :: [*]) = LabeledR
 
--- | make a data type that allows recovering the field name
-instance (f ~ Identity, s ~ '[], t ~ '[], a ~ (), b ~ (),
-           x' ~ x) => Labelable x' (Labeled x) f s t a b where
-        hLens' _ = Labeled :: Labeled x (a -> f b) (Record s -> f (Record t))
 
+-- | make a @Lens (Record s) (Record t) a b@
+instance HLens x Record s t a b
+        => Labelable x Record s t a b where
+            type LabelableTy Record = LabelableLens
+            hLens' = hLens
 
--- | modification of '.=.' which works with the labels from this module,
--- and those from "Data.HList.Label6". Note that this is not strictly a
--- generalization of '.=.', since it does not work with labels like
--- "Data.HList.Label3" which have the wrong kind.
-l .==. v = toLabel l .=. v
+-- | used with 'toLabel' and/or '.==.'
+instance LabeledCxt1 s t a b => Labelable x LabeledR s t a b where
+        type LabelableTy LabeledR = LabelableLabel
+        hLens' _ = LabeledTo
 
+-- | sets all type variables to dummy values: only the @Labeled x@
+-- part is actually needed
+type LabeledCxt1 s t a b = (s ~ '[], t ~ '[], a ~ (), b ~ ())
 
--- | extracts the type that is actually the label in @a@ and puts it in @b@
-class ToSym a b
+-- | make a @Prism (Variant s) (Variant t) a b@
+instance (HPrism x s t a b,
+          to ~ (->)) => Labelable x Variant s t a b where
+    type LabelableTy Variant = LabelablePrism
+    hLens' x s = hPrism x s
 
--- | for labels in this module
-instance (x ~ x', p ~ Labeled x') => ToSym (p a b) x'
+-- | @hLens' :: Label a -> Prism' (TIC s) a@
+--
+-- note that a more general function @'ticPrism' :: Prism (TIC s) (TIC t) a b@,
+-- cannot have an instance of Labelable
+instance (TICPrism s t a b, x ~ a, a ~ b, s ~ t,
+          SameLength s t) =>
+    Labelable (x :: *) TIC s t a b where
+      type LabelableTy TIC = LabelablePrism
+      hLens' _ = ticPrism
 
--- | for "Data.HList.Label6" labels
-instance (x ~ x') => ToSym (Label x) x'
 
-toLabel :: ToSym t t' => t -> Label (t' :: Symbol)
-toLabel _ = Label
+-- | make a @Lens' (TIP s) a@.
+--
+-- 'tipyLens' provides a @Lens (TIP s) (TIP t) a b@, which tends to need
+-- too many type annotations to be practical
+instance LabelableTIPCxt x s t a b =>
+    Labelable x TIP s t a b where
+    type LabelableTy TIP = LabelableLens
+    hLens' = hLens
 
+type LabelableTIPCxt x s t a b =
+     (s ~ t, a ~ b, x ~ a,
+      HLens x TIP s t a b)
 
 
-{- | @makeLabelable \"x y z\"@ will generate haskell identifiers that work with '.==.' and
-are also lenses.
+-- | modification of '.=.' which works with the labels from this module,
+-- and those from "Data.HList.Label6". Note that this is not strictly a
+-- generalization of '.=.', since it does not work with labels like
+-- "Data.HList.Label3" which have the wrong kind.
+l .==. v = toLabel l .=. v
 
-> x = hLens' (Label :: Label "x")
-> y = hLens' (Label :: Label "y")
-> z = hLens' (Label :: Label "z")
+infixr 4 .==.
 
+
+{- | Create a @'Label' (x :: 'Symbol')@:
+
+> toLabel :: LabeledTo x _ _ -> Label x
+> toLabel :: Label x         -> Label x
+> toLabel :: Proxy x         -> Label x
+
 -}
-makeLabelable :: String -> Q [Dec]
-makeLabelable xs = fmap concat $ mapM makeLabel1 (words xs)
-    where
-        -- a bit indirect, ghc-7.6 TH is a bit too eager to reject
-        -- mis-matched kind variables
-        makeLabel1 x = sequence
-              [
-                sigD (mkName x) makeSig,
-                valD (varP (mkName x)) (normalB (varE 'hLens' `appE` lt))
-                            []
-                ]
-            where lt = [| Label :: $([t| Label $l |]) |]
-                  l = litT (strTyLit x)
+class ToSym label (s :: Symbol) | label -> s where
+    toLabel :: label -> Label s
 
-                  makeSig = [t| Labelable $l p f s t a b => p (a -> f b) (Record s -> f (Record t)) |]
+instance LabeledTo x (a `p` f b) (LabeledR s `p` f (LabeledR t)) ~ v1 v2 v3
+    => ToSym (v1 v2 v3) x where
+      toLabel _ = Label
 
+instance ToSym (label x) x where
+    toLabel _ = Label
 
+
 {- $comparisonWithhLensFunction
 
 Note that passing around variables defined with 'hLens'' doesn't get
@@ -165,4 +212,114 @@
 It may work to use 'hLens'' instead of 'hLens' in the second code,
 but that is a bit beside the point being made here.
 
+The same points apply to the use of 'hPrism' over 'hLens''.
+
 -}
+
+{- | Sometimes it may be more convenient to operate on a record/variant
+that only contains the fields of interest. 'projected' can then be used
+to apply that function to a record that contains additional elements.
+
+
+>>> :set -XViewPatterns
+>>> import Data.HList.RecordPuns
+>>> let f [pun| (x y) |] = case x+y of z -> [pun| z |]
+>>> :t f
+f :: Num v =>
+     Record '[Tagged "x" v, Tagged "y" v] -> Record '[Tagged "z" v]
+
+>>> let r = (let x = 1; y = 2; z = () in [pun| x y z |])
+>>> r
+Record{x=1,y=2,z=()}
+
+>>> r & sameLabels . projected %~ f
+Record{x=1,y=2,z=3}
+
+
+
+
+-}
+class Projected r s t a b where
+    projected :: (ty ~ LabelableTy r,
+                LabeledOpticP ty p,
+                LabeledOpticF ty f) => r a `p` f (r b) -> r s `p` f (r t)
+
+-- | @Lens rs rt ra rb@
+--
+-- where @rs ~ Record s, rt ~ Record t, ra ~ Record a, rb ~ Record b@
+instance (-- for Record s -> Record a
+          H2ProjectByLabels (LabelsOf a) s a_ _s_minus_a,
+          HRLabelSet a_, HRLabelSet a,
+          HRearrange (LabelsOf a) a_ a,
+
+          HLeftUnion b s bs, HRLabelSet bs,
+          HRearrange (LabelsOf t) bs t, HRLabelSet t
+        ) => Projected Record s t a b where
+    projected f s = (\b -> hRearrange' (b .<++. s)) <$> f (hProjectByLabels' s :: Record a)
+
+-- | @Prism (Variant s) (Variant t) (Variant a) (Variant b)@
+instance (ExtendsVariant b t,
+          ProjectVariant s a,
+          ProjectExtendVariant s t,
+
+          HLeftUnion b s bs, HRLabelSet bs,
+          HRearrange (LabelsOf t) bs t)
+      => Projected Variant s t a b where
+    projected = prism extendsVariant
+            (\s -> case projectVariant s of
+                    Just a -> Right a
+                    Nothing | Just t <- projectExtendVariant s -> Left t
+                    _ -> error "Data.HList.Labelable.projected impossible"
+                    -- projectExtendVariant gives Nothing when the element of
+                    -- `t` that is actually stored in the variant comes
+                    -- from the `b`. But in that case the projectVariant
+                    -- above must have been Just
+              )
+
+
+{- | @Lens' (Record s) (Record a)@
+
+@Prism' (Variant s) (Variant a)@
+-}
+projected' s = simple (projected (simple s))
+
+
+{- | Together with the instance below, this allows writing
+
+@
+'makeLabelable' "x y z"
+p = x .*. y .*. z .*. 'emptyProxy'
+@
+
+Or with HListPP
+
+@
+p = `x .*. `y .*. `z .*. emptyProxy
+@
+
+instead of
+
+> p = Proxy :: Proxy ["x","y","z"]
+
+-}
+instance (to ~ LabeledTo x, ToSym (to p q) x)
+       => HExtend (to p q) (Proxy ('[] :: [*])) where
+    type HExtendR (to p q) (Proxy ('[] :: [*])) = Proxy '[GetXFromLabeledTo to]
+    (.*.) _ _ = Proxy
+
+instance (to ~ LabeledTo x, ToSym (to p q) x)
+       => HExtend (to p q) (Proxy (x ': xs)) where
+    type HExtendR (to p q) (Proxy (x ': xs)) = Proxy (GetXFromLabeledTo to ': x ': xs)
+    (.*.) _ _ = Proxy
+
+-- | if the proxy has Data.HList.Label3."Lbl", then everything has to be
+-- wrapped in Label to make the kinds match up.
+instance (to ~ LabeledTo x, ToSym (to p q) x)
+       => HExtend (to p q) (Proxy (Lbl n ns desc ': xs)) where
+    type HExtendR (to p q) (Proxy (Lbl n ns desc ': xs))
+        = Proxy (Label (GetXFromLabeledTo to) ': MapLabel (Lbl n ns desc ': xs))
+    (.*.) _ _ = Proxy
+
+type family GetXFromLabeledTo (to :: * -> * -> *) :: Symbol
+type instance GetXFromLabeledTo (LabeledTo x) = x
+
diff --git a/Data/HList/MakeLabels.hs b/Data/HList/MakeLabels.hs
--- a/Data/HList/MakeLabels.hs
+++ b/Data/HList/MakeLabels.hs
@@ -7,12 +7,16 @@
 module Data.HList.MakeLabels (
     makeLabels,
     makeLabels3,
+
+    -- * labels using kind 'Symbol'
     makeLabels6,
-    -- | see also 'Data.HList.Labelable.makeLabelable'
+    makeLabelable,
     ) where
 
+import Data.Typeable
 import Data.HList.FakePrelude
 import Data.HList.Label3
+import Data.HList.Labelable
 
 import Language.Haskell.TH
 import Data.Char
@@ -30,7 +34,7 @@
     c = make_cname n
     d = make_dname n
 
-    dd = dataD (return []) c [] [] [{- 'Typeable -}]
+    dd = dataD (return []) c [] [] [''Typeable]
 
     labelSig = sigD d [t| Label $(conT c) |]
 
@@ -39,13 +43,6 @@
                   (normalB [| Label |])
                   []
 
-    showLabelInst = instanceD
-            (return [])
-            [t| ShowLabel $(conT c) |]
-            [valD (varP 'showLabel)
-                (normalB [| \_ -> n |])
-                [] ]
-
     showInst = instanceD
             (return [])
             [t| Show $(conT c) |]
@@ -59,13 +56,12 @@
 
         dd,
 
-        showLabelInst,
         showInst ]
 
 
 {- |
 
-Labels like "Data.HList.Label4" used to provide (only no Typeable).
+Labels like "Data.HList.Label5".
 
  The following TH declaration splice should be placed at top-level, before the
  created values are used. Enable @-XTemplateHaskell@ too.
@@ -74,21 +70,16 @@
 
 should expand into the following declarations
 
-> data LabelGetX
-> data LabelGetY
-> data LabelDraw
-> data LabelX
+> data LabelGetX deriving Typeable
+> data LabelGetY deriving Typeable
+> data LabelDraw deriving Typeable
+> data LabelX deriving Typeable
 
 > getX = Label :: Label LabelGetX
 > getY = Label :: Label LabelGetY
 > draw = Label :: Label LabelDraw
 > x    = Label :: Label LabelX
 
-
-> instance ShowLabel LabelGetX where showLabel = \_ -> "getX"
-> instance ShowLabel LabelGetY where showLabel = \_ -> "getY"
-> instance ShowLabel LabelDraw where showLabel = \_ -> "draw"
-
 -}
 makeLabels :: [String] -> Q [Dec]
 makeLabels = fmap concat . mapM dcl
@@ -117,8 +108,45 @@
 -- possibly there is a better option
 makeLabels3 ns [] = fail ("makeLabels3 "++ ns ++ " []")
 
--- | for "Data.HList.Label6"
+{- | for "Data.HList.Label6"
+
+> makeLabels6 ["x","y"]
+
+is a shortcut for
+
+> x = Label :: Label "x"
+> y = Label :: Label "y"
+
+-}
 makeLabels6 :: [String] -> Q [Dec]
 makeLabels6 ns = fmap concat $ forM ns $ \n -> sequence
   [sigD (make_dname n) [t| Label $(litT (strTyLit n)) |],
    valD (varP (make_dname n)) (normalB [| Label |]) []]
+
+
+{- | @makeLabelable \"x y z\"@ expands out to
+
+> x = hLens' (Label :: Label "x")
+> y = hLens' (Label :: Label "y")
+> z = hLens' (Label :: Label "z")
+
+Refer to "Data.HList.Labelable" for usage.
+
+-}
+makeLabelable :: String -> Q [Dec]
+makeLabelable xs = fmap concat $ mapM makeLabel1 (words xs)
+    where
+        -- a bit indirect, ghc-7.6 TH is a bit too eager to reject
+        -- mis-matched kind variables
+        makeLabel1 x = sequence
+              [
+                sigD (mkName x) makeSig,
+                valD (varP (mkName x)) (normalB (varE 'hLens' `appE` lt))
+                            []
+                ]
+            where lt = [| Label :: $([t| Label $l |]) |]
+                  l = litT (strTyLit x)
+
+                  makeSig = [t| forall r s t a b. (Labelable $l r s t a b) =>
+                              LabeledOptic $l r s t a b
+                              |]
diff --git a/Data/HList/Record.hs b/Data/HList/Record.hs
--- a/Data/HList/Record.hs
+++ b/Data/HList/Record.hs
@@ -10,9 +10,13 @@
 
    * "Data.HList.Label3"
 
-   * "Data.HList.MakeLabels"
+   * "Data.HList.Label5"
 
+   * "Data.HList.Label6"
 
+   * "Data.HList.Labelable"
+
+
    These used to work:
 
    * "Data.HList.Label1"
@@ -21,8 +25,6 @@
 
    * "Data.HList.Label4"
 
-   * "Data.HList.Label5"
-
 -}
 
 module Data.HList.Record
@@ -35,24 +37,34 @@
     -- ** Labels
     -- $labels
     module Data.Tagged,
-    (.=.), (.-.),
+    (.=.),
 
     -- ** Record
     Record(..),
     mkRecord,
     emptyRecord,
+    hEndR,
+    hEndP,
 
-    -- *** Getting Labels
-    RecordLabels,
-    recordLabels,
+    hListRecord, hListRecord',
 
+    -- *** Getting Labels
     LabelsOf,
-    hLabels,
+    labelsOf,
+    asLabelsOf,
 
     -- *** Getting Values
     RecordValues(..),
     recordValues,
+    hMapTaggedFn,
 
+    unlabeled0,
+
+    Unlabeled,
+    unlabeled,
+    Unlabeled',
+    unlabeled',
+
     -- * Operations
     -- ** Show
     -- | A corresponding 'Show' instance exists as
@@ -61,42 +73,53 @@
     ShowComponents(..),
     ShowLabel(..),
 
+    -- ** Extension
+    -- | 'hExtend', 'hAppend'
+    (.*.),
 
     -- ** Delete
     -- | 'hDeleteAtLabel' @label record@
-    hDeleteAtLabel,
+    (.-.),
+    HDeleteLabels(..),
 
     -- ** Lookup/update
     -- $lens
-    hLens,
+    HLens(hLens),
 
     -- ** Lookup
     HasField(..),
-    HasField'(..),
+    HasFieldM(..),
     (.!.),
 
     -- ** Update
     (.@.),
-    hUpdateAtLabel,
+    HUpdateAtLabel(hUpdateAtLabel),
     -- *** type-preserving versions
-    -- $note these restrict the resulting record type to be the same as in
+    -- | Note: these restrict the resulting record type to be the same as in
     -- input record type, which can help reduce the number of type annotations
     -- needed
     (.<.),
+    HTPupdateAtLabel,
     hTPupdateAtLabel,
 
     -- ** Rename Label
     hRenameLabel,
 
     -- ** Projection
+
+    Labels,
+
     -- $projection
     hProjectByLabels,
+    hProjectByLabels',
     hProjectByLabels2,
 
+    -- *** a lens for projection
+    -- | see "Data.HList.Labelable".'Projected'
+
     -- ** Unions
     -- *** Left
     HLeftUnion(hLeftUnion),
-    HLeftUnionBool(hLeftUnionBool),
     (.<++.),
 
     -- *** Symmetric
@@ -106,40 +129,74 @@
 
     -- ** Reorder Labels
     hRearrange,
+    hRearrange',
 
-    -- ** Extension
-    -- | 'hExtend', 'hAppend'
-    (.*.),
+    -- *** isos using hRearrange
+    Rearranged(rearranged), rearranged',
 
 
+    -- ** Apply a function to all values
+    hMapR, HMapR(..),
+
+    -- ** cast labels
+    Relabeled(relabeled),
+    relabeled',
+
     -- * Hints for type errors
     DuplicatedLabel,
-    ExtraField(..),
-    FieldNotFound(..),
+    ExtraField,
+    FieldNotFound,
 
     -- * Unclassified
+
     -- | Probably internals, that may not be useful
+    zipTagged,
+    HasField'(..),
+    DemoteMaybe,
+    HasFieldM1(..),
     H2ProjectByLabels(h2projectByLabels),
     H2ProjectByLabels'(h2projectByLabels'),
     HLabelSet,
     HLabelSet',
     HRLabelSet,
-    HRLabelSet',
+    HAllTaggedLV,
     HRearrange(hRearrange2),
-    HRearrange'(hRearrange2'),
+    HRearrange3(hRearrange3),
+    HRearrange4(hRearrange4),
     UnionSymRec'(..),
+    HFindLabel,
     labelLVPair,
     newLVPair,
+    UnLabel,
+    HMemberLabel,
+    TaggedFn(..),
+    ReadComponent,
+    HMapTaggedFn,
+    HLensCxt,
+
+    -- ** zip
+    -- | use the more general 'HZip' class instead
+    HZipRecord(..),
+    -- *** alternative implementation
+    hZipRecord2, hUnzipRecord2
 ) where
 
 
 import Data.HList.FakePrelude
 import Data.HList.HListPrelude
 import Data.HList.HList
-import Data.HList.HArray
 
+import Data.HList.Label3 (MapLabel)
+
 import Data.Tagged
- 
+import Control.Monad
+
+import Text.ParserCombinators.ReadP
+
+import LensDefs
+
+import Data.Array (Ix)
+
 -- imports for doctest/examples
 import Data.HList.Label6 ()
 import Data.HList.TypeEqO ()
@@ -187,46 +244,121 @@
 l .=. v = newLVPair l v
 
 
-newtype Record (r :: [*]) = Record (HList r) -- deriving Eq
+newtype Record (r :: [*]) = Record (HList r)
 
+deriving instance Monoid (HList r) => Monoid (Record r)
+deriving instance (Eq (HList r)) => Eq (Record r)
+deriving instance (Ord (HList r)) => Ord (Record r)
+deriving instance (Ix (HList r)) => Ix (Record r)
+deriving instance (Bounded (HList r)) => Bounded (Record r)
 
+
 -- | Build a record
 mkRecord :: HRLabelSet r => HList r -> Record r
 mkRecord = Record
 
+-- | @HRLabelSet t => Iso (HList s) (HList t) (Record s) (Record t)@
+hListRecord x = isoNewtype mkRecord (\(Record r) -> r) x
 
+-- | @Iso' (HList s) (Record s)@
+hListRecord' x = simple (isoNewtype Record (\(Record r) -> r) x)
+
 -- | Build an empty record
 emptyRecord :: Record '[]
 emptyRecord = mkRecord HNil
 
+-- | @Iso (Record s) (Record t) (HList a) (HList b)@
+--
+-- @view unlabeled == 'recordValues'@
+unlabeled0 x = sameLabels (iso recordValues hMapTaggedFn x)
 
--- | Propery of a proper label set for a record: no duplication of labels
 
+unlabeled :: (Unlabeled x y, Profunctor p, Functor f) =>
+    (HList (RecordValuesR x) `p` f (HList (RecordValuesR y))) ->
+    (Record x `p` f (Record y))
+unlabeled x = sameLength (unlabeled0 (sameLength x))
+
+type Unlabeled x y =
+      (HMapCxt HList TaggedFn (RecordValuesR y) y,
+       RecordValues x, RecordValues y,
+       SameLength (RecordValuesR x) (RecordValuesR y),
+       SameLength x y, SameLabels x y,
+       HAllTaggedLV x, HAllTaggedLV y)
+type Unlabeled' x = Unlabeled x x
+
+
+
+-- | @Unlabeled' x => Iso' (Record x) (HList (RecordValuesR x))@
+unlabeled' :: (Unlabeled' x, Profunctor p, Functor f) =>
+    (HList (RecordValuesR x) `p` f (HList (RecordValuesR x))) ->
+    (Record x `p` f (Record x))
+unlabeled' = unlabeled
+
+{- | @Iso (Record s) (Record t) (Record a) (Record b)@, such that
+@relabeled = unlabeled . from unlabeled@
+
+in other words, pretend a record has different labels, but the same values.
+
+-}
+class Relabeled r where
+  relabeled :: forall p f s t a b.
+      (HMapTaggedFn (RecordValuesR s) a,
+       HMapTaggedFn (RecordValuesR b) t,
+       SameLengths '[s,a,t,b],
+       RecordValuesR t ~ RecordValuesR b,
+       RecordValuesR s ~ RecordValuesR a,
+       RecordValues b, RecordValues s,
+       Profunctor p,
+       Functor f
+       ) => r a `p` f (r b) -> r s `p` f (r t)
+
+instance Relabeled Record where
+  relabeled = iso
+    (\ s -> hMapTaggedFn (recordValues s))
+    (\ b -> hMapTaggedFn (recordValues b))
+    -- isoNewtype should be safe here, but there are no guarantees
+    -- http://stackoverflow.com/questions/24222552
+
+-- | @Iso' (Record s) (Record a)@
+--
+-- such that @RecordValuesR s ~ RecordValuesR a@
+relabeled' x = simple (relabeled x)
+
+data TaggedFn = TaggedFn
+instance (tx ~ Tagged t x) => ApplyAB TaggedFn x tx where
+    applyAB _ = Tagged
+
+type HMapTaggedFn l r =
+    (HMapCxt HList TaggedFn l r,
+     RecordValuesR r ~ l,
+     RecordValues r)
+
+-- | \"inverse\" to 'recordValues'
+hMapTaggedFn :: HMapTaggedFn a b => HList a -> Record b
+hMapTaggedFn = Record . hMap TaggedFn
+
+-- | Property of a proper label set for a record: no duplication of labels,
+-- and every element of the list is @Tagged label value@
+
 data DuplicatedLabel l
 
-class HRLabelSet (ps :: [*])
-instance HRLabelSet '[]
-instance HRLabelSet '[x]
-instance ( HEq l1 l2 leq
-         , HRLabelSet' l1 l2 leq r
-         ) => HRLabelSet (Tagged l1 v1 ': Tagged l2 v2 ': r)
+class (HLabelSet (LabelsOf ps), HAllTaggedLV ps) => HRLabelSet (ps :: [*])
+instance (HLabelSet (LabelsOf ps), HAllTaggedLV ps) => HRLabelSet (ps :: [*])
 
-class HRLabelSet' l1 l2 (leq::Bool) (r :: [*])
-instance ( HRLabelSet (Tagged l2 () ': r)
-         , HRLabelSet (Tagged l1 () ': r)
-         ) => HRLabelSet' l1 l2 False r
-instance ( Fail (DuplicatedLabel l1) ) => HRLabelSet' l1 l2 True r
 
 
--- | Relation between HLabelSet and HRLabelSet
-{-
-instance (HZip ls vs ps, HLabelSet ls) => HRLabelSet ps
+
+{- | Relation between HLabelSet and HRLabelSet
+
+> instance HLabelSet (LabelsOf ps) => HRLabelSet ps
+
+see also 'HSet'
 -}
 
 class HLabelSet ls
 instance HLabelSet '[]
 instance HLabelSet '[x]
-instance ( HEq l1 l2 leq
+instance ( HEqK l1 l2 leq
          , HLabelSet' l1 l2 leq r
          ) => HLabelSet (l1 ': l2 ': r)
 
@@ -236,19 +368,32 @@
          ) => HLabelSet' l1 l2 False r
 instance ( Fail (DuplicatedLabel l1) ) => HLabelSet' l1 l2 True r
 
--- | Construct the (phantom) list of labels of the record.
---
+-- | Construct the (phantom) list of labels of a record,
+-- or list of Label.
+type family LabelsOf (ls :: [*]) :: [*]
+type instance LabelsOf '[] = '[]
+type instance LabelsOf (Label l ': r)  = Label l ': LabelsOf r
+type instance LabelsOf (Tagged l v ': r) = Label l ': LabelsOf r
 
-type family RecordLabels (r :: [*]) :: [k]
-type instance RecordLabels '[]               = '[]
-type instance RecordLabels (Tagged l v ': r) = l ': RecordLabels r
+labelsOf :: hlistOrRecord l -> Proxy (LabelsOf l)
+labelsOf _ = Proxy
 
-recordLabels :: Record r -> Proxy (RecordLabels r)
-recordLabels _ = Proxy
 
 
+-- | remove the Label type constructor. The @proxy@ argument is
+-- supplied to make it easier to fix the kind variable @k@.
+type family UnLabel (proxy :: k) (ls :: [*]) :: [k]
+type instance UnLabel proxy (Label x ': xs) = x ': UnLabel proxy xs
+type instance UnLabel proxy '[] = '[]
+
+-- | A version of 'HFind' where the @ls@ type variable is a list of
+-- 'Tagged' or 'Label'. This is a bit indirect, and ideally LabelsOf
+-- could have kind [*] -> [k].
+type HFindLabel (l :: k) (ls :: [*]) (n :: HNat) = HFind l (UnLabel l (LabelsOf ls)) n
+
 -- | Construct the HList of values of the record.
-class RecordValues (r :: [*]) where
+class SameLength r (RecordValuesR r)
+      => RecordValues (r :: [*]) where
   type RecordValuesR r :: [*]
   recordValues' :: HList r -> HList (RecordValuesR r)
 
@@ -262,14 +407,13 @@
 recordValues :: RecordValues r => Record r -> HList (RecordValuesR r)
 recordValues (Record r) = recordValues' r
 
-
--- | Making this ls::[*] and [k] breaks the MainGhcGeneric1.hs...
-type family LabelsOf (ls :: [*]) :: [*]
-type instance LabelsOf '[] = '[]
-type instance LabelsOf (Label l ': r)  = l ': LabelsOf r
+{- shorter, but worse in terms needing annotations to allow ambiguous types
+- but better in terms of inference
+recordValues :: RecordValues r rv => Record r -> HList rv
+recordValues (Record r) = hMap HUntag r
 
-hLabels :: HList l -> Proxy (LabelsOf l)
-hLabels _ = Proxy
+type RecordValues r rv = HMapCxt HUntag r rv
+-}
 
 -- --------------------------------------------------------------------------
 
@@ -299,15 +443,57 @@
      ++ showComponents "," r
 
 
+-- --------------------------------------------------------------------------
 
+-- 'Read' instance to appeal to normal records
 
+
+data ReadComponent = ReadComponent Bool -- ^ include comma?
+
+instance (Read v, ShowLabel l,
+          x ~ Tagged l v,
+          ReadP x ~ y) =>
+  ApplyAB ReadComponent (Proxy x) y where
+    applyAB (ReadComponent comma) _ = do
+      when comma (() <$ string ",")
+      _ <- string (showLabel (Label :: Label l))
+      _ <- string "="
+      v <- readS_to_P reads
+      return (Tagged v)
+
+
+instance (HMapCxt HList ReadComponent (AddProxy rs) bs,
+          ApplyAB ReadComponent (Proxy r) readP_r,
+          HProxies rs,
+          HSequence ReadP (readP_r ': bs) (r ': rs)) => Read (Record (r ': rs)) where
+    readsPrec _ = readP_to_S $ do
+        _ <- string "Record{"
+        content <- hSequence parsers
+        _ <- string "}"
+        return (Record content)
+
+      where
+        rs :: HList (AddProxy rs)
+        rs = hProxies
+
+        readP_r :: readP_r
+        readP_r = applyAB
+                      (ReadComponent False)
+                      (Proxy :: Proxy r)
+
+        parsers = readP_r `HCons` (hMap (ReadComponent True) rs :: HList bs)
+
+
+
+
+
 -- --------------------------------------------------------------------------
 
 -- Extension
 
-instance HRLabelSet (Tagged l v ': r) 
-    => HExtend (Tagged (l :: k) v) (Record r) where
-  type HExtendR (Tagged l v) (Record r) = Record (Tagged l v ': r)
+instance HRLabelSet (t ': r)
+    => HExtend t (Record r) where
+  type HExtendR t (Record r) = Record (t ': r)
   f .*. (Record r) = mkRecord (HCons f r)
 
 
@@ -328,11 +514,11 @@
 
 -- Concatenation
 
-instance (HRLabelSet (HAppendList r1 r2), HAppend (HList r1) (HList r2))
+instance (HRLabelSet (HAppendListR r1 r2), HAppend (HList r1) (HList r2))
     => HAppend (Record r1) (Record r2) where
   hAppend (Record r) (Record r') = mkRecord (hAppend r r')
 
-type instance HAppendR (Record r1) (Record r2) = Record (HAppendList r1 r2)
+type instance HAppendR (Record r1) (Record r2) = Record (HAppendListR r1 r2)
 -- --------------------------------------------------------------------------
 
 -- Lookup
@@ -348,7 +534,7 @@
     hLookupByLabel:: Label l -> r -> v
 
 {- alternative "straightforward" implementation
-instance ( RecordLabels r ~ ls
+instance ( LabelsOf r ~ ls
          , HFind l ls n
          , HLookupByHNat n r
          , HLookupByHNatR n r ~ LVPair l v
@@ -359,14 +545,54 @@
         (LVPair v) = hLookupByHNat (proxy :: Proxy n) r
 -}
 
+{- | a version of 'HasField' / 'hLookupByLabel' / '.!.' that
+returns a default value when the label is not in the record:
 
+>>> let r = x .=. "the x value" .*. emptyRecord
 
-instance (HEq l l1 b, HasField' b l (Tagged l1 v1 ': r) v)
+>>> hLookupByLabelM y r ()
+()
+
+>>> hLookupByLabelM x r ()
+"the x value"
+
+
+
+-}
+class HasFieldM (l :: k) r (v :: Maybe *) | l r -> v where
+    hLookupByLabelM :: Label l
+          -> r -- ^ Record (or Variant,TIP,TIC)
+          -> t -- ^ default value
+          -> DemoteMaybe t v
+
+type family DemoteMaybe (d :: *) (v :: Maybe *) :: *
+type instance DemoteMaybe d (Just a) = a
+type instance DemoteMaybe d Nothing = d
+
+class HasFieldM1 (b :: Maybe [*]) (l :: k) r v | b l r -> v where
+    hLookupByLabelM1 :: Proxy b -> Label l -> r -> t -> DemoteMaybe t v
+
+instance (HMemberM (Label l) (LabelsOf xs) b,
+          HasFieldM1 b l (r xs) v)  => HasFieldM l (r xs) v where
+    hLookupByLabelM = hLookupByLabelM1 (Proxy :: Proxy b)
+
+instance HasFieldM1 Nothing l r Nothing where
+    hLookupByLabelM1 _ _ _ t = t
+
+instance HasField l r v => HasFieldM1 (Just b) l r (Just v) where
+    hLookupByLabelM1 _ l r _t = hLookupByLabel l r
+
+
+
+instance (HEqK l l1 b, HasField' b l (Tagged l1 v1 ': r) v)
     => HasField l (Record (Tagged l1 v1 ': r)) v where
     hLookupByLabel l (Record r) =
              hLookupByLabel' (Proxy::Proxy b) l r
 
+instance Fail (FieldNotFound l) => HasField l (Record '[]) (FieldNotFound l) where
+    hLookupByLabel _ _ = error "Data.HList.Record.HasField: Fail instances should not exist"
 
+
 class HasField' (b::Bool) (l :: k) (r::[*]) v | b l r -> v where
     hLookupByLabel':: Proxy b -> Label l -> HList r -> v
 
@@ -417,11 +643,10 @@
 
 -- Delete
 
-hDeleteAtLabel :: forall l t t1 t2. 
-   (H2ProjectByLabels '[l] t t1 t2) =>
-   Label l -> Record t -> Record t2
-hDeleteAtLabel _ (Record r) = 
-  Record $ snd $ h2projectByLabels (Proxy::Proxy '[l]) r
+instance (H2ProjectByLabels '[Label l] v t1 v')
+      => HDeleteAtLabel Record l v v' where
+  hDeleteAtLabel _ (Record r) =
+    Record $ snd $ h2projectByLabels (Proxy::Proxy '[Label l]) r
 
 infixl 2 .-.
 {-|
@@ -446,8 +671,8 @@
   >         .-. label1
 
 -}
-(.-.) :: (H2ProjectByLabels '[l] r _r' r') =>
-    Record r -> Label l -> Record r'
+(.-.) :: (HDeleteAtLabel r l xs xs') =>
+    r xs -> Label l -> r xs'
 r .-. l =  hDeleteAtLabel l r
 
 
@@ -456,12 +681,53 @@
 -- Update
 
 -- | 'hUpdateAtLabel' @label value record@
-hUpdateAtLabel :: forall (r :: [*]) (l :: k) (n::HNat) (v :: *). 
-  (HFind l (RecordLabels r) n, HUpdateAtHNat n (Tagged l v) r) =>
-  Label l -> v -> Record r -> Record (HUpdateAtHNatR n (Tagged l v) r)
-hUpdateAtLabel l v (Record r) = 
+
+class
+    HUpdateAtLabel record (l :: k) (v :: *) (r :: [*]) (r' :: [*])
+          | l v r -> r', l r' -> v where
+    hUpdateAtLabel :: SameLength r r' => Label l -> v -> record r -> record r'
+
+instance (HUpdateAtLabel2 l v r r',
+        HasField l (Record r') v) =>
+        HUpdateAtLabel Record l v r r' where
+    hUpdateAtLabel = hUpdateAtLabel2
+
+{- alternative impl which reports a Fail constraint that is too long (the
+one from HUpdateAtHNat) on ghc 7.10 RC1
+
+instance (HasField l (Record r') v,
+          HFindLabel l r n,
+          HUpdateAtHNat n (Tagged l v) r,
+          HUpdateAtHNatR n (Tagged l v) r ~ r',
+          SameLength r r') =>
+  HUpdateAtLabel Record l v r r' where
+  hUpdateAtLabel l v (Record r) =
     Record (hUpdateAtHNat (Proxy::Proxy n) (newLVPair l v) r)
+-}
 
+class HUpdateAtLabel2 (l :: k) (v :: *) (r :: [*]) (r' :: [*])
+        | l r v -> r' where
+    hUpdateAtLabel2 :: Label l -> v -> Record r -> Record r'
+
+class HUpdateAtLabel1 (b :: Bool) (l :: k) (v :: *) (r :: [*]) (r' :: [*])
+        | b l v r -> r' where
+    hUpdateAtLabel1 :: Proxy b -> Label l -> v -> Record r -> Record r'
+
+instance HUpdateAtLabel1 True l v (Tagged l e ': xs) (Tagged l v ': xs) where
+    hUpdateAtLabel1 _b _l v (Record (e `HCons` xs)) = Record (e{ unTagged = v } `HCons` xs)
+
+instance HUpdateAtLabel2 l v xs xs' => HUpdateAtLabel1 False l v (x ': xs) (x ': xs') where
+    hUpdateAtLabel1 _b l v (Record (x `HCons` xs)) = case hUpdateAtLabel2 l v (Record xs) of
+        Record xs' -> Record (x `HCons` xs')
+
+instance (HEqK l l' b, HUpdateAtLabel1 b l v (Tagged l' e ': xs) xs')
+    => HUpdateAtLabel2 l v (Tagged l' e ': xs) xs' where
+    hUpdateAtLabel2 = hUpdateAtLabel1 (Proxy :: Proxy b)
+
+instance Fail (FieldNotFound l) => HUpdateAtLabel2 l v '[] '[] where
+    hUpdateAtLabel2 _ _ r = r
+
+
 infixr 2 .@.
 {-|
 
@@ -485,24 +751,58 @@
 
 
 -- | @hProjectByLabels ls r@ returns @r@ with only the labels in @ls@ remaining
-hProjectByLabels :: (HRLabelSet a, H2ProjectByLabels ls t a b) => 
-	Proxy ls -> Record t -> Record a
+hProjectByLabels :: (HRLabelSet a, H2ProjectByLabels ls t a b) =>
+        proxy ls -> Record t -> Record a
 hProjectByLabels ls (Record r) = mkRecord (fst $ h2projectByLabels ls r)
 
 -- | See 'H2ProjectByLabels'
-hProjectByLabels2 :: 
+hProjectByLabels2 ::
     (H2ProjectByLabels ls t t1 t2, HRLabelSet t1, HRLabelSet t2) =>
     Proxy ls -> Record t -> (Record t1, Record t2)
 hProjectByLabels2 ls (Record r) = (mkRecord rin, mkRecord rout)
    where (rin,rout) = h2projectByLabels ls r
 
+-- need to rearrange because the ordering in the result is determined by
+-- the ordering in the original record, not the ordering in the proxy. In
+-- other words,
+--
+-- > hProjectByLabels (Proxy :: Proxy ["x","y"]) r == hProjectByLabels (Proxy :: Proxy ["y","x"]) r
+hProjectByLabels' r =
+    let r' = hRearrange' (hProjectByLabels (labelsOf r') r)
+    in r'
+
+
+
+{- | A helper to make the Proxy needed by hProjectByLabels,
+and similar functions which accept a list of kind [*].
+
+For example:
+
+@(rin,rout) = 'hProjectByLabels2' (Proxy :: Labels ["x","y"]) r@
+
+behaves like
+
+> rin = r .!. (Label :: Label "x") .*.
+>       r .!. (Label :: Label "y") .*.
+>       emptyRecord
+>
+> rout = r .-. (Label :: Label "x") .-. (Label :: Label "y")
+
+-}
+type family Labels (xs :: [k]) :: *
+type instance Labels xs = Proxy (Labels1 xs)
+
+type family Labels1 (xs :: [k]) :: [*]
+type instance Labels1 '[] = '[]
+type instance Labels1 (x ': xs) = Label x ': Labels1 xs
+
 -- | /Invariant/:
 --
 --  > r === rin `disjoint-union` rout
 --  > labels rin === ls
 --  >     where (rin,rout) = hProjectByLabels ls r
-class H2ProjectByLabels (ls::[k]) r rin rout | ls r -> rin rout where
-    h2projectByLabels :: Proxy ls -> HList r -> (HList rin,HList rout)
+class H2ProjectByLabels (ls::[*]) r rin rout | ls r -> rin rout where
+    h2projectByLabels :: proxy ls -> HList r -> (HList rin,HList rout)
 
 instance H2ProjectByLabels '[] r '[] r where
     h2projectByLabels _ r = (HNil,r)
@@ -510,15 +810,15 @@
 instance H2ProjectByLabels (l ': ls) '[] '[] '[] where
     h2projectByLabels _ _ = (HNil,HNil)
 
-instance (HMemberM l1 ((l::k) ': ls) (b :: Maybe [k]),
+instance (HMemberM (Label l1) ((l :: *) ': ls) (b :: Maybe [*]),
           H2ProjectByLabels' b (l ': ls) (Tagged l1 v1 ': r1) rin rout)
     => H2ProjectByLabels (l ': ls) (Tagged l1 v1 ': r1) rin rout where
     h2projectByLabels = h2projectByLabels' (Proxy::Proxy b)
 
-class H2ProjectByLabels' (b::Maybe [k]) (ls::[k]) r rin rout 
+class H2ProjectByLabels' (b::Maybe [*]) (ls::[*]) r rin rout
                          | b ls r -> rin rout where
-    h2projectByLabels' :: Proxy b -> Proxy ls -> 
-				     HList r -> (HList rin,HList rout)
+    h2projectByLabels' :: Proxy b -> proxy ls ->
+                                     HList r -> (HList rin,HList rout)
 
 instance H2ProjectByLabels ls1 r rin rout =>
     H2ProjectByLabels' ('Just ls1) ls (f ': r) (f ': rin) rout where
@@ -546,12 +846,11 @@
 
 -- --------------------------------------------------------------------------
 
+type HTPupdateAtLabel record l v r = (HUpdateAtLabel record l v r r, SameLength' r r)
+
 -- | A variation on 'hUpdateAtLabel': type-preserving update.
+hTPupdateAtLabel :: HTPupdateAtLabel record l v r => Label l -> v -> record r -> record r
 hTPupdateAtLabel l v r = hUpdateAtLabel l v r
- where
-   _te :: a -> a -> ()
-   _te _ _ = ()
-   _ = _te v (hLookupByLabel l r)
 
 {- ^
 
@@ -574,40 +873,41 @@
 -- --------------------------------------------------------------------------
 -- | Subtyping for records
 
-instance H2ProjectByLabels (RecordLabels r2) r1 r2 rout
+instance H2ProjectByLabels (LabelsOf r2) r1 r2 rout
     => SubType (Record r1) (Record r2)
 
 
+type HMemberLabel l r b = HMember l (UnLabel l (LabelsOf r)) b
+
 -- --------------------------------------------------------------------------
 
 -- Left Union
 
-class  HLeftUnion r r' r'' | r r' -> r''
- where hLeftUnion :: Record r -> Record r' -> Record r''
+class HDeleteLabels ks r r' | ks r -> r'
+ where hDeleteLabels :: proxy (ks :: [*]) -- ^ as provided by labelsOf
+          -> Record r -> Record r'
 
-instance HLeftUnion r '[] r
- where   hLeftUnion r _ = r
+instance (HMember (Label l) ks b,
+          HCond b (Record r2) (Record (Tagged l v ': r2)) (Record r3),
+          HDeleteLabels ks r1 r2) =>
+    HDeleteLabels ks (Tagged l v ': r1) r3 where
+      hDeleteLabels ks (Record (HCons lv r1)) =
+          case hDeleteLabels ks (Record r1) of
+             Record r2 -> hCond (Proxy :: Proxy b)
+                  (Record r2)
+                  (Record (HCons lv r2))
+instance HDeleteLabels ks '[] '[] where
+    hDeleteLabels _ _ = emptyRecord
 
-instance ( RecordLabels r ~ ls
-         , HMember l ls b
-         , HLeftUnionBool b r (Tagged l v) r'''
-         , HLeftUnion r''' r' r''
-         )
-           => HLeftUnion r (Tagged l v ': r') r''
-  where
-   hLeftUnion r (Record (HCons f r')) = r''
-    where
-     r'''    = hLeftUnionBool (Proxy :: Proxy b) r f
-     r''     = hLeftUnion (r''' :: Record r''') (Record r' :: Record r')
 
-class  HLeftUnionBool (b :: Bool) r f r' | b r f -> r'
- where hLeftUnionBool :: Proxy b -> Record r -> f -> Record r'
+class  HLeftUnion r r' r'' | r r' -> r''
+ where hLeftUnion :: Record r -> Record r' -> Record r''
 
-instance HLeftUnionBool True r f r
-   where hLeftUnionBool _ r _  = r
+instance (HDeleteLabels (LabelsOf l) r r',
+         HAppend (Record l) (Record r'),
+         HAppendR (Record l) (Record r') ~ (Record lr)) => HLeftUnion l r lr
+ where  hLeftUnion l r = l `hAppend` hDeleteLabels (labelsOf l) r
 
-instance HLeftUnionBool False r f (f ': r)
-   where hLeftUnionBool _ (Record r) f = Record (HCons f r)
 
 infixl 1 .<++.
 {-|
@@ -651,11 +951,10 @@
 class UnionSymRec r1 r2 ru | r1 r2 -> ru where
     unionSR :: Record r1 -> Record r2 -> (Record ru, Record ru)
 
-instance UnionSymRec r1 '[] r1 where
+instance (r1 ~ r1') => UnionSymRec r1 '[] r1' where
     unionSR r1 _ = (r1, r1)
 
-instance ( RecordLabels r1 ~ ls
-         , HMember l ls b
+instance ( HMemberLabel l r1 b
          , UnionSymRec' b r1 (Tagged l v) r2' ru
          )
     => UnionSymRec r1 (Tagged l v ': r2') ru
@@ -668,29 +967,25 @@
 
 
 
-{-
 -- | Field f2 is already in r1, so it will be in the union of r1
 -- with the rest of r2.
 --
 -- To inject (HCons f2 r2) in that union, we should replace the
 -- field f2
--}
 instance (UnionSymRec r1 r2' ru,
-          HasField l2 (Record ru) v2,
-          HUpdateAtHNat n (Tagged l2 v2) ru,
-          ru ~ HUpdateAtHNatR n (Tagged l2 v2) ru,
-          RecordLabels ru ~ ls,
-          f2 ~ Tagged l2 v2,
-          HFind l2 ls n)
+          HTPupdateAtLabel Record l2 v2 ru,
+          f2 ~ Tagged l2 v2)
     => UnionSymRec' True r1 f2 r2' ru where
     unionSR' _ r1 (Tagged v2) r2' =
        case unionSR r1 r2'
         of (ul,ur) -> (ul, hTPupdateAtLabel (Label :: Label l2) v2 ur)
 
 
+
 instance (UnionSymRec r1 r2' ru,
           HExtend f2 (Record ru),
-          HExtendR f2 (Record ru) ~ Record f2ru)
+          Record f2ru ~ HExtendR f2 (Record ru)
+        )
     => UnionSymRec' False r1 f2 r2' f2ru where
     unionSR' _ r1 f2 r2' = (ul', ur')
        where (ul,ur) = unionSR r1 r2'
@@ -699,55 +994,335 @@
 
 -- --------------------------------------------------------------------------
 -- | Rearranges a record by labels. Returns the record r, rearranged such that
--- the labels are in the order given by ls. (recordLabels r) must be a
+-- the labels are in the order given by ls. (LabelsOf r) must be a
 -- permutation of ls.
-hRearrange :: (HLabelSet ls, HRearrange ls r (HList r')) => Proxy ls -> Record r -> Record r'
+hRearrange :: (HLabelSet ls, HRearrange ls r r') => Proxy ls -> Record r -> Record r'
 hRearrange ls (Record r) = Record (hRearrange2 ls r)
 
+{- | 'hRearrange'' is 'hRearrange' where ordering specified by the Proxy
+argument is determined by the result type.
+
+With built-in haskell records, these @e1@ and @e2@ have the same type:
+
+> data R = R { x, y :: Int }
+> e1 = R{ x = 1, y = 2}
+> e2 = R{ y = 2, x = 1}
+
+'hRearrange'' can be used to allow either ordering to be accepted:
+
+> h1, h2 :: Record [ Tagged "x" Int, Tagged "y" Int ]
+> h1 = hRearrange' $
+>     x .=. 1 .*.
+>     y .=. 2 .*.
+>     emptyRecord
+>
+> h2 = hRearrange' $
+>     y .=. 2 .*.
+>     x .=. 1 .*.
+>     emptyRecord
+
+-}
+hRearrange' r =
+    let r' = hRearrange (labelsOf r') r
+    in r'
+
+
+class Rearranged r s t a b where
+    -- @Iso (r s) (r t) (r a) (r b)@
+    rearranged :: (Profunctor p, Functor f) => r a `p` f (r b) -> r s `p` f (r t)
+
+
+{- | @Iso (Record s) (Record t) (Record a) (Record b)@
+
+where @s@ is a permutation of @a@, @b@ is a permutation of @t@.
+In practice 'sameLabels' and 'sameLength' are likely needed on both
+sides of @rearranged@, to avoid ambiguous types.
+
+An alternative implementation:
+
+> rearranged x = iso hRearrange' hRearrange' x
+
+-}
+instance (la ~ LabelsOf a, lt ~ LabelsOf t,
+          HRearrange la s a,
+          HRearrange lt b t,
+          HLabelSet la,
+          HLabelSet lt)
+  => Rearranged Record s t a b where
+    rearranged = iso (hRearrange (Proxy :: Proxy la))
+                     (hRearrange (Proxy :: Proxy lt))
+
+{- | @Iso' (r s) (r a)@
+
+where @s@ is a permutation of @a@ -}
+rearranged' x = simple (rearranged (simple x))
+
 -- | Helper class for 'hRearrange'
-class HRearrange ls r r' where
-    hRearrange2 :: Proxy ls -> HList r -> r'
+class (HRearrange3 ls r r', LabelsOf r' ~ ls,
+       SameLength ls r, SameLength r r')
+      => HRearrange (ls :: [*]) r r' | ls r -> r', r' -> ls where
+    hRearrange2 :: proxy ls -> HList r -> HList r'
 
-instance (HList '[] ~ r) => HRearrange '[] '[] r where
-   hRearrange2 _ _ = HNil
 
+instance (HRearrange3 ls r r', LabelsOf r' ~ ls,
+        SameLength ls r, SameLength r r') => HRearrange ls r r' where
+    hRearrange2 = hRearrange3
+
+-- | same as HRearrange, except no backwards FD
+class HRearrange3 (ls :: [*]) r r' | ls r -> r' where
+    hRearrange3 :: proxy ls -> HList r -> HList r'
+
+instance HRearrange3 '[] '[] '[] where
+   hRearrange3 _ _ = HNil
+
 instance (H2ProjectByLabels '[l] r rin rout,
-          HRearrange' l ls rin rout (HList r'),
-          r'' ~ HList r') =>
-        HRearrange (l ': ls) r r'' where
-   hRearrange2 _ r = hRearrange2' (Proxy :: Proxy l) (Proxy :: Proxy ls) rin rout
+          HRearrange4 l ls rin rout r',
+          l ~ Label ll) =>
+        HRearrange3 (l ': ls) r r' where
+   hRearrange3 _ r = hRearrange4 (Proxy :: Proxy l) (Proxy :: Proxy ls) rin rout
       where (rin, rout) = h2projectByLabels (Proxy :: Proxy '[l]) r
 
 
 -- | Helper class 2 for 'hRearrange'
-class HRearrange' l ls rin rout r' where
-    hRearrange2' :: Proxy l -> Proxy ls -> HList rin -> HList rout -> r'
- 
-instance (HRearrange ls rout (HList r'),
-         r'' ~ HList (Tagged l v ': r')) =>
-        HRearrange' l ls '[Tagged l v] rout r'' where
-   hRearrange2' _ ls (HCons lv@(Tagged v) _HNil) rout
-        = HCons (Tagged v `asTypeOf` lv) (hRearrange2 ls rout)
+class HRearrange4 (l :: *) (ls :: [*]) rin rout r' | l ls rin rout -> r' where
+    hRearrange4 :: proxy l -> Proxy ls -> HList rin -> HList rout -> HList r'
 
-data ExtraField l = ExtraField
-data FieldNotFound l = FieldNotFound
+instance (HRearrange3 ls rout r',
+         r'' ~ (Tagged l v ': r'),
+         ll ~ Label l) =>
+        HRearrange4 ll ls '[Tagged l v] rout r'' where
+   hRearrange4 _ ls (HCons lv@(Tagged v) _HNil) rout
+        = HCons (Tagged v `asTypeOf` lv) (hRearrange3 ls rout)
 
 -- | For improved error messages
-instance Fail (FieldNotFound l) => 
-        HRearrange' l ls '[] rout (FieldNotFound l) where
-   hRearrange2' _ _ _ _ = FieldNotFound
+instance Fail (FieldNotFound l) =>
+        HRearrange4 l ls '[] rout '[] where
+   hRearrange4 _ _ _ _ = error "Fail has no instances"
 
 -- | For improved error messages
-instance Fail (ExtraField l) => 
-          HRearrange '[] (Tagged l v ': a) (ExtraField l) where
-   hRearrange2 _ _ = ExtraField
+instance Fail (ExtraField l) =>
+          HRearrange3 '[] (Tagged l v ': a) '[] where
+   hRearrange3 _ _ = error "Fail has no instances"
 
 
 -- --------------------------------------------------------------------------
 -- $lens
--- Lens-based setters/getters are popular.
+-- Lens-based setters/getters are popular. hLens packages up
+-- 'hUpdateAtLabel' and 'hLookupByLabel'.
 --
--- This is a provisional method to make a @Lens (Record s) (Record t) a b@,
--- out of a 'Label' @x@. Refer to @examples/lens.hs@ for an example.
-hLens lab f rec = fmap (\v -> hUpdateAtLabel lab v rec) (f (rec .!. lab)) 
+-- Refer to @examples/lens.hs@ and @examples/labelable.hs@ for examples.
 
+-- | constraints needed to implement 'HLens'
+type HLensCxt x r s t a b =
+    (HasField x (r s) a,
+     HUpdateAtLabel r x b s t,
+     HasField x (r t) b,
+     HUpdateAtLabel r x a t s,
+     SameLength s t,
+     SameLabels s t)
+
+class HLensCxt x r s t a b => HLens x r s t a b
+        | x s b -> t, x t a -> s, -- need to repeat fundeps implied by HLensCxt
+          x s -> a, x t -> b where
+    -- | @hLens :: Label x -> Lens (r s) (r t) a b@
+    hLens :: Label x -> (forall f. Functor f => (a -> f b) -> (r s -> f (r t)))
+
+instance HLensCxt r x s t a b => HLens r x s t a b where
+  hLens lab f rec = fmap (\v -> hUpdateAtLabel lab v rec) (f (rec .!. lab))
+
+
+{- | map over the values of a record. This is a shortcut for
+
+  > \ f (Record a) -> Record (hMap (HFmap f) a)
+
+[@Example@]
+
+suppose we have a function that should be applied to every element
+of a record:
+>>> let circSucc_ x | x == maxBound = minBound | otherwise = succ x
+
+> :t circSucc_
+circSucc_ :: (Eq a, Enum a, Bounded a) => a -> a
+
+Use a shortcut ('Fun') to create a value that has an appropriate 'ApplyAB' instance:
+>>> let circSucc = Fun circSucc_ :: Fun '[Eq,Enum,Bounded] '()
+
+Confirm that we got Fun right:
+>>> :t applyAB circSucc
+applyAB circSucc :: (Bounded b, Enum b, Eq b) => b -> b
+
+>>> applyAB circSucc True
+False
+
+define the actual record:
+>>> let r = x .=. 'a' .*. y .=. False .*. emptyRecord
+>>> r
+Record{x='a',y=False}
+
+>>> hMapR circSucc r
+Record{x='b',y=True}
+
+-}
+hMapR f r = applyAB (HMapR f) r
+
+newtype HMapR f = HMapR f
+
+instance (HMapCxt Record f x y, rx ~ Record x, ry ~ Record y)
+      => ApplyAB (HMapR f) rx ry where
+        applyAB (HMapR f) = hMapAux f
+
+instance HMapAux HList (HFmap f) x y =>
+    HMapAux Record f x y where
+      hMapAux f (Record x) = Record (hMapAux (HFmap f) x)
+
+
+
+-- --------------------------------------------------------------------------
+-- | This instance allows creating Record with
+--
+-- @hBuild 3 'a' :: Record '[Tagged "x" Int, Tagged "y" Char]@
+instance (HReverse l lRev,
+         HMapTaggedFn lRev l') => HBuild' l (Record l') where
+  hBuild' l = hMapTaggedFn (hReverse l)
+
+-- | serves the same purpose as 'hEnd'
+hEndR :: Record a -> Record a
+hEndR = id
+
+
+-- | see 'hEndP'
+instance (HRevAppR l '[] ~ lRev,
+          HExtendRs lRev (Proxy ('[] :: [*])) ~ Proxy l1,
+          l' ~ l1) => HBuild' l (Proxy l') where
+  hBuild' _ = Proxy
+
+{- | @'hEndP' $ 'hBuild' label1 label2@
+
+is one way to make a Proxy of labels (for use with 'asLabelsOf'
+for example). Another way is
+
+@label1 .*. label2 .*. 'emptyProxy'@
+
+-}
+hEndP :: Proxy (xs :: [k]) -> Proxy xs
+hEndP = id
+
+type family HExtendRs (ls :: [*]) (z :: k) :: k
+type instance HExtendRs (l ': ls) z = HExtendR l (HExtendRs ls z)
+type instance HExtendRs '[] z = z
+
+-- --------------------------------------------------------------------------
+
+{- |
+
+>>> let x :: Record '[Tagged "x" Int]; x = undefined
+>>> let y :: Record '[Tagged "x" Char]; y = undefined
+>>> :t hZip x y
+hZip x y :: Record '[Tagged "x" (Int, Char)]
+
+-}
+instance (HZipRecord x y xy, SameLengths [x,y,xy])
+      => HZip Record x y xy where
+    hZip = hZipRecord
+
+instance (HZipRecord x y xy, SameLengths [x,y,xy])
+      => HUnzip Record x y xy where
+    hUnzip = hUnzipRecord
+
+
+instance (RecordValuesR lvs ~ vs,
+          SameLabels ls lvs,
+          LabelsOf lvs ~ ls,
+          SameLengths [ls,vs,lvs],
+          HAllTaggedLV lvs)
+    => HUnzip Proxy ls vs lvs where
+    hUnzip _ = (Proxy, Proxy)
+
+instance HUnzip Proxy ls vs lvs
+      => HZip Proxy ls vs lvs where
+  hZip _ _ = Proxy
+
+
+{- | a variation on 'hZip' for 'Proxy', where
+the list of labels does not have to include Label
+(as in @ts'@)
+
+>>> let ts = Proxy :: Proxy ["x","y"]
+>>> let ts' = Proxy :: Proxy [Label "x",Label "y"]
+>>> let vs = Proxy :: Proxy [Int,Char]
+
+>>> :t zipTagged ts Proxy
+zipTagged ts Proxy :: Proxy '[Tagged "x" y, Tagged "y" y1]
+
+>>> :t zipTagged ts vs
+zipTagged ts vs :: Proxy '[Tagged "x" Int, Tagged "y" Char]
+
+
+And and the case when hZip does the same thing:
+
+>>> :t zipTagged ts' vs
+zipTagged ts' vs :: Proxy '[Tagged "x" Int, Tagged "y" Char]
+
+>>> :t hZip ts' vs
+hZip ts' vs :: Proxy '[Tagged "x" Int, Tagged "y" Char]
+
+-}
+zipTagged :: (MapLabel ts ~ lts,
+              HZip Proxy lts vs tvs)
+      => Proxy ts -> proxy vs -> Proxy tvs
+zipTagged _ _ = Proxy
+
+
+
+class HZipRecord x y xy | x y -> xy, xy -> x y where
+    hZipRecord :: Record x -> Record y -> Record xy
+    hUnzipRecord :: Record xy -> (Record x,Record y)
+
+
+instance HZipRecord '[] '[] '[] where
+    hZipRecord _ _ = emptyRecord
+    hUnzipRecord _ = (emptyRecord, emptyRecord)
+
+instance HZipRecord as bs abss
+       => HZipRecord (Tagged x a ': as) (Tagged x b ': bs) (Tagged x (a,b) ': abss) where
+    hZipRecord (Record (Tagged a `HCons` as)) (Record (Tagged b `HCons` bs)) =
+        let Record abss = hZipRecord (Record as) (Record bs)
+        in Record (Tagged (a,b) `HCons` abss)
+    hUnzipRecord (Record (Tagged (a,b) `HCons` abss)) =
+        let (Record as, Record bs) = hUnzipRecord (Record abss)
+        in (Record (Tagged a `HCons` as), Record (Tagged b `HCons` bs))
+
+
+-- | instead of explicit recursion above, we could define HZipRecord in
+-- terms of 'HZipList'. While all types are inferred, this implementation
+-- is probably slower, so explicit recursion is used in the 'HZip' 'Record'
+-- instance.
+hZipRecord2 x y = hMapTaggedFn (hZipList (recordValues x) (recordValues y))
+        `asLabelsOf` x `asLabelsOf` y
+
+hUnzipRecord2 xy = let (x,y) = hUnzipList (recordValues xy)
+                 in (hMapTaggedFn x `asLabelsOf` xy, hMapTaggedFn y `asLabelsOf` xy)
+
+
+{- | similar to 'asTypeOf':
+
+>>> let s0 = Proxy :: Proxy '["x", "y"]
+>>> let s1 = Proxy :: Proxy '[Label "x", Label "y"]
+>>> let s2 = Proxy :: Proxy '[Tagged "x" Int, Tagged "y" Char]
+
+>>> let f0 r = () where _ = r `asLabelsOf` s0
+>>> let f1 r = () where _ = r `asLabelsOf` s1
+>>> let f2 r = () where _ = r `asLabelsOf` s2
+
+>>> :t f0
+f0 :: r '[Tagged "x" v, Tagged "y" v1] -> ()
+
+>>> :t f1
+f1 :: r '[Tagged "x" v, Tagged "y" v1] -> ()
+
+>>> :t f2
+f2 :: r '[Tagged "x" v, Tagged "y" v1] -> ()
+
+-}
+asLabelsOf :: (HAllTaggedLV x, SameLabels x y, SameLength x y) => r x -> s y -> r x
+asLabelsOf = const
diff --git a/Data/HList/RecordPuns.hs b/Data/HList/RecordPuns.hs
--- a/Data/HList/RecordPuns.hs
+++ b/Data/HList/RecordPuns.hs
@@ -62,41 +62,69 @@
 restricts the fields in the record supplied to be exactly the ones
 provided. In other words
 
-> [pun| (x y) |] = list
+> [pun| (x _ y{}) |] = list
 > -- desugars to something like:
-> Record (HCons (Tagged x :: Tagged "x" s1)
->              (HCons (Tagged x :: Tagged "y" s2)
->               HNil)) = list
+> Record ((Tagged x :: Tagged "x" s1) `HCons`
+>         (Tagged _ :: Tagged t   s2) `HCons`
+>         (Tagged _ :: Tagged "y" s3) `HCons`
+>          HNil) = list
 
 Where the @s1@ and @s2@ are allowed to fit whatever is in the HList.
+Note that this also introduces the familiar wild card pattern (@_@),
+and shows again how to ensure a label is present but not bind a variable
+to it.
 
-See also @examples/pun.hs@.
+See also @examples/pun.hs@. In @{}@ patterns, @pun@ can work with
+'Variant' too.
+
 -}
 
 
--- | requires the use of "Data.HList.Label6" (ie. the label for foo is @Label :: Label \"foo\"@)
+-- | requires labels to be promoted strings (kind Symbol), as provided by
+-- "Data.HList.Label6" (ie. the label for foo is @Label :: Label \"foo\"@),
+-- or "Data.HList.Labelable"
 pun :: QuasiQuoter
 pun = QuasiQuoter {
-    quotePat = mp . parseRec,
-    quoteExp = me . parseRec,
+    quotePat = suppressWarning mp . parseRec,
+    quoteExp = suppressWarning me . parseRec,
     quoteDec  = error "Data.HList.RecordPuns.quoteDec",
     quoteType = error "Data.HList.RecordPuns.quoteType"
  }
 
 
+-- | the warning about @implicit {} added@ doesn't
+-- make sense at top level (but it does if you say
+-- have  [pun| x @ y |]
+suppressWarning f (V a) = f (C [V a])
+suppressWarning f x = f x
+
 -- like  \x -> (x .!. x1, x .!. x2)
 extracts xs = do
     record <- newName "record"
-    lamE [varP record]
-        (tupE
+    let val = tupE
             [ [| $(varE record) .!. $label  |]
                 | x <- xs,
-                let label = [| Label :: Label $(litT (strTyLit x)) |]
-                ])
+                let label = [| Label :: Label $(litT (strTyLit x)) |],
+                x /= "_"
+                ]
 
-mkExp :: [String] -> ExpQ
-mkExp xs = foldr (\x acc -> [| $(mkPair x (dyn x)) .*. $acc |]) [| emptyRecord |] xs
+        -- constrain the type of the supplied record to have at least
+        -- as many elements as are extracted. In other words:
+        --
+        -- > f :: r (e1 ': e2 ': e3 ': e4 ': es) -> () -- is inferred
+        -- > f [pun| { _ _ _ _ } |] = ()
+        ensureLength = [| $(varE record) `asTypeOf` $(minLen xs) |]
 
+    lamE [varP record] [|  $val `const` $ensureLength |]
+
+
+-- | generates an @undefined :: r xs@, such that @xs :: [k]@ has
+-- at least as long as the input list
+minLen :: [t] -> ExpQ
+minLen [] = [| error "Data.HList.RecordPuns.minLen" :: r (es :: [*]) |]
+minLen (_ : xs) = [| (error "Data.HList.RecordPuns.minLen"
+                        :: r es -> r (e ': es)) $(minLen xs) |]
+
 mkPair :: String -> ExpQ -> ExpQ
 mkPair x xe = [| (Label :: Label $(litT (strTyLit x))) .=. $xe |]
 
@@ -119,20 +147,21 @@
                         show (map ppTree inp)
 
 mp :: Tree -> PatQ
-mp (C as) = case unzip (mps as) of
-    (a, b) -> viewP (extracts a) (tupP b)
+mp (C as) =
+    let extractPats = mps as
+        tupleP = tupP [ p | (binding, p) <- extractPats, binding /= "_" ]
+    in viewP (extracts (map fst extractPats)) tupleP
 
--- use of prime here (non GADT version) because it is better for type
--- inference. See commentary surrounding HCons' in Data.HList.HList
+
 mp (D as) = conP 'Record
-  [viewP (varE 'prime) -- nicer to have [p| prime -> $( ... ) |],
-                       -- but ghc-7.6 rejects that over types
-   (foldr ( \ (n,p) xs -> conP 'HCons'
-                [viewP [| \x -> x `asTypeOf`
-                            (undefined :: Tagged $(litT (strTyLit n)) t) |]
-                (conP 'Tagged [p]),
+  [(foldr ( \ (n,p) xs -> conP 'HCons
+                [ let ty
+                          | n == "_"  = [| undefined :: Tagged anyLabel t |]
+                          | otherwise = [| undefined :: Tagged $(litT (strTyLit n)) t |]
+                  in viewP [| \x -> x `asTypeOf` $ty |]
+                      (conP 'Tagged [p]),
                 xs])
-          (conP 'HNil' [])
+          (conP 'HNil [])
           (mps as))]
 mp a = do
     reportWarning $ "Data.HList.RecordPuns.mp implicit {} added around:" ++ show a
@@ -142,6 +171,7 @@
 mps (V a : V "@" : b : c) = (a, asP (mkName a) (mp b)) :  mps c
 mps (V a : C b : c) = (a, mp (C b)) : mps c
 mps (V a : D b : c) = (a, mp (D b)) : mps c
+mps (V "_" : b) = ("_", wildP) : mps b
 mps (V a : b) = (a, varP (mkName a)) : mps b
 mps [] = []
 mps inp = error $ "Data.HList.RecordPuns.mps: cannot translate remaining pattern:" ++
diff --git a/Data/HList/TIC.hs b/Data/HList/TIC.hs
--- a/Data/HList/TIC.hs
+++ b/Data/HList/TIC.hs
@@ -1,60 +1,200 @@
-{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE KindSignatures #-}
-{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE CPP #-}
 
 {- |
    The HList library
 
    (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
 
-   Type-indexed co-products.
+   Type-indexed co-products. The public interface is exposed
+   in <Data-HList-CommonMain.html#t:TIC CommonMain#TIC>
 
    (There are other ways: see ConsUnion.hs, for example)
 -}
 
 module Data.HList.TIC where
 
-import Data.Dynamic
+import Data.HList.TIP
+import Data.HList.FakePrelude
+import Data.HList.HListPrelude
 
+import Data.HList.Record
+import Data.HList.Variant
 import Data.HList.HList
-import Data.HList.TIP
 
+import Data.HList.HArray
 
+import Data.Array (Ix)
+
+import Text.ParserCombinators.ReadP
+import LensDefs
+
 -- --------------------------------------------------------------------------
--- | A datatype for type-indexed co-products
+-- | A datatype for type-indexed co-products. A 'TIC' is just a 'Variant',
+-- where the elements of the type-level list @\"l\"@ are in the form
+-- @Tagged x x@.
 
-newtype TIC (l :: [*]) = TIC Dynamic
+newtype TIC (l :: [*]) = TIC (Variant l)
 
+deriving instance Eq (Variant l) => Eq (TIC l)
+deriving instance Ord (Variant l) => Ord (TIC l)
+deriving instance Ix (Variant l) => Ix (TIC l)
+deriving instance Bounded (Variant l) => Bounded (TIC l)
+deriving instance Enum (Variant l) => Enum (TIC l)
+deriving instance Monoid (Variant l) => Monoid (TIC l)
 
+
+instance HMapAux Variant f xs ys => HMapAux TIC f xs ys where
+    hMapAux f (TIC a) = TIC (hMapAux f a)
+
+-- | @Iso (TIC s) (TIC t) (Variant s) (Variant t)@
+--
+-- 'typeIndexed' may be more appropriate
+ticVariant x = isoNewtype (\(TIC a) -> a) TIC x
+
+-- | @Iso' (TIC s) (Variant s)@
+ticVariant' x = simple (ticVariant x)
+
+
 -- --------------------------------------------------------------------------
+
+{- | Conversion between type indexed collections ('TIC' and 'TIP')
+and the corresponding collection that has other label types ('Variant'
+and 'Record' respectively)
+
+See 'typeIndexed''
+
+-}
+class TypeIndexed r tr | r -> tr, tr -> r where
+    -- | @Iso (r s) (r t) (tr a) (tr b)@
+    typeIndexed :: forall p f s t a b.
+       (TypeIndexedCxt s t a b, Profunctor p, Functor f) =>
+      p (tr (TagR a)) (f (tr (TagR b))) -> p (r s) (f (r t))
+
+type TypeIndexedCxt s t a b =
+ (HMapCxt HList TaggedFn b t,
+  RecordValues s, RecordValues t,
+  a ~ RecordValuesR s,
+  b ~ RecordValuesR t,
+  SameLabels s t,
+  SameLength s t,
+  SameLength b a,
+  {- to use castVariant instead of unsafeCastVariant
+  RecordValuesR (TagR a) ~ a,
+  RecordValuesR (TagR b) ~ b,
+  SameLength (TagR a) s,
+  SameLength (TagR b) t,
+  -}
+  Coercible (TagR b) t,
+  Coercible (TagR a) s,
+  HAllTaggedLV s,
+  HRLabelSet t,
+  TagUntag a,
+  TagUntag b)
+
+instance TypeIndexed Record TIP where
+    typeIndexed = sameLength . unlabeled . fromTipHList
+      where fromTipHList = iso (TIP . hTagSelf) (\(TIP a) -> hUntagSelf a)
+
+instance TypeIndexed Variant TIC where
+    typeIndexed = isoNewtype unsafeCastVariant unsafeCastVariant
+                . isoNewtype TIC (\(TIC a) -> a)
+
+{- |
+
+@'Iso'' ('Variant' s) ('TIC' a)@
+
+@'Iso'' ('Record' s) ('TIP' a)@
+
+where @s@ has a type like @'[Tagged \"x\" Int]@, and
+@a@ has a type like @'[Tagged Int Int]@.
+-}
+typeIndexed' x = simple (typeIndexed x)
+
+-- --------------------------------------------------------------------------
 -- | Public constructor (or, open union's injection function)
 
-mkTIC :: ( HTypeIndexed l
-         , HMember i l True
-         , Typeable i
+mkTIC' :: forall i l proxy.
+         ( HTypeIndexed l
+         , MkVariant i i l
          )
-      => i -> TIC l
+      => i
+      -> proxy l -- ^ the ordering of types in the @l :: [*]@ matters.
+                 -- This argument is intended to fix the ordering
+                 -- it can be a Record, Variant, TIP, Proxy
+      -> TIC l
 
-mkTIC i = TIC (toDyn i)
+mkTIC' i p = TIC (mkVariant (Label :: Label i) i p)
 
+-- | make a TIC that contains one element
+mkTIC1 :: forall i. MkVariant i i '[Tagged i i] => i -> TIC '[Tagged i i]
+mkTIC1 i = TIC (mkVariant1 (Label :: Label i) i)
 
+-- | make a TIC for use in contexts where the result type is fixed
+mkTIC i = mkTIC' i Proxy
+
+
 -- --------------------------------------------------------------------------
 -- | Public destructor (or, open union's projection function)
+instance HasField o (Variant l) (Maybe o) =>
+      HasField o (TIC l) (Maybe o) where
+    hLookupByLabel l (TIC i) = hLookupByLabel l i
 
-unTIC :: ( HTypeIndexed l
-         , HMember o l True
-         , Typeable o
-         )
-      => TIC l -> Maybe o
 
-unTIC (TIC i) = fromDynamic i
+instance (HasField o (TIC l) mo, mo ~ Maybe o) => HOccurs mo (TIC l) where
+    hOccurs = hLookupByLabel (Label :: Label o)
 
 
+-- | similar to 'HPrism'
+class TICPrism s t a b | s a b -> t, t a b -> s where
+  ticPrism :: (SameLength s t, Choice p, Applicative f)
+      => (a `p` f b) -> (TIC s `p` f (TIC t))
+
+instance (
+    MkVariant b b t,
+    HasField a (Variant s) (Maybe a),
+    SameLength s t,
+
+    HFindLabel b t n,
+    HFindLabel a s n,
+
+    HUpdateAtHNatR n (Tagged b b) s ~ t,
+    HUpdateAtHNatR n (Tagged a a) t ~ s
+
+    ) => TICPrism s t a b where
+  ticPrism = ticVariant . prism (\b -> mkVariant (Label :: Label b) b Proxy)
+      (\s -> case hLookupByLabel (Label :: Label a) s of
+          Just a -> Right a
+          Nothing -> Left (unsafeCastVariant s :: Variant t))
+
+-- | @Prism' (TIC s) a@
+ticPrism' :: forall s t a b. (HPrism a s t a b, a~b, s~t)
+  => (forall f p. (Applicative f, Choice p) => (a `p` f b) -> (TIC s `p` f (TIC t)))
+ticPrism' = ticVariant . hPrism (Label :: Label a)
+
+
 -- --------------------------------------------------------------------------
--- | TICs are opaque
+-- | TICs are not opaque
 
-instance Show (TIC l)
+instance ShowVariant l => Show (TIC l)
  where
-  show _ = "<Cannot show TIC content!>"
+  showsPrec _ (TIC v) = ("TIC{"++) . showVariant v . ('}':)
 
+
+instance (ReadVariant l, HAllTaggedEq l, HRLabelSet l) => Read (TIC l)
+ where
+   readsPrec _ = readP_to_S $ do
+     _ <- string "TIC{"
+     r <- readVariant
+     _ <- string "}"
+     return (TIC r)
+
+
+{- |
+> Nothing .*. x = x
+> Just a .*. y = mkTIC a
+-}
+instance (me ~ Maybe e, HOccursNot (Tagged e e) l)
+       => HExtend me (TIC l) where
+    type HExtendR me (TIC l) = TIC (Tagged (UnMaybe me) (UnMaybe me) ': l)
+    Just e .*. _ = TIC (unsafeMkVariant 0 e)
+    Nothing .*. TIC x = TIC (extendVariant x)
diff --git a/Data/HList/TIP.hs b/Data/HList/TIP.hs
--- a/Data/HList/TIP.hs
+++ b/Data/HList/TIP.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 
 {- |
    The HList library
@@ -5,26 +6,47 @@
    (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
 
    Type-indexed products.
+   The public interface is described in
+   <Data-HList-CommonMain.html#t:TIP CommonMain#TIP>
 -}
 
-module Data.HList.TIP where
+module Data.HList.TIP
+  (module Data.HList.TIPtuple,
+   module Data.HList.TIP) where
 
 
 import Data.HList.HListPrelude
+import Data.HList.FakePrelude
 import Data.HList.HList
-import Data.HList.HArray ()
-import Data.HList.HTypeIndexed
+import Data.HList.Record
+import Data.HList.HTypeIndexed ()
+import Data.HList.HOccurs
+import Data.HList.TIPtuple
+import Data.List (intercalate)
+import Data.Array (Ix)
 
-import Data.HList.TypeEqO () -- for doctest
+import LensDefs
 
 -- --------------------------------------------------------------------------
 -- * The newtype for type-indexed products
 
+-- | TIPs are like 'Record', except element \"i\" of the list \"l\"
+-- has type @Tagged e_i e_i@
 newtype TIP (l :: [*]) = TIP{unTIP:: HList l}
 
-instance Show (HList l) => Show (TIP l) where
-  show (TIP l) = "TIP" ++ show l
+deriving instance Monoid (HList a) => Monoid (TIP a)
+deriving instance Eq (HList a) => Eq (TIP a)
+deriving instance (Ord (HList r)) => Ord (TIP r)
+deriving instance (Ix (HList r)) => Ix (TIP r)
+deriving instance (Bounded (HList r)) => Bounded (TIP r)
 
+
+instance HMapOut (HShow `HComp` HUntag) l String => Show (TIP l) where
+  showsPrec _ (TIP l) = ("TIPH[" ++)
+                              . (intercalate "," (hMapOut (HShow `HComp` HUntag) l) ++)
+                              . (']' :)
+
+
 mkTIP :: HTypeIndexed l => HList l -> TIP l
 mkTIP = TIP
 
@@ -34,19 +56,29 @@
 -- --------------------------------------------------------------------------
 -- * Type-indexed type sequences
 
-class HTypeIndexed (l :: [*])
-instance HTypeIndexed '[]
-instance (HOccursNot e l,HTypeIndexed l) => HTypeIndexed (e ': l)
+-- | this constraint ensures that a TIP created by 'mkTIP' has no
+-- duplicates
+class (HAllTaggedEq l, HRLabelSet l) => HTypeIndexed (l :: [*])
 
+instance (HAllTaggedEq l, HRLabelSet l) => HTypeIndexed l
+
+class HAllTaggedEq (l :: [*])
+instance HAllTaggedEq '[]
+instance (HAllTaggedEq l, tee ~ Tagged e e') => HAllTaggedEq (tee ': l)
+
 -- --------------------------------------------------------------------------
 -- Implementing the HListPrelude interface
 
-instance (HOccursNot e l, HTypeIndexed l) => HExtend e (TIP l) 
+instance (HRLabelSet (Tagged e e ': l), HTypeIndexed l) => HExtend e (TIP l)
  where
-  type HExtendR e (TIP l) = TIP (e ': l)
-  e .*. TIP l = mkTIP (HCons e l)
+  type HExtendR e (TIP l) = TIP (Tagged e e ': l)
+  e .*. TIP l = mkTIP (HCons (Tagged e) l)
 
 
+
+instance (e ~ e', HasField e (Record l) e') => HasField e (TIP l) e' where
+    hLookupByLabel lab (TIP l) = hLookupByLabel lab (Record l)
+
 -- | One occurrence and nothing is left
 --
 -- This variation provides an extra feature for singleton lists.
@@ -54,43 +86,87 @@
 -- Hence the explicit provision of a result type can be omitted.
 --
 
-instance e' ~ e => HOccurs e' (TIP '[e]) where
-  hOccurs (TIP (HCons e' _)) = e'
+instance (tee ~ Tagged e e) => HOccurs e (TIP '[tee]) where
+  hOccurs (TIP (HCons (Tagged e) _)) = e
 
-instance HOccurs e (HList (x ': y ': l))
+instance HasField e (Record (x ': y ': l)) e
       => HOccurs e (TIP (x ': y ': l)) where
-  hOccurs (TIP l) = hOccurs l
-
-
-instance HOccursNot e l => HOccursNot e (TIP l)
+  hOccurs (TIP l) = Record l .!. (Label :: Label e)
 
 
-instance (HAppend (HList l) (HList l'), HTypeIndexed (HAppendList l l'))
+instance (HAppend (HList l) (HList l'), HTypeIndexed (HAppendListR l l'))
            => HAppend (TIP l) (TIP l')
  where
   hAppend (TIP l) (TIP l') = mkTIP (hAppend l l')
 
-type instance HAppendR (TIP l) (TIP l') = TIP (HAppendList l l')
+type instance HAppendR (TIP l) (TIP l') = TIP (HAppendListR l l')
 
--- instance HOccurrence e l l' => HOccurrence e (TIP l) l'
+
+-- instance HOccurrence HList e l l' => HOccurrence TIP e l l'
 --  where
---   hOccurrence e = hOccurrence e . unTIP
+--   hOccurrence e = TIP . hOccurrence e . unTIP
 
 -- --------------------------------------------------------------------------
 -- * Shielding type-indexed operations
 -- $note The absence of signatures is deliberate! They all must be inferred.
 
-onTIP f (TIP l) = mkTIP (f l)
+onRecord f (TIP l) = let Record l' = f (Record l) in mkTIP l'
 
-tipyDelete  p t  = onTIP (hDeleteAt p) t
-tipyUpdate  e t  = onTIP (hUpdateAt e) t
-tipyProject ps t = onTIP (hProjectBy ps) t
+instance (HDeleteAtLabel Record e v v',
+          HTypeIndexed v')
+      => HDeleteAtLabel TIP e v v' where
+  hDeleteAtLabel e v = onRecord (hDeleteAtLabel e) v
 
 
--- | Split produces two TIPs
-tipySplit ps (TIP l) = (mkTIP l',mkTIP l'')
+tipyUpdate  e t  = hTPupdateAtLabel (fromValue e) e t
+  where fromValue :: e -> Label e
+        fromValue _ = Label
+
+instance (HUpdateAtLabel Record e' e r r',
+          HTypeIndexed r',
+         e ~ e') => HUpdateAtLabel TIP e' e r r' where
+  hUpdateAtLabel l e r = onRecord (hUpdateAtLabel l e) r
+
+
+-- | Use 'Labels' to specify the first argument
+tipyProject ps t = onRecord (hProjectByLabels ps) t
+
+-- | provides a @Lens' (TIP s) a@. 'hLens'' @:: Label a -> Lens' (TIP s) a@
+-- is another option.
+#if __GLASGOW_HASKELL__ < 707
+tipyLens' x = simple (tipyLens x) -- rejected by GHC-7.10RC1
+#else
+tipyLens' f s = simple (hLens x f) (asTIP s) -- rejected by GHC-7.6.3
+  where
+    x = getA f
+    getA :: (a -> f a) -> Label a
+    getA _ = Label
+
+    asTIP :: TIP a -> TIP a
+    asTIP = id
+#endif
+
+{- | provides a @Lens (TIP s) (TIP t) a b@
+
+When using @set@ (also known as @.~@), 'tipyLens'' can address the
+ambiguity as to which field \"a\" should actually be updated.
+
+-}
+tipyLens f s = hLens x f (asTIP s)
+  where
+    x = getA f
+    getA :: (a -> f b) -> Label a
+    getA _ = Label
+
+    asTIP :: TIP a -> TIP a
+    asTIP = id
+
+
+-- | The same as 'tipyProject', except also return the
+-- types not requested in the @proxy@ argument
+tipyProject2 ps (TIP l) = (mkTIP l',mkTIP l'')
  where
-  (l',l'') = hSplitBy ps l
+  (l',l'') = h2projectByLabels ps l
 
 
 -- --------------------------------------------------------------------------
@@ -103,14 +179,271 @@
 
 
 -- --------------------------------------------------------------------------
+-- * conversion to and from 'HList'
 
+{- | 'TagR' can also be used to avoid redundancy when defining types for TIC and TIP.
+
+>  type XShort = TagR [A,B,C,D]
+
+>  type XLong = [Tagged A A, Tagged B B, Tagged C C, Tagged D D]
+
+
+an equivalent FD version, which is slightly better with respect to
+simplifying types containing type variables (in ghc-7.8 and 7.6):
+<http://stackoverflow.com/questions/24110410/>
+
+With ghc-7.10 (http://ghc.haskell.org/trac/ghc/ticket/10009) the FD version is superior
+to the TF version:
+
+@
+class (UntagR (TagR a) ~ a) => TagUntag a where
+    type TagR a :: [*]
+    hTagSelf :: HList a -> HList (TagR a)
+    hUntagSelf :: HList (TagR a) -> HList a
+
+instance TagUntag '[] where
+    type TagR '[] = '[]
+    hTagSelf _ = HNil
+    hUntagSelf _ = HNil
+
+instance TagUntag xs => TagUntag (x ': xs) where
+    type TagR (x ': xs) = Tagged x x ': TagR xs
+    hTagSelf (HCons x xs) = Tagged x `HCons` hTagSelf xs
+    hUntagSelf (HCons (Tagged x) xs) = x `HCons` hUntagSelf xs
+
+type family UntagR (xs :: [*]) :: [*]
+type instance UntagR '[] = '[]
+type instance UntagR (x ': xs) = Untag1 x ': UntagR xs
+@
+
+Length information should flow backwards
+
+>>> let len2 x = x `asTypeOf` (undefined :: HList '[a,b])
+>>> let f = len2 $ hTagSelf (hReplicate Proxy ())
+>>> :t f
+f :: HList '[Tagged () (), Tagged () ()]
+
+-}
+class SameLength a ta => TagUntagFD a ta | a -> ta, ta -> a where
+    hTagSelf :: HList a -> HList ta
+    hUntagSelf :: HList ta -> HList a
+
+instance TagUntagFD '[] '[] where
+    hTagSelf _ = HNil
+    hUntagSelf _ = HNil
+
+instance (TagUntagFD xs ys, txx ~ Tagged x x)
+      => TagUntagFD (x ': xs) (txx ': ys) where
+    hTagSelf (HCons x xs) = Tagged x `HCons` hTagSelf xs
+    hUntagSelf (HCons (Tagged x) xs) = x `HCons` hUntagSelf xs
+
+type TagUntag xs = TagUntagFD xs (TagR xs)
+
+-- | Sometimes the type variables available have @TagR@ already applied
+-- (ie the lists have elements like @Tagged X X@). Then this abbreviation
+-- is useful:
+type UntagTag xs = TagUntagFD (UntagR xs) xs
+
+type family TagR (a :: [*]) :: [*]
+type family UntagR (ta :: [*]) :: [*]
+
+type instance TagR '[] = '[]
+type instance UntagR '[] = '[]
+
+type instance TagR (x ': xs) = Tagged x x ': TagR xs
+type instance UntagR (Tagged y y ': ys) = y ': UntagR ys
+
+type family Untag1 (x :: *) :: *
+type instance Untag1 (Tagged k x) = x
+
+
+-- | @Iso (TIP (TagR a)) (TIP (TagR b)) (HList a) (HList b)@
+tipHList x = iso (\(TIP a) -> hUntagSelf a) (TIP . hTagSelf) x
+
+-- | @Iso' (TIP (TagR s)) (HList a)@
+tipHList' x = simple (tipHList x)
+
+
+-- * conversion to and from 'Record'
+
+-- | @Iso (TIP s) (TIP t) (Record s) (Record t)@
+--
+-- 'typeIndexed' may be more appropriate
+tipRecord x = isoNewtype (\(TIP a) -> Record a) (\(Record b) -> TIP b) x
+
+-- | @Iso' (TIP (TagR s)) (Record a)@
+tipRecord' x = simple (tipRecord x)
+
+-- --------------------------------------------------------------------------
+-- * Zip
+
+instance (HZipList (UntagR x) (UntagR y) (UntagR xy),
+          UntagTag x, UntagTag y, UntagTag xy,
+          SameLengths [x,y,xy],
+          HTypeIndexed x, HTypeIndexed y,
+          HUnzip TIP x y xy
+          -- HTypeIndexed xy is always satisfied given the above
+          -- constraints (with a handwaving proof), so don't require
+          -- callers of hZip/hUnzip to supply such proof
+    ) => HZip TIP x y xy where
+  hZip = hZipTIP
+
+
+instance (HZipList (UntagR x) (UntagR y) (UntagR xy),
+          UntagTag x, UntagTag y, UntagTag xy,
+          HTypeIndexed x, HTypeIndexed y,
+          SameLengths [x,y,xy]) => HUnzip TIP x y xy where
+  hUnzip = hUnzipTIP
+
+-- | specialization of 'hZip'
+hZipTIP (TIP x) (TIP y) = TIP (hTagSelf (hZipList (hUntagSelf x) (hUntagSelf y)))
+
+-- | specialization of 'hUnzip'
+hUnzipTIP (TIP xy) = case hUnzipList (hUntagSelf xy) of
+  (x,y) -> (mkTIP (hTagSelf x), mkTIP (hTagSelf y))
+
+
+
+-- --------------------------------------------------------------------------
+-- * TIP Transform
+
+{- |
+
+Transforming a TIP: applying to a TIP a (polyvariadic) function
+that takes arguments from a TIP and updates the TIP with the result.
+
+In more detail: we have a typed-indexed collection TIP and we
+would like to apply a transformation function to it, whose argument
+types and the result type are all in the TIP. The function should locate
+its arguments based on their types, and update the TIP
+with the result. The function may have any number of arguments,
+including zero; the order of arguments should not matter.
+
+The problem was posed by Andrew U. Frank on Haskell-Cafe, Sep 10, 2009.
+<http://www.haskell.org/pipermail/haskell-cafe/2009-September/066217.html>
+The problem is an interesting variation of the keyword argument problem.
+
+Examples can be found in @examples/TIPTransform.hs@ and @examples/TIPTransformM.hs@
+-}
+
+class TransTIP op db where
+    ttip :: op -> TIP db -> TIP db
+
+instance (HMember (Tagged op op) db b,
+          Arity op n,
+          TransTIP1 b n op db)
+    => TransTIP op db where
+    ttip = ttip1 (Proxy ::Proxy b) (Proxy :: Proxy n)
+
+class TransTIP1 (b :: Bool) (n :: HNat) op db where
+    ttip1 :: Proxy b -> Proxy n -> op -> TIP db -> TIP db
+
+-- If op is found in a TIP, update the TIP with op
+instance HTPupdateAtLabel TIP op op db
+    => TransTIP1 True n op db where
+    ttip1 _ _ = tipyUpdate
+
+-- If op is not found in a TIP, it must be a function. Try to look up
+-- its argument in a TIP and recur.
+instance (HMember (Tagged arg arg) db b,
+          TransTIP2 b arg op db)
+    => TransTIP1 False (HSucc n) (arg -> op) db where
+    ttip1 _ _ = ttip2 (Proxy :: Proxy b)
+
+instance Fail '(TypeNotFound notfun, "in TIP", db) => TransTIP1 False HZero notfun db where
+    ttip1 = error "TransTIP1 Fail must have no instances"
+
+class TransTIP2 (b :: Bool) arg op db where
+    ttip2 :: Proxy b -> (arg -> op) -> TIP db -> TIP db
+
+instance (HOccurs arg (TIP db),
+         TransTIP op db)
+   => TransTIP2 True arg op db where
+    ttip2 _ f db = ttip (f (hOccurs db)) db
+
+instance Fail '(TypeNotFound arg, "in TIP", db) => TransTIP2 False arg op db where
+    ttip2 = error "TransTIP2 Fail must have no instances"
+
+-- ** Monadic version
+
+{- |
+
+In March 2010, Andrew Frank extended the problem for monadic operations.
+This is the monadic version of @TIPTransform.hs@ in the present directory.
+
+This is the TF implementation. When specifying the operation to perform over
+a TIP, we can leave it polymorphic over the monad. The type checker
+will instantiate the monad based on the context.
+
+-}
+class Monad m => TransTIPM m op db where
+    ttipM :: op -> TIP db -> m (TIP db)
+
+-- Check to see if the operation is a computation whose result
+-- is in the TIP. The type variable m' of the kind *->* below
+-- can be instantiated either to a monad type constructor, or (arg->).
+instance (Monad m, HMember (Tagged op op) db b,
+           Arity (m' op) n,
+           TransTIPM1 b n m (m' op) db)
+    => TransTIPM m (m' op) db where
+    ttipM = ttipM1 (Proxy :: Proxy b) (Proxy :: Proxy n)
+
+class Monad m => TransTIPM1 (b :: Bool) (n :: HNat) m op db where
+    ttipM1 :: Proxy b -> Proxy n -> op -> TIP db -> m (TIP db)
+
+-- If op is found in a TIP, update the TIP with op.
+-- The type variable m' must be equal to the type of the monad
+-- in which the final result is reported.
+instance (Monad m, m ~ m', HTPupdateAtLabel TIP op op db)
+    => TransTIPM1 True n m (m' op) db where
+    ttipM1 _ _ op db = do
+         op' <- op
+         return $ tipyUpdate op' db
+
+instance (Fail '(TypeNotFound op, "in TIP", db), Monad m)
+    => TransTIPM1 False HZero m op db where
+    ttipM1 _ _ = error "TransTIPM1 Fail must have no instances"
+
+-- If op is not found in a TIP, it must be a function. Look up
+-- its argument in a TIP and recur.
+instance (Monad m,
+          HMember (Tagged arg arg) db b,
+          TransTIPM2 b m arg op db)
+    => TransTIPM1 False (HSucc n) m (arg-> op) db where
+    ttipM1 _ _ = ttipM2 (Proxy :: Proxy b)
+
+
+class TransTIPM2 (b :: Bool) m arg op db where
+    ttipM2 :: Proxy b -> (arg -> op) -> TIP db -> m (TIP db)
+
+instance (HOccurs arg (TIP db), TransTIPM m op db)
+      => TransTIPM2 True m arg op db where
+    ttipM2 _ f db = ttipM (f (hOccurs db)) db
+
+
+instance (Fail '(TypeNotFound op, "in TIP", db))
+    => TransTIPM2 False m arg op db where
+    ttipM2 _ _ = error "TransTIPM1 Fail must have no instances"
+
+-- --------------------------------------------------------------------------
+
+-- tests for tipyTuple. These only work if tipyTuple is compiled
+-- in a module that has NoMonoLocalBinds enabled
+_ = tipyTuple ( '1' .*. True .*. emptyTIP ) :: (Char, Bool)
+_ = tipyTuple ( '1' .*. True .*. emptyTIP ) :: (Bool, Char)
+
+
+-- --------------------------------------------------------------------------
+
 -- * Sample code
 
 {- $setup
 
 [@Assume@]
 
+>>> import Data.HList.TypeEqO
 >>> import Data.HList.FakePrelude
+>>> import Data.HList.HOccurs
 
 >>> :{
 newtype Key    = Key Integer deriving (Show,Eq,Ord)
@@ -118,13 +451,12 @@
 data Breed     = Cow | Sheep deriving (Show,Eq)
 newtype Price  = Price Float deriving (Show,Eq,Ord)
 data Disease   = BSE | FM deriving (Show,Eq)
-type Animal =  '[Key,Name,Breed,Price]
+type Animal =  TagR '[Key,Name,Breed,Price]
 :}
 
 >>> :{
-let myAnimal :: HList Animal
-    myAnimal = hBuild (Key 42) (Name "Angus") Cow (Price 75.5)
-    myTipyCow = TIP myAnimal
+let myTipyCow :: TIP Animal -- optional
+    myTipyCow = Key 42 .*.  Name "Angus" .*.  Cow .*.  Price 75.5 .*. emptyTIP
     animalKey :: (HOccurs Key l, SubType l (TIP Animal)) => l -> Key
     animalKey = hOccurs
 :}
@@ -141,16 +473,23 @@
 Cow
 
 >>> BSE .*. myTipyCow
-TIPH[BSE, Key 42, Name "Angus", Cow, Price 75.5]
+TIPH[BSE,Key 42,Name "Angus",Cow,Price 75.5]
 
 
 
->>> Sheep .*. tipyDelete (Proxy::Proxy Breed) myTipyCow
-TIPH[Sheep, Key 42, Name "Angus", Price 75.5]
+>>> Sheep .*. hDeleteAtLabel (Label::Label Breed) myTipyCow
+TIPH[Sheep,Key 42,Name "Angus",Price 75.5]
 
 >>> tipyUpdate Sheep myTipyCow
-TIPH[Key 42, Name "Angus", Sheep, Price 75.5]
+TIPH[Key 42,Name "Angus",Sheep,Price 75.5]
 
+
+>>> tipyProject2 (Proxy :: Labels '[Name,Price]) myTipyCow
+(TIPH[Name "Angus",Price 75.5],TIPH[Key 42,Cow])
+
+>>> tipyProject (Proxy :: Labels '[Name,Price]) myTipyCow
+TIPH[Name "Angus",Price 75.5]
+
 -}
 
 
@@ -158,9 +497,10 @@
 
 Don't bother repeating the type error:
 
->>> let doctestEq x y = x == y || "No instance for" `Data.List.isInfixOf` x
 
 >>> Sheep .*. myTipyCow
--- type error --
+...
+...No instance for (Fail (DuplicatedLabel (Label Breed)))
+...
 
 -}
diff --git a/Data/HList/TIPtuple.hs b/Data/HList/TIPtuple.hs
new file mode 100644
--- /dev/null
+++ b/Data/HList/TIPtuple.hs
@@ -0,0 +1,70 @@
+{-# LANGUAGE NoMonoLocalBinds #-}
+{-# LANGUAGE NoTypeFamilies #-}
+{- | Description: TIP functions needing different LANGUAGE extensions
+
+While NoMonoLocalBinds could be enabled in TIP.hs, the ghc manual warns
+"type inference becomes less predicatable if you do so. (Read the papers!)".
+These definitions don't need type families, putting these definitions in
+a separate module avoids that mess.
+
+XXX these should be implemented in terms of 'HTuple' and 'tipyProject',
+which means adding
+-}
+module Data.HList.TIPtuple where
+
+import Data.HList.HOccurs
+
+{- | project a TIP (or HList) into a tuple
+
+@tipyTuple' x = ('hOccurs' x, hOccurs x)@
+
+behaves similarly, except @tipyTuple@ excludes
+the possibility of looking up the same element
+twice, which allows inferring a concrete type
+in more situations. For example
+
+> (\x y z -> tipyTuple (x .*. y .*. emptyTIP) `asTypeOf` (x, z)) () 'x'
+
+has type @Char -> ((), Char)@. tipyTuple' would
+need a type annotation to decide whether the type
+should be @Char -> ((), Char)@ or @() -> ((), ())@
+
+-}
+tipyTuple l = t (,) `asTypeOf` t (flip (,))
+  where
+  t f = case hOccursRest l of
+     (x, ly) -> case hOccursRest ly of
+         (y, _) -> f x y
+
+tipyTuple3 l = t (,,)
+          `asTypeOf` t (\a b c -> (b,c,a))
+          `asTypeOf` t (\a b c -> (c,a,b))
+  where
+  t f = case hOccursRest l of
+    (x, lyz) -> case hOccursRest lyz of
+       (y, lz) -> case hOccursRest lz of
+          (z, _) -> f x y z
+
+tipyTuple4 l = t (,,,)
+          `asTypeOf` t (\a b c d -> (b,c,d,a))
+          `asTypeOf` t (\a b c d -> (c,d,a,b))
+          `asTypeOf` t (\a b c d -> (d,a,b,c))
+  where
+  t f = case hOccursRest l of
+    (a, lbcd) -> case hOccursRest lbcd of
+       (b, lcd) -> case hOccursRest lcd of
+          (c, ld) -> case hOccursRest ld of
+             (d, _) -> f a b c d
+
+tipyTuple5 l = t (,,,,)
+          `asTypeOf` t (\a b c d e -> (b,c,d,e,a))
+          `asTypeOf` t (\a b c d e -> (c,d,e,a,b))
+          `asTypeOf` t (\a b c d e -> (d,e,a,b,c))
+          `asTypeOf` t (\a b c d e -> (e,a,b,c,d))
+  where
+  t f = case hOccursRest l of
+    (a, lbcde) -> case hOccursRest lbcde of
+       (b, lcde) -> case hOccursRest lcde of
+          (c, lde) -> case hOccursRest lde of
+            (d, le) -> case hOccursRest le of
+               (e, _) -> f a b c d e
diff --git a/Data/HList/TypeEqO.hs b/Data/HList/TypeEqO.hs
--- a/Data/HList/TypeEqO.hs
+++ b/Data/HList/TypeEqO.hs
@@ -1,13 +1,18 @@
+{-# LANGUAGE CPP #-}
+#if (__GLASGOW_HASKELL__ < 709)
 {-# LANGUAGE OverlappingInstances #-}
+{-# OPTIONS_GHC -fno-warn-unrecognised-pragmas #-}
+#endif
 
 {- |
    The HList library
 
    (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
 
-   Generic type equality predicate: 
+   Generic type equality predicate:
    The implementation based on overlapping instances
    The only place where overlapping instances are really used
+   besides Label5
 
 -}
 
@@ -15,15 +20,36 @@
 
 import Data.HList.FakePrelude
 
-instance HEq x x True
-instance False ~ b => HEq x y b
+#if !NEW_TYPE_EQ
+instance {-# OVERLAPPING #-} HEq x x True
+instance {-# OVERLAPPABLE  #-} False ~ b => HEq x y b
 -- instance TypeEq x y HFalse -- would violate functional dependency
+#endif
 
 
-class TupleType t (b :: Bool) | t -> b
-instance TupleType () True
-instance TupleType (x,y) True
-instance TupleType (x,y,z) True
+
+class TupleType (t :: *) (b :: Bool) | t -> b
+instance {-# OVERLAPPING #-} TupleType () True
+instance {-# OVERLAPPING #-} TupleType (x,y) True
+instance {-# OVERLAPPING #-} TupleType (x,y,z) True
 -- Continue for a while
-instance False ~ b => TupleType x b
+instance {-# OVERLAPPABLE #-} False ~ b => TupleType x b
 -- instance TupleType x HFalse -- would violate functional dependency
+
+-- overlaps an instance Show (Proxy t) for convenience
+instance {-# OVERLAPPING #-} Show (Proxy True)  where show _ = "HTrue"
+instance {-# OVERLAPPING #-} Show (Proxy False) where show _ = "HFalse"
+
+instance {-# OVERLAPPING #-} HNat2Integral n => Show (Proxy (n :: HNat)) where
+    show n = "H" ++ show (hNat2Integral n :: Integer)
+
+
+instance {-# OVERLAPPABLE #-} hZero ~ HZero => ArityFwd f hZero
+instance {-# OVERLAPPING #-} Arity f n => ArityFwd (x -> f) (HSucc n)
+
+
+-- | All our keywords must be registered
+class IsKeyFN (t :: *) (flag :: Bool) | t-> flag
+-- | overlapping/fallback case
+instance {-# OVERLAPPABLE #-} (False ~ flag) => IsKeyFN t flag
+
diff --git a/Data/HList/Variant.hs b/Data/HList/Variant.hs
--- a/Data/HList/Variant.hs
+++ b/Data/HList/Variant.hs
@@ -1,68 +1,1168 @@
-{-# LANGUAGE ConstraintKinds #-}
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, UndecidableInstances #-}
-
-
-{- |
-   The HList library
-
-   (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
-
-   Variants, i.e., labelled sums.
-
-   One approach to their implementation would be to consider both
-   the favoured label and the corresponding value as dynamics upon
-   variant construction. Since we are too lazy to programme some
-   Typeable instances for non-ghc systems (NB: in GHC, Typeable
-   is derivable), we rather model variants as (opaque) records
-   with maybies for the values. Only one value will actually hold
-   non-Nothing, as guaranteed by the constructor.
-
-   See VariantP.hs for a different approach to open sums.
--}
-
-module Data.HList.Variant where
-
-import Data.HList.FakePrelude
-import Data.HList.Record
-import Data.HList.HList
-
-
--- --------------------------------------------------------------------------
--- | Variant types on the basis of label-maybe pairs.
-
-newtype Variant mr = Variant mr
-
-
--- --------------------------------------------------------------------------
--- | Turn proxy sequence into sequence of Nothings
-
-data HMaybeF = HMaybeF
-instance ((Tagged l (Proxy t) ~ a, b ~ Tagged l (Maybe t))) =>  ApplyAB HMaybeF a b   where
-    applyAB _ _ = Tagged Nothing
-
-hMaybied x = hMap HMaybeF x
-
-
--- --------------------------------------------------------------------------
--- | Public constructor
--- it seems we can blame 'hUpdateAtLabel' (not 'HMap') for needing the asTypeOf?
-mkVariant x y (Record v) = let r1 = Record (hMaybied v) in
-    case hUpdateAtLabel x (Just y) r1 `asTypeOf` r1 of
-    Record t -> Variant t
-
--- --------------------------------------------------------------------------
--- | Public destructor
-
-unVariant x (Variant v) = hLookupByLabel x (Record v)
-
-
--- --------------------------------------------------------------------------
--- | Variants are opaque
-
-instance Show (Variant v)
- where
-  show _ = "<Cannot show Variant content!>"
-
-
+{-# LANGUAGE CPP #-}
+
+{- |
+   Description: Variants, i.e., labelled sums, generalizations of Either
+
+   The HList library
+
+   See <Data-HList-CommonMain.html#t:Variant CommonMain#Variant>
+   for the public (safe) interface.
+
+   The implementation here follows "Data.Dynamic", though Typeable is not
+   needed.
+
+   See @broken/VariantP.hs@ and @broken/VariantOld.hs@ for different approaches
+   to open sums.
+-}
+
+module Data.HList.Variant where
+
+import Data.HList.FakePrelude
+import Data.HList.Record
+import Data.HList.HList
+import Data.HList.HListPrelude
+import Data.HList.HOccurs()
+import Data.HList.HArray
+
+import Text.ParserCombinators.ReadP hiding (optional)
+
+import Unsafe.Coerce
+import GHC.Prim (Any)
+import GHC.Exts (Constraint)
+
+import Data.Data
+import Control.Applicative
+import LensDefs
+import Control.Monad
+
+-- * Labels for doctests
+
+{- $setup
+
+>>> import Data.HList.RecordPuns
+>>> let x = Label :: Label "x"
+>>> let y = Label :: Label "y"
+>>> let z = Label :: Label "z"
+>>> let _left = Label :: Label "left"
+>>> let _right = Label :: Label "right"
+
+>>> :set -XQuasiQuotes -XViewPatterns -XDataKinds
+
+
+-- * Creating Variants
+
+It is necessary to specify the order in which the fields occur, using
+a data type like
+
+>>> let p = Proxy :: Proxy '[Tagged "left" Char, Tagged "right" Int]
+
+Then this argument can be passed into 'mkVariant'
+
+>>> let v = mkVariant _left 'x' p
+>>> let w = mkVariant _right 5  p
+
+>>> :t v
+v :: Variant '[Tagged "left" Char, Tagged "right" Int]
+
+>>> :t w
+w :: Variant '[Tagged "left" Char, Tagged "right" Int]
+
+
+>>> [v,w]
+[V{left='x'},V{right=5}]
+
+-}
+
+
+-- ** Alternative: a 'Record' as the Proxy
+{- $mkVariant2
+
+The type of mkVariant also allows using a 'Record' as the proxy. For example:
+
+>>> :{
+let p2 = [pun| left right |] where
+            left = 'a'
+            right = (4::Int)
+:}
+
+>>> let v2 = mkVariant _left 'x' p2
+>>> let w2 = mkVariant _right 5  p2
+
+>>> :t v2
+v2 :: Variant '[Tagged "left" Char, Tagged "right" Int]
+
+>>> :t w2
+w2 :: Variant '[Tagged "left" Char, Tagged "right" Int]
+
+>>> (v2,w2)
+(V{left='x'},V{right=5})
+
+-}
+
+-- ** A polymorphic Proxy
+{- $mkVariant3
+
+It is also possible to leave the @Char@ and @Int@ as type variables,
+and have them inferred.
+
+>>> let p3 = Proxy :: Proxy '[Tagged "left" a, Tagged "right" b]
+
+Using @p3@ takes some care. The following attempt shows the problem:
+
+>>> :{
+let v3' = mkVariant _left 'x' p3
+    w3' = mkVariant _right (5::Int) p3
+:}
+
+>>> :t v3'
+v3' :: Variant '[Tagged "left" Char, Tagged "right" b]
+
+>>> :t w3'
+w3' :: Variant '[Tagged "left" a, Tagged "right" Int]
+
+Here each use of @p3@ does not constrain the type of the other use.
+In some cases those type variables will be inferred from other constraints,
+such as when putting the variants into a list
+
+>>> [v3', w3']
+[V{left='x'},V{right=5}]
+
+In other cases the other tags will be defaulted to (), at least if `ExtendedDefaultRules` is enabled:
+
+>>> v3'
+V{left='x'}
+
+>>> :set -XNoExtendedDefaultRules
+>>> v3'
+...
+...No instance for (Show ...) arising from a use of ‘print’
+...
+
+
+Another way around this issue is to make sure that the proxy
+is bound in a monomorphic pattern. These are patterns that allow
+name shadowing.
+
+* @\p -> ...@
+* @case e of p -> ...@
+* @do p <- e; ...@
+* implicit parameters @let ?p = e in ...@
+* <http://stackoverflow.com/questions/23899279#23899611 other patterns involved in mutually recursive bindings>
+
+An example of the case:
+
+>>> :{
+let (v3,w3) = case p3 of
+              p -> (mkVariant _left 'x' p,
+                    mkVariant _right (5 :: Int) p)
+:}
+
+
+>>> :t v3
+v3 :: Variant '[Tagged "left" Char, Tagged "right" Int]
+
+>>> :t w3
+w3 :: Variant '[Tagged "left" Char, Tagged "right" Int]
+
+-}
+
+-- --------------------------------------------------------------------------
+{- |
+@Variant vs@ has an implementation similar to 'Dynamic', except the
+contained value is one of the elements of the @vs@ list, rather than
+being one particular instance of 'Typeable'.
+
+>>> v .!. _right
+Nothing
+
+>>> v .!. _left
+Just 'x'
+
+In some cases the 'pun' quasiquote works with variants,
+
+>>> let f [pun| left right |] = (left,right)
+>>> f v
+(Just 'x',Nothing)
+
+>>> f w
+(Nothing,Just 5)
+
+
+>>> let add1 v = hMapV (Fun succ :: Fun '[Enum] '()) v
+
+>>> f (add1 v)
+(Just 'y',Nothing)
+
+>>> f (add1 w)
+(Nothing,Just 6)
+
+
+-}
+data Variant (vs :: [*]) = Variant !Int Any
+
+#if __GLASGOW_HASKELL__ > 707
+-- the inferred role is phantom, which is not safe
+type role Variant representational
+#endif
+
+
+
+-- ** Unsafe operations
+
+-- | This is only safe if the n'th element of vs has type @Tagged t v@
+unsafeMkVariant :: Int -- ^ n
+                -> v
+                -> Variant vs
+unsafeMkVariant n a = Variant n (unsafeCoerce a)
+
+{- | Safe when (e ~ e') given that
+
+> Tagged t e ~ HLookupByHNatR n v
+> Tagged t' e' ~ HLookupByHNatR n v'
+
+'hUpdateAtLabel' is the safe version
+
+-}
+unsafeCastVariant :: Variant v -> Variant v'
+unsafeCastVariant (Variant n e) = Variant n e
+
+-- | in ghc>=7.8, 'Data.Coerce.coerce' is probably a better choice
+castVariant :: (RecordValuesR v ~ RecordValuesR v',
+              SameLength v v') => Variant v -> Variant v'
+castVariant = unsafeCastVariant
+
+instance Relabeled Variant where
+    relabeled = iso castVariant castVariant
+
+-- | private destructor. This is safe only if the value
+-- contained actually has type `e`
+unsafeUnVariant :: Variant v -> e
+unsafeUnVariant (Variant _ e) = unsafeCoerce e
+
+
+{- | This function is unsafe because it can lead to a runtime error
+when used together with the 'HExtend' instance (.*.)
+
+>>> print $ (Label :: Label "x") .=. (Nothing :: Maybe ()) .*. unsafeEmptyVariant
+V{*** Exception: invalid variant
+
+use 'mkVariant1' instead
+
+-}
+unsafeEmptyVariant :: Variant '[]
+unsafeEmptyVariant = unsafeMkVariant 0 ()
+
+-- --------------------------------------------------------------------------
+-- * Public constructor
+
+class HasField x (Variant vs) (Maybe v) =>
+      MkVariant x v vs | x vs -> v where
+    mkVariant :: Label x -- ^ the tag
+        -> v -- ^ value to be stored
+        -> proxy vs -- ^ a helper to fix the ordering and types of the
+                    -- potential values that this variant contains.
+                    -- Typically this will be a 'Proxy', 'Record' or
+                    -- another 'Variant'
+        -> Variant vs
+
+mkVariant1 l v = l .=. Just v .*. unsafeEmptyVariant
+
+instance (HFindLabel x vs n,
+          HNat2Integral n,
+          HasField x (Variant vs) (Maybe v)) =>
+    MkVariant x v vs where
+  mkVariant _x y _p = unsafeMkVariant (hNat2Integral (Proxy :: Proxy n)) y
+  -- done as a one-instance class instead of a function to be able to hide
+  -- the 'n' type variable
+
+-- --------------------------------------------------------------------------
+-- * Public destructor
+
+{- $note 'hLookupByLabel' (synonym '.!.')
+
+> (.!.)             :: Variant v -> Label x -> Maybe e
+> hLookupByLabel    :: Label x -> Variant v -> Maybe e
+
+'hPrism' and 'hLens'' combine this with 'mkVariant'
+-}
+instance (HasField x (Record vs) a,
+          HFindLabel x vs n,
+          HNat2Integral n)
+  => HasField x (Variant vs) (Maybe a) where
+  hLookupByLabel _x (Variant n d)
+          | hNat2Integral (Proxy :: Proxy n) == n = Just (unsafeCoerce d)
+          | otherwise = Nothing
+
+splitVariant1 :: Variant (Tagged s x ': xs) -> Either x (Variant xs)
+splitVariant1 (Variant 0 x) = Left (unsafeCoerce x)
+splitVariant1 (Variant n x) = Right (Variant (n-1) x)
+
+-- | x ~ Tagged s t
+splitVariant1' :: Variant (x ': xs) -> Either x (Variant xs)
+splitVariant1' (Variant 0 x) = Left (unsafeCoerce x)
+splitVariant1' (Variant n x) = Right (Variant (n-1) x)
+
+extendVariant :: Variant l -> Variant (e ': l)
+extendVariant (Variant m e) = Variant (m+1) e
+
+-- --------------------------------------------------------------------------
+-- * Prism
+
+{- | Make a @Prism (Variant s) (Variant t) a b@ out of a Label.
+
+See "Data.HList.Labelable".'hLens'' is a more overloaded version.
+
+Few type annotations are necessary because of the restriction
+that `s` and `t` have the same labels in the same order, and to
+get \"t\" the \"a\" in \"s\" is replaced with \"b\".
+
+-}
+class (SameLength s t, SameLabels s t)
+        => HPrism x s t a b
+          | x s -> a, x t -> b,    -- lookup
+            x s b -> t, x t a -> s -- update
+  where
+    hPrism :: (Choice p, Applicative f)
+        => Label x -> p a (f b) -> p (Variant s) (f (Variant t))
+
+
+instance (
+    MkVariant x b t,
+
+    HasField x (Variant s) (Maybe a),
+
+    -- labels in the HList are not changed at all:
+    -- number, ordering, actual values are all constant
+    SameLength s t,
+    SameLabels s t,
+
+    -- only the target of the prism can have it's type changed
+    H2ProjectByLabels '[Label x] s si so,
+    H2ProjectByLabels '[Label x] t ti to,
+    so ~ to,
+
+    -- to convince GHC the fundeps are satisfied
+    HUpdateAtLabel Variant x b s t,
+    HUpdateAtLabel Variant x a t s
+   ) => HPrism x s t a b where
+    hPrism x = prism (\b -> mkVariant x b Proxy)
+                  (\s -> case hLookupByLabel x s of
+                    Just a -> Right a
+                    Nothing -> Left (unsafeCastVariant s :: Variant t))
+
+
+
+-- --------------------------------------------------------------------------
+-- * Read
+-- | Variants are not opaque
+instance (ShowVariant vs) => Show (Variant vs) where
+    showsPrec _ v = ("V{"++) . showVariant v . ('}':)
+
+
+-- | helper class for defining the Show instance
+class ShowVariant vs where
+    showVariant :: Variant vs -> ShowS
+
+instance (ShowLabel l, Show v, ShowVariant (w ': ws))
+      => ShowVariant (Tagged l v ': w ': ws) where
+    showVariant vs = case splitVariant1 vs of
+        Left v -> \rest -> showLabel l ++ "=" ++ show v ++ rest
+        Right wws -> showVariant wws
+      where l = Label :: Label l
+
+instance (ShowLabel l, Show v, lv ~ Tagged l v) => ShowVariant '[lv] where
+    showVariant vs = case splitVariant1 vs of
+        Left v -> \rest -> showLabel l ++ "=" ++ show v ++ rest
+        Right _ -> error "invalid variant"
+      where l = Label :: Label l
+
+-- --------------------------------------------------------------------------
+-- * Show
+-- | A corresponding read instance
+
+instance ReadVariant v => Read (Variant v) where
+    readsPrec _ = readP_to_S $ do
+      _ <- string "V{"
+      r <- readVariant
+      _ <- string "}"
+      return r
+
+class ReadVariant vs where
+    readVariant :: ReadP (Variant vs)
+
+instance ReadVariant '[] where
+    readVariant = return unsafeEmptyVariant
+
+instance (ShowLabel l, Read v, ReadVariant vs,
+          HOccursNot (Label l) (LabelsOf vs))
+    => ReadVariant (Tagged l v ': vs) where
+    readVariant = do
+      mlv <- optional lv
+      case mlv of
+        Nothing -> do
+          rest <- readVariant
+          return (l .=. mlv .*. rest)
+        Just e -> do
+          return (mkVariant l e p)
+
+      where
+        lv = do
+            _ <- string (showLabel l)
+            _ <- string "="
+            readS_to_P reads
+
+        l = Label :: Label l
+
+        p = Proxy :: Proxy (Tagged l v ': vs)
+
+
+-- * Data
+instance (Typeable (Variant v), GfoldlVariant v v,
+          GunfoldVariant v v,
+          VariantConstrs v)
+        => Data (Variant v) where
+    gfoldl = gfoldlVariant
+    gunfold k z c = gunfoldVariant (\con -> k (z con)) (Proxy :: Proxy v) (constrIndex c - 1)
+    toConstr v@(Variant n _) = case drop n (variantConstrs (dataTypeOf v) v) of
+        c : _ -> c
+        _ -> error "Data.HList.Variant.toConstr impossible"
+    dataTypeOf x = let self = mkDataType (show (typeOf x)) (variantConstrs self x)
+          in self
+
+class VariantConstrs (xs :: [*]) where
+  variantConstrs :: DataType -> proxy xs -> [Constr]
+
+instance VariantConstrs '[] where
+  variantConstrs _ _ = []
+
+instance (ShowLabel l, VariantConstrs xs) => VariantConstrs (Tagged l e ': xs) where
+  variantConstrs dt _ = mkConstr dt (showLabel (Label :: Label l)) [] Prefix :
+        variantConstrs dt (Proxy :: Proxy xs)
+
+
+
+
+{- | [@implementation of gunfold for Variant@]
+
+In ghci
+
+> :set -ddump-deriv -XDeriveDataTypeable
+> data X a b c = A a | B b | C c deriving (Data,Typeable)
+
+shows that gunfold is defined something like
+
+> gunfold k z c = case constrIndex c of
+>       1 -> k (z Ghci1.A)
+>       2 -> k (z Ghci1.B)
+>       _ -> k (z Ghci1.C)
+
+If we instead had
+
+type X a b c = Variant [Tagged "A" a, Tagged "B" b, Tagged "C" c]
+
+Then we could write:
+
+> gunfold1 :: (forall b r. Data b => (b -> r) -> c r)
+          -> Variant [Tagged "A" a, Tagged "B" b, Tagged "C" c]
+> gunfold1 f c = case constrIndex c of
+>       1 -> f mkA
+>       2 -> f mkB
+>       _ -> f mkC
+>   where mkA a = mkVariant (Label :: Label "A") (a :: a) v
+>         mkB b = mkVariant (Label :: Label "B") (b :: b) v
+>         mkC c = mkVariant (Label :: Label "C") (c :: c) v
+>         v = Proxy :: Proxy [Tagged "A" a, Tagged "B" b, Tagged "C" c]
+
+where @f = k.z@
+
+
+-}
+class GunfoldVariant (es :: [*]) v where
+    gunfoldVariant ::
+        (forall b. Data b => (b -> Variant v) -> c (Variant v))
+          -- ^ @f = k . z@
+        -> Proxy es
+        -> Int
+        -> c (Variant v)
+
+instance (MkVariant l e v, Data e) => GunfoldVariant '[Tagged l e] v where
+    gunfoldVariant f _ _ = f (\e -> mkVariant (Label :: Label l) (e :: e) Proxy)
+
+instance (MkVariant l e v, Data e,
+        GunfoldVariant (b ': bs) v) => GunfoldVariant (Tagged l e ': b ': bs)  v where
+    gunfoldVariant f _ 0 = f (\e -> mkVariant (Label :: Label l) (e :: e) Proxy)
+    gunfoldVariant f _ n = gunfoldVariant f (Proxy :: Proxy (b ': bs)) (n-1)
+
+
+
+class GfoldlVariant xs xs' where
+  -- | the same as 'gfoldl', except the variant that is returned can have more
+  -- possible values (needed to actually implement gfoldl).
+  gfoldlVariant ::
+     (forall d b. Data d => c (d -> b) -> d -> c b)
+     -> (forall g. g -> c g) -> Variant xs -> c (Variant xs')
+
+instance (a ~ Tagged l v, MkVariant l v r, Data v,
+          GfoldlVariant (b ': c) r)
+      => GfoldlVariant (a ': b ': c) r where
+  gfoldlVariant k z xxs = case splitVariant1 xxs of
+      Right xs -> gfoldlVariant k z xs
+      -- If the c@type variable in 'gfoldl' had a Functor constraint,
+      -- this case could be extendVariant `fmap` gfoldl k z xs,
+      -- and then 'GfoldlVariant' would be unnecessary
+      Left x ->
+            let mkV e = mkVariant (Label :: Label l) e Proxy
+            in z mkV `k` x
+
+instance (Unvariant '[a] v, a ~ Tagged l v, Data v,
+          MkVariant l v b) => GfoldlVariant '[a] b where
+    gfoldlVariant k z xxs = z mkV `k` unvariant xxs
+        where mkV e = mkVariant (Label :: Label l) e Proxy
+
+
+
+-- --------------------------------------------------------------------------
+-- * Map
+-- | Apply a function to all possible elements of the variant
+newtype HMapV f = HMapV f
+
+-- | shortcut for @applyAB . HMapV@. 'hMap' is more general
+hMapV f v = applyAB (HMapV f) v
+
+-- | @hMapOutV f = unvariant . hMapV f@, except an ambiguous type
+-- variable is resolved by 'HMapOutV_gety'
+hMapOutV :: forall x y z f. (SameLength x y,
+      HMapAux Variant (HFmap f) x y,
+      Unvariant y z,
+      HMapOutV_gety x z ~ y
+  ) => f -> Variant x -> z
+hMapOutV f v = unvariant (hMapV f v :: Variant y)
+
+
+-- | resolves an ambiguous type in 'hMapOutV'
+type family HMapOutV_gety (x :: [*]) (z :: *) :: [*]
+type instance HMapOutV_gety (Tagged s x ': xs) z = Tagged s z ': HMapOutV_gety xs z
+type instance HMapOutV_gety '[] z = '[]
+
+
+-- | apply a function to all values that could be in the variant.
+instance (vx ~ Variant x,
+          vy ~ Variant y,
+          HMapAux Variant (HFmap f) x y,
+          SameLength x y)
+     => ApplyAB (HMapV f) vx vy where
+    applyAB (HMapV f) x = hMapAux (HFmap f) x
+
+instance (ApplyAB f te te') => HMapAux Variant f '[te] '[te'] where
+    hMapAux f v = case splitVariant1' v of
+        Left te -> unsafeMkVariant 0 (applyAB f te :: te')
+        Right _ -> error "HMapVAux: variant invariant broken"
+
+instance (ApplyAB f te te',
+          HMapCxt Variant f (l ': ls) (l' ': ls'))
+    => HMapAux Variant f (te ': l ': ls) (te' ': l' ': ls') where
+      hMapAux f v = case splitVariant1' v of
+          Left te -> unsafeMkVariant 0 (applyAB f te :: te')
+          Right es -> extendVariant (hMapAux f es)
+
+-- --------------------------------------------------------------------------
+-- * HUpdateAtLabel instance
+
+{- |
+
+> hUpdateAtLabel x e' (mkVariant x e proxy) == mkVariant x e' proxy
+> hUpdateAtLabel y e' (mkVariant x e proxy) == mkVariant x e  proxy
+
+-}
+instance
+   (HUpdateVariantAtLabelCxt l e v v' n _e) =>
+    HUpdateAtLabel Variant l e v v' where
+    hUpdateAtLabel l e v = case hLookupByLabel l v of
+          Just _e -> mkVariant l e (Proxy :: Proxy v')
+          Nothing -> unsafeCastVariant v
+
+type HUpdateVariantAtLabelCxt l e v v' n _e =
+   (HFindLabel l v n,
+    HFindLabel l v' n,
+    HUpdateAtHNatR n (Tagged l e) v ~ v',
+    HasField l (Variant v) (Maybe _e),
+    HasField l (Record v') e,
+    MkVariant l e v')
+
+
+-- --------------------------------------------------------------------------
+-- * HExtend instance
+{- | Extension for Variants prefers the first value
+
+> (l .=. Nothing) .*. v = v
+> (l .=. Just e)  .*. _ = mkVariant l e Proxy
+
+-}
+instance (le ~ Tagged l (Maybe e), HOccursNot (Label l) (LabelsOf v)) =>
+    HExtend le (Variant v) where
+    type HExtendR le (Variant v) = Variant (UnMaybe le ': v)
+    Tagged (Just e) .*. _ = unsafeMkVariant 0 e
+    Tagged Nothing .*. (Variant n e) = Variant (n+1) e
+
+type family UnMaybe le
+type instance UnMaybe (Tagged l (Maybe e)) = Tagged l e
+
+-- | used for 'HExtend' 'TIP'
+type instance UnMaybe (Maybe e) = e
+
+
+-- --------------------------------------------------------------------------
+-- * Conversion to an untagged value
+class HAllEqVal (x :: [*]) (b :: Bool) | x -> b
+instance HAllEqVal '[] True
+instance HAllEqVal '[x] True
+instance (HEq a a' b,
+          HAllEqVal (Tagged t a' ': xs) b2,
+          HAnd b b2 ~ b3) =>
+  HAllEqVal (Tagged s a ': Tagged t a' ': xs) b3
+
+
+class HAllEqVal' (x :: [*])
+instance HAllEqVal' '[]
+instance HAllEqVal' '[x]
+instance (HAllEqVal' (ta ': xs),
+          a' ~ a,
+          ta ~ Tagged t a,
+          ta' ~ Tagged t' a')
+  => HAllEqVal' (ta' ': ta ': xs)
+
+
+{- | Similar to 'unvariant', except type variables in @v@
+will be made equal to @e@ if possible. That allows the type
+of @Nothing@ to be inferred as @Maybe Char@.
+
+>>> unvariant' $ x .=. Nothing .*. mkVariant1 y 'y'
+'y'
+
+However, this difference leads to more local error messages
+(@Couldn't match type ‘()’ with ‘Char’@), rather than the following
+with @unvariant@:
+
+> Fail
+>    '("Variant",
+>      '[Tagged "left" Char, Tagged "right" ()],
+>      "must have all values equal to ",
+>      e))
+
+-}
+class Unvariant' v e | v -> e where
+    unvariant' :: Variant v -> e
+
+instance (HAllEqVal' (Tagged () e ': v), Unvariant v e) =>
+    Unvariant' v e where
+  unvariant' = unvariant
+
+{- | Convert a Variant which has all possibilities having the same type
+into a value of that type. Analogous to @either id id@.
+
+See also 'unvariant'' -}
+class Unvariant v e | v -> e where
+    unvariant :: Variant v -> e
+
+instance (Unvariant1 b v e,
+          HAllEqVal v b,
+          HAllEqVal (Tagged () e ': v) b)
+    => Unvariant v e where
+      unvariant = unvariant1 (Proxy :: Proxy b)
+
+
+class Unvariant1 b v e | b v -> e where
+    unvariant1 :: Proxy b -> Variant v -> e
+
+instance (v ~ Tagged t1 e)
+    => Unvariant1 True (v ': vs) e where
+    unvariant1 _ = unsafeUnVariant
+
+data UnvariantTypeMismatch (vs :: [*])
+
+instance Fail (UnvariantTypeMismatch (v ': vs))
+      => Unvariant1 False (v ': vs) (UnvariantTypeMismatch (v ': vs)) where
+    unvariant1 _ = error "Data.HList.Variant.Unvariant1 Fail must have no instances"
+
+instance Fail "Unvariant applied to empty variant"
+      => Unvariant1 b '[] (Proxy "Unvariant applied to empty variant") where
+    unvariant1 _ = error "Data.HList.Variant.Unvariant1 Fail must have no instances"
+
+{- | @Lens (Variant s) (Variant t) a b@
+
+Analogue of @Control.Lens.chosen :: Lens (Either a a) (Either b b) a b@
+-}
+unvarianted :: (Unvariant' s a,
+                Unvariant' t b,
+                SameLabels s t, -- extra constraints to reduce ambiguity
+                SameLength s t,
+                Functor f) =>
+    (a -> f b) -> Variant s -> f (Variant t)
+unvarianted f v@(Variant n _) = fmap (\e' -> unsafeMkVariant n e')
+                                      (f (unvariant' v))
+
+-- | @Lens' (Variant s) a@
+--
+-- where we might have @s ~ '[Tagged t1 a, Tagged t2 a]@
+unvarianted' x = simple (unvarianted x)
+
+-- * Zip
+
+{- | Applies to variants that have the same labels
+in the same order. A generalization of
+
+> zipEither :: Either a b -> Either a b -> Maybe (Either (a,a) (b,b))
+> zipEither (Left a) (Left a') = Just (Left (a,a'))
+> zipEither (Right a) (Right a') = Just (Right (a,a'))
+> zipEither _ _ = Nothing
+
+see 'HZip' for zipping other collections
+
+-}
+class ZipVariant x y xy | x y -> xy, xy -> x y where
+    zipVariant :: Variant x -> Variant y -> Maybe (Variant xy)
+
+instance ZipVariant '[] '[] '[] where
+    zipVariant _ _ = Nothing
+
+instance (tx ~ Tagged t x,
+          ty ~ Tagged t y,
+          txy ~ Tagged t (x,y),
+          ZipVariant xs ys zs,
+          MkVariant t (x,y) (txy ': zs))
+  => ZipVariant (tx ': xs) (ty ': ys) (txy ': zs) where
+    zipVariant x y = case (splitVariant1 x, splitVariant1 y) of
+        (Left x', Left y') -> Just (mkVariant (Label :: Label t) (x',y') Proxy)
+        (Right x', Right y') -> extendVariant <$> zipVariant x' y'
+        _ -> Nothing
+
+
+instance (HUnzip Variant (x2 ': xs) (y2 ': ys) (xy2 ': xys),
+          tx ~ Tagged t x,
+          ty ~ Tagged t y,
+          txy ~ Tagged t (x,y))
+      => HUnzip Variant (tx ': x2 ': xs) (ty ': y2 ': ys) (txy ': xy2 ': xys) where
+    hUnzip xy = case splitVariant1 xy of
+      Left (x,y) -> (mkVariant (Label :: Label t) x Proxy,
+                     mkVariant (Label :: Label t) y Proxy)
+      Right xy' | (x,y) <- hUnzip xy' ->
+                    (extendVariant x,
+                     extendVariant y)
+
+instance (Unvariant '[txy] txy,
+          tx ~ Tagged t x,
+          ty ~ Tagged t y,
+          txy ~ Tagged t (x,y))
+      => HUnzip Variant '[tx] '[ty] '[txy] where
+    hUnzip xy | Tagged (x,y) <- unvariant xy =
+        (mkVariant1 Label x, mkVariant1 Label y)
+
+
+-- ** with a record
+
+{- | Apply a record of functions to a variant of values.
+The functions are selected based on those having the same
+label as the value.
+
+-}
+class (SameLength v v',
+       SameLabels v v')  => ZipVR fs v v' | fs v -> v' where
+    -- | 'zipVR' is probably a better choice in most
+    -- situations, since it requires that @fs@ has one function for every
+    -- element of @v@
+    zipVR_ :: Record fs -> Variant v -> Variant v'
+
+instance (lv ~ Tagged l v,
+          lv' ~ Tagged l v',
+          HMemberM (Label l) (LabelsOf fs) b,
+          HasFieldM l (Record fs) f,
+          DemoteMaybe (v -> v) f ~ (v -> v'),
+          MkVariant l v' (lv' ': rs),
+          ZipVR fs vs rs) =>
+          ZipVR fs (lv ': vs) (lv' ': rs) where
+    zipVR_ r lvs = case splitVariant1 lvs of
+                  Left v | v' <- hLookupByLabelM l r (id :: v -> v) v -> mkVariant l v' Proxy
+                  Right vs -> extendVariant $ zipVR_ r vs
+      where l = Label :: Label l
+
+
+instance ZipVR fs '[] '[] where
+    zipVR_ _ x = x
+
+{- |
+
+>>> let xy = x .*. y .*. emptyProxy
+>>> let p = Proxy `asLabelsOf` xy
+>>> let vs = [ mkVariant x 1.0 p, mkVariant y () p ]
+
+
+>>> zipVR (hBuild (+1) id) `map` vs
+[V{x=2.0},V{y=()}]
+
+
+-}
+zipVR :: (SameLabels fs v, SameLength fs v, ZipVR fs v v',
+          ZipVRCxt fs v v')
+    => Record fs -> Variant v -> Variant v'
+zipVR = zipVR_
+
+
+{- | Lets 'zipVR' act as if @'ZipVR' fs v v'@ had an FD @v v' -> fs@
+
+> ZipVRCxt [Tagged s f,  Tagged t g]
+>          [Tagged s fx, Tagged t gx]
+>          [Tagged s fy, Tagged t gy]
+>   = (f ~ (fx -> fy), g ~ (gx -> gy))
+
+-}
+type family ZipVRCxt (fs :: [*]) (xs :: [*]) (ys :: [*]) :: Constraint
+
+type instance ZipVRCxt (Tagged s f ': fs) (Tagged s x ': xs) (Tagged s y ': ys) =
+        (f ~ (x -> y), ZipVRCxt fs xs ys)
+type instance ZipVRCxt '[] '[] '[] = ()
+
+-- * Eq
+instance Eq (Variant '[]) where
+  _ == _ = True
+
+instance (Eq (Variant xs), Eq x) => Eq (Variant (x ': xs)) where
+  v == v' = case (splitVariant1' v, splitVariant1' v') of
+    (Left l, Left r) -> l == r
+    (Right l, Right r) -> l == r
+    _ -> False
+
+-- ** Alternative Eq
+-- | implemented like @and (zipWith (==) xs ys)@. Behaves the same as the Eq instances for 'Variant'
+eqVariant v v' = maybe False (hMapOutV UncurryEq) $ zipVariant v v'
+
+data UncurryEq = UncurryEq
+
+instance (ee ~ (e,e), Eq e, bool ~ Bool) =>
+    ApplyAB UncurryEq ee bool where
+      applyAB _ (e,e') = e == e'
+
+-- * Ord
+instance Ord (Variant '[]) where
+  compare _ _ = EQ
+
+instance (Ord x, Ord (Variant xs)) => Ord (Variant (x ': xs)) where
+  compare a b = compare (splitVariant1' a) (splitVariant1' b)
+
+-- * Bounded
+instance (Bounded x, Bounded z,
+          HRevAppR (Tagged s x ': xs) '[] ~ (Tagged t z ': sx),
+          MkVariant t z (Tagged s x ': xs))
+        => Bounded (Variant (Tagged s x ': xs)) where
+  minBound = mkVariant (Label :: Label s) (minBound :: x) Proxy
+  maxBound = mkVariant (Label :: Label t) (maxBound :: z) Proxy
+
+-- * Enum
+{- |
+
+>>> let t = minBound :: Variant '[Tagged "x" Bool, Tagged "y" Bool]
+>>> [t .. maxBound]
+[V{x=False},V{x=True},V{y=False},V{y=True}]
+
+
+[@Odd behavior@]
+There are some arguments that this instance should not exist.
+
+The last type in the Variant does not need to be Bounded. This
+means that 'enumFrom' behaves a bit unexpectedly:
+
+>>> [False .. ]
+[False,True]
+
+>>> [t .. ]
+[V{x=False},V{x=True},V{y=False},V{y=True},V{y=*** Exception: Prelude.Enum.Bool.toEnum: bad argument
+
+This is a \"feature\" because it allows an @Enum (Variant '[Tagged \"a\" Bool, Tagged \"n\" 'Integer'])@
+
+Another difficult choice is that the lower bound is @fromEnum 0@ rather than @minBound@:
+
+>>> take 5 [ minBound :: Variant '[Tagged "b" Bool, Tagged "i" Int] .. ]
+[V{b=False},V{b=True},V{i=0},V{i=1},V{i=2}]
+
+-}
+instance (Enum x, Bounded x, Enum (Variant (y ': z))) => Enum (Variant (Tagged s x ': y ': z)) where
+  fromEnum v = case splitVariant1 v of
+    Left x -> fromEnum x
+    Right yz -> 1 + fromEnum (maxBound :: Tagged s x) + fromEnum yz
+
+  toEnum n
+      | m >= n = mkVariant (Label :: Label s) (toEnum n) Proxy
+      | otherwise = extendVariant $ toEnum (n - m - 1)
+    where m = fromEnum (maxBound :: Tagged s x)
+
+{- |
+
+While the instances could be written Enum (Variant '[])
+Eq/Ord which cannot produce values, so they have instances for
+empty variants ('unsafeEmptyVariant'). Enum can produce values,
+so it is better that @fromEnum 0 :: Variant '[]@ fails with No instance for
+@Enum (Variant '[])@ than producing an invalid variant.
+
+-}
+instance Enum x => Enum (Variant '[Tagged s x]) where
+  fromEnum v = case splitVariant1 v of
+    Left x -> fromEnum x
+    _ -> error "Data.HList.Variant fromEnum impossible"
+  toEnum n = mkVariant (Label :: Label s) (toEnum n) Proxy
+
+-- * Ix (TODO)
+
+
+-- * Monoid
+instance (Unvariant '[Tagged t x] x, Monoid x) => Monoid (Variant '[Tagged t x]) where
+    mempty = mkVariant (Label :: Label t) mempty Proxy
+    mappend a b = case (unvariant a, unvariant b) of
+                    (l, r) -> mkVariant (Label :: Label t) (mappend l r) Proxy
+
+
+-- | XXX check this mappend is legal
+instance (Monoid x, Monoid (Variant (a ': b))) => Monoid (Variant (Tagged t x ': a ': b)) where
+    mempty = extendVariant mempty
+    mappend a b = case (splitVariant1 a, splitVariant1 b) of
+                    (Left l, Left r) -> mkVariant (Label :: Label t) (mappend l r) Proxy
+                    (Left l, _) -> mkVariant (Label :: Label t) l Proxy
+                    (_, Left r) -> mkVariant (Label :: Label t) r Proxy
+                    (Right l, Right r) -> extendVariant $ mappend l r
+
+-- * Projection
+
+{- | convert a variant with more fields into one with fewer (or the same)
+fields.
+
+
+>>> let ty = Proxy :: Proxy [Tagged "left" Int, Tagged "right" Int]
+>>> let l = mkVariant _left 1 ty
+>>> let r = mkVariant _right 2 ty
+
+
+>>> map projectVariant [l, r] :: [Maybe (Variant '[Tagged "left" Int])]
+[Just V{left=1},Nothing]
+
+
+@'rearrangeVariant' = 'fromJust' . 'projectVariant'@ is one implementation
+of 'rearrangeVariant', since the result can have the same fields with
+a different order:
+
+>>> let yt = Proxy :: Proxy [Tagged "right" Int, Tagged "left" Int]
+
+>>> map projectVariant [l, r] `asTypeOf` [Just (mkVariant _left 0 yt)]
+[Just V{left=1},Just V{right=2}]
+
+
+-}
+class ProjectVariant x y where
+    projectVariant :: Variant x -> Maybe (Variant y)
+
+instance (ProjectVariant x ys,
+          HasField t (Variant x) (Maybe y),
+          HOccursNot (Label t) (LabelsOf ys),
+          ty ~ Tagged t y)
+  => ProjectVariant x (ty ': ys) where
+    projectVariant x = y `mplus` ys
+      where t = Label :: Label t
+            y = (\v -> mkVariant t v Proxy) <$> x .!. t
+            ys = (mty  .*.) <$> (projectVariant x :: Maybe (Variant ys))
+            mty = Tagged Nothing :: Tagged t (Maybe y)
+
+instance ProjectVariant x '[] where
+    projectVariant _ = Nothing
+
+
+
+{- | @projectExtendVariant = fmap 'extendVariant' . 'projectVariant'@
+
+where intermediate variant is as large as possible. Used to implement
+"Data.HList.Labelable".'projected'
+
+Note that:
+
+>>> let r = projectExtendVariant (mkVariant1 Label 1 :: Variant '[Tagged "x" Int])
+>>> r :: Maybe (Variant '[Tagged "x" Integer])
+Nothing
+
+-}
+class HAllTaggedLV y => ProjectExtendVariant x y where
+    projectExtendVariant :: Variant x -> Maybe (Variant y)
+
+instance HAllTaggedLV y => ProjectExtendVariant '[] y where
+    projectExtendVariant _ = Nothing
+
+instance (lv ~ Tagged l v,
+          HMemberM lv y inY,
+          ProjectExtendVariant' inY lv y,
+          ProjectExtendVariant xs y
+      ) => ProjectExtendVariant (lv ': xs) y where
+  projectExtendVariant v = case splitVariant1' v of
+      Left lv -> projectExtendVariant' (Proxy :: Proxy inY) lv
+      Right v' -> projectExtendVariant v'
+
+
+class ProjectExtendVariant' (inY :: Maybe [*]) lv (y :: [*]) where
+    projectExtendVariant' :: Proxy inY -> lv -> Maybe (Variant y)
+
+instance ProjectExtendVariant' Nothing lv y where
+    projectExtendVariant' _ _ = Nothing
+
+instance (MkVariant l v y, lv ~ Tagged l v) => ProjectExtendVariant' (Just t) lv y where
+    projectExtendVariant' _ (Tagged v) = Just (mkVariant (Label :: Label l) v Proxy)
+
+
+
+class (ProjectVariant x yin,
+       ProjectVariant x yout) => SplitVariant x yin yout where
+    splitVariant :: Variant x -> Either (Variant yin) (Variant yout)
+
+instance
+   (-- implementation
+    ProjectVariant x yin,
+    ProjectVariant x yout,
+
+    -- constraints to ensure exactly one of
+    -- the uses of projectVariant gives a Just
+    H2ProjectByLabels (LabelsOf yin) x xi xo,
+    HRearrange (LabelsOf yin) xi yin,
+    HRearrange (LabelsOf yout) xo yout,
+
+    HLeftUnion xi xo xixo,
+    HRearrange (LabelsOf x) xixo x,
+
+    -- probably redundant
+    HAllTaggedLV x, HAllTaggedLV yin, HAllTaggedLV yout) =>
+  SplitVariant x yin yout where
+  splitVariant x = case (projectVariant x, projectVariant x) of
+   (Nothing, Just yout) -> Right yout
+   (Just yin, Nothing) -> Left yin
+   _ -> error "Data.HList.Variant:splitVariant impossible"
+
+-- | @projectVariant . extendsVariant = Just@ (when the types match up)
+--
+-- 'extendVariant' is a special case
+class (HAllTaggedLV y, HAllTaggedLV x) => ExtendsVariant x y where
+    extendsVariant :: Variant x -> Variant y
+
+instance (MkVariant l e y, le ~ Tagged l e,
+          ExtendsVariant (b ': bs) y) => ExtendsVariant (le ': b ': bs) y where
+    extendsVariant v = case splitVariant1 v of
+        Left e -> mkVariant (Label :: Label l) (e :: e) Proxy
+        Right vs -> extendsVariant vs
+
+instance (HAllTaggedLV x, Unvariant '[le] e, MkVariant l e x,
+          le ~ Tagged l e) => ExtendsVariant '[le] x where
+    extendsVariant v = mkVariant (Label :: Label l) (unvariant v) Proxy
+
+
+-- | @rearrangeVariant@ is a specialization of 'extendsVariant' whose
+-- result is always . see also 'rearranged'
+rearrangeVariant :: (SameLength v v', ExtendsVariant v v')
+      => Variant v -> Variant v'
+rearrangeVariant v = extendsVariant v
+
+instance (SameLength s a, ExtendsVariant s a,
+          SameLength b t, ExtendsVariant b t) => Rearranged Variant s t a b
+  where
+    rearranged = iso rearrangeVariant rearrangeVariant
+
+-- | @Prism (Record tma) (Record tmb) (Variant ta) (Variant tb)@
+--
+-- see 'hMaybied''
+hMaybied x = prism variantToHMaybied
+    (\s -> case hMaybiedToVariants s of
+          [a] -> Right a
+          _ -> Left (hMapR HCastF s))
+    x
+
+
+data HCastF = HCastF
+
+instance (mx ~ Maybe x,
+          my ~ Maybe y,
+          HCast y x) =>
+  ApplyAB HCastF mx my where
+    applyAB _ x = hCast =<< x
+
+
+
+{- | @Prism' (Record tma) (Variant ta)@
+
+where @tma@ and @tmb@ are lists like
+
+> tma ~ '[Tagged x (Maybe a), Tagged y (Maybe b)]
+> ta  ~ '[Tagged x        a , Tagged y        b ]
+
+If one element of the record is Just, the Variant will
+contain that element. Otherwise, the prism fails.
+
+[@Note@]
+
+The types work out to define a prism:
+
+@l = 'prism'' 'variantToHMaybied' ('listToMaybe' . 'hMaybiedToVariants')@
+
+but the law: @s^?l ≡ Just a ==> l # a ≡ s@ is not followed,
+because we could have:
+
+@
+  s, s2 :: Record '[Tagged "x" (Maybe Int), Tagged "y" (Maybe Char)]
+  s = hBuild (Just 1) (Just '2')
+  s2 = hBuild (Just 1) Nothing
+
+  v :: Variant '[Tagged "x" Int, Tagged "y" Char]
+  v = mkVariant (Label :: Label "x") 1 Proxy
+@
+
+So that @s^?l == Just v@. But @l#v == s2 /= s@, while the law
+requires @l#v == s@. hMaybied avoids this problem by only
+producing a value when there is only one present.
+
+-}
+hMaybied' x = simple (hMaybied (simple x))
+
+class VariantToHMaybied v r | v -> r, r -> v where
+    variantToHMaybied :: Variant v -> Record r
+
+instance VariantToHMaybied '[] '[] where
+    variantToHMaybied _ = emptyRecord
+
+instance (VariantToHMaybied v r,
+          HReplicateF nr ConstTaggedNothing () r,
+
+          tx ~ Tagged t x,
+          tmx ~ Tagged t (Maybe x))
+    => VariantToHMaybied (tx ': v) (tmx ': r) where
+      variantToHMaybied v = case splitVariant1 v of
+            Left x -> Record
+                $ HCons (Tagged (Just x))
+                $ hReplicateF Proxy ConstTaggedNothing ()
+            Right rest ->
+                case variantToHMaybied rest of
+                  Record a -> Record $ (Tagged Nothing :: Tagged t (Maybe x)) `HCons` a
+          -- don't use (.*.) because we have (LabelsOf v ~ LabelsOf r), so
+          -- the duplicate check (HRLabelSet) implied by (.*.) is redundant
+
+data ConstTaggedNothing = ConstTaggedNothing
+instance (y ~ Tagged t (Maybe e)) => ApplyAB ConstTaggedNothing x y where
+    applyAB _ _ = Tagged Nothing
+
+-- | Every element of the record that is Just becomes one element
+-- in the resulting list. See 'hMaybied'' example types that @r@
+-- and @v@ can take.
+hMaybiedToVariants ::
+  (HFoldr HMaybiedToVariantFs [Variant '[]] r [Variant v], -- impl
+   VariantToHMaybied v r -- evidence for typechecking
+  ) => Record r -> [Variant v]
+hMaybiedToVariants (Record r) = hFoldr HMaybiedToVariantFs ([] :: [Variant '[]]) r
+
+data HMaybiedToVariantFs = HMaybiedToVariantFs
+
+instance (x ~ (Tagged t (Maybe e), [Variant v]),
+          y ~ [Variant (Tagged t e ': v)],
+          MkVariant t e (Tagged t e ': v))
+        => ApplyAB HMaybiedToVariantFs x y where
+
+  applyAB _ (Tagged me, v) = case me of
+    Just e -> mkVariant (Label :: Label t) e Proxy : map extendVariant v
+    _ -> fmap extendVariant v
diff --git a/Data/HList/broken/Label5.hs b/Data/HList/broken/Label5.hs
deleted file mode 100644
--- a/Data/HList/broken/Label5.hs
+++ /dev/null
@@ -1,42 +0,0 @@
-{-# LANGUAGE FlexibleInstances, UndecidableInstances, MultiParamTypeClasses #-}
-
-{- |
-   The HList library
-
-   (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
-
-   Yet another model of labels.
-
-   This model allow us to use any type as label type.
-   As a result, we need some generic instances.
-
-   Also, type errors may be more confusing now.
--}
-
-module Data.HList.Label5 where
-
-import Data.Typeable
-import Data.Char
-import Data.HList.FakePrelude
-import Data.HList.Record
-
-
--- | Equality on labels
-
--- instance TypeEq x y b => HEq x y b
-
-
--- | Show label
-
-instance Typeable x => ShowLabel x
- where
-  showLabel = (\(x:xs) -> toLower x:xs)
-            . reverse
-            . takeWhile (not . (==) '.')
-            . reverse
-            . show
-{-
-            . tyConString
-            . typeRepTyCon
--}
-            . typeOf
diff --git a/Data/HList/broken/RecordAdv.hs b/Data/HList/broken/RecordAdv.hs
--- a/Data/HList/broken/RecordAdv.hs
+++ b/Data/HList/broken/RecordAdv.hs
@@ -32,8 +32,6 @@
 --
 -- Replace a proxy by a value of the proxied type.
 -- The signature is inferred
-hUnproxyLabel :: (HFind l (RecordLabels r) n,HUpdateAtHNat n (LVPair l v) r,HasField l (Record r) (Proxy v)) =>
-    Label l-> v -> Record r -> Record (HUpdateAtHNatR n (LVPair l v) r)
 hUnproxyLabel l v r = hUpdateAtLabel l v r
  where
   tpe :: a -> Proxy a -> ()
@@ -45,8 +43,7 @@
 {-|
   This is a variation on updating, so use the same fixity as (.\@.).
 -}
-f@(LVPair v) .^. r = hUnproxyLabel (labelLVPair f) v r
-
+f@(Tagged v) .^. r = hUnproxyLabel (labelLVPair f) v r
 
 -- --------------------------------------------------------------------------
 
@@ -61,10 +58,11 @@
 class HasNoProxies (l :: [*])
 instance HasNoProxies '[]
 instance Fail (ProxyFound x) => HasNoProxies (Proxy x ': l)
-instance Fail (ProxyFound x) => HasNoProxies (LVPair lab (Proxy x) ': l)
+instance Fail (ProxyFound x) => HasNoProxies (Tagged lab (Proxy x) ': l)
 instance HasNoProxies l => HasNoProxies (e ': l)
 
 
+
 -- --------------------------------------------------------------------------
 
 -- | Narrow a record to a different record type
@@ -75,16 +73,16 @@
 class  NarrowM a b res | a b -> res where
     narrowM :: Record a -> Record b -> res
 
-instance NarrowM a '[] (HJust (Record '[])) where
+instance res ~ HJust (Record '[]) => NarrowM a '[] res where
 
     narrowM _ _ = HJust emptyRecord
 
 instance (H2ProjectByLabels (l ': '[]) a rin rout,
           NarrowM' rin rout b res)
-    => NarrowM a (LVPair l v ': b) res where
+    => NarrowM a (Tagged l v ': b) res where
     narrowM (Record a) _ = narrowM' (Record rin) (Record rout) (undefined:: Record b)
      where
-        (rin,rout) = h2projectByLabels (proxy :: Proxy (l ': '[])) a
+        (rin,rout) = h2projectByLabels (Proxy :: Proxy (l ': '[])) a
 
 -- | could be changed to type family
 class  NarrowM' rin rout b res | rin rout b -> res where
@@ -99,10 +97,10 @@
     narrowM' (Record (f `HCons` HNil)) rout b =
         narrowM'' f (narrowM rout b)
 
-class  NarrowM'' f r r' where
+class  NarrowM'' f r r' | f r -> r' where
     narrowM'' :: f -> r -> r'
 
-instance (hNothing ~ HNothing) => NarrowM'' f HNothing hNothing where
+instance NarrowM'' f HNothing HNothing where
     narrowM'' _ _ = HNothing
 
 instance NarrowM'' f (HJust (Record r)) (HJust (Record (f ': r))) where
@@ -116,10 +114,10 @@
  where   narrow _ = emptyRecord
 
 instance ( Narrow rout r'
-         , H2ProjectByLabels (l ': '[]) r (LVPair l v ': '[]) rout
-         ) => Narrow r ( LVPair l v ': r' )
+         , H2ProjectByLabels (l ': '[]) r (Tagged l v ': '[]) rout
+         ) => Narrow r ( Tagged l v ': r' )
   where
-    narrow (Record r) = case h2projectByLabels (proxy::Proxy (l ': '[])) r of
+    narrow (Record r) = case h2projectByLabels (Proxy::Proxy (l ': '[])) r of
         (HCons f HNil,rout) -> let (Record r')    = narrow (Record rout)
             in Record (HCons f r')
 
@@ -132,7 +130,7 @@
  where
   lubNarrow :: Record a -> Record b -> (Record c, Record c)
 
-instance ( HTIntersect (RecordLabels a) (RecordLabels b) lc
+instance ( HTIntersect (LabelsOf a) (LabelsOf b) lc
          , H2ProjectByLabels lc a c aout
          , H2ProjectByLabels lc b c bout
          , HRLabelSet c
@@ -141,16 +139,16 @@
  where
 
  lubNarrow ra@(Record _) rb@(Record _) =
-     ( hProjectByLabels (proxy::Proxy lc) ra
-     , hProjectByLabels (proxy::Proxy lc) rb
+     ( hProjectByLabels (Proxy::Proxy lc) ra
+     , hProjectByLabels (Proxy::Proxy lc) rb
      )
 
 
-{-
 -- --------------------------------------------------------------------------
 
 -- | List constructors that also LUB together
 
+{-
 data NilLub
 nilLub :: NilLub
 nilLub = undefined
@@ -176,33 +174,32 @@
 
 class HLub l e | l -> e
  where
-  hLub :: l -> [e]
+  hLub :: HList l -> [e]
 
 instance ( LubNarrow h h' e
          )
-      => HLub (HCons h (HCons h' HNil)) e
+      => HLub [h,h'] e
  where
   hLub (HCons h (HCons h' _)) = [fst ee, snd ee]
    where
     ee = lubNarrow h h'
 
-instance ( HLub (HCons h (HCons h'' t)) e'
-         , HLub (HCons h' (HCons h'' t)) e''
+instance ( HLub (h ': h'' ': t) e'
+         , HLub (h' ': h'' ': t) e''
          , LubNarrow e' e'' e
-         , HLub (HCons e (HCons h'' t)) e
+         , HLub (e ': h'' ': t) e
          )
-      => HLub (HCons h (HCons h' (HCons h'' t))) e
+      => HLub (h ': h' ': h'' ': t) e
  where
-  hLub (HCons h (HCons h' t)) = fst e : ( snd e : tail r )
+  hLub (HCons h (HCons h' t)) = fst e : snd e : tail r
    where
     e' = hLub (HCons h t)
     e'' = hLub (HCons h' t)
     e = lubNarrow (head e') (head e'')
     r = hLub (HCons (fst e) t)
+    -}
 
 
--}
-
 -- --------------------------------------------------------------------------
 -- | Record equivalence modulo field order
 --
@@ -227,13 +224,13 @@
 -- field uniquely determines the type of the field value. Therefore, we
 -- should not use equivR on two records with inconsistent labeling...
 
-class RecordEquiv r1 r2 res | r1 r2 -> res where
+class RecordEquiv r1 r2 res where
     equivR :: r1 -> r2 -> res
 
 
 instance (HEq r1 r2 b, RecordEquiv' b r1 r2 res)
     => RecordEquiv r1 r2 res where
-    equivR _ _ = equivR' (undefined::Proxy b) (undefined::r1) (undefined::r2)
+    equivR _ _ = equivR' (Proxy::Proxy b) (undefined::r1) (undefined::r2)
 -- Two records have the same type: the fast path
 instance RecordEquiv' True r r
                       (HJust (r->r,r->r)) where
@@ -242,14 +239,15 @@
 instance (NarrowM r1 r2 r12, NarrowM r2 r1 r21,
           RecordEquiv' False (Record r1->r12) (Record r2->r21) res)
     => RecordEquiv (Record r1) (Record r2) res where
-    equivR r1 r2 = equivR' (undefined :: Proxy False) r1p r2p
+    equivR r1 r2 = equivR' (Proxy :: Proxy False) r1p r2p
      where r1p r1 = narrowM (r1 :: Record r1) r2
            r2p r2 = narrowM (r2 :: Record r2) r1
 
 class RecordEquiv' (b :: Bool) pj1 pj2 res | b pj1 pj2 -> res where
     equivR' :: Proxy b -> pj1 -> pj2 -> res
 
-instance RecordEquiv' False (r1->HJust r2) (r2->HJust r1) (HJust (r1->r2,r2->r1))
+instance res ~ HJust (r1->r2,r2->r1) =>
+      RecordEquiv' False (r1->HJust r2) (r2->HJust r1) res
     where
     equivR' _ r12 r21 = HJust (unj.r12,unj.r21)
      where unj (HJust x) = x
@@ -262,45 +260,9 @@
 instance RecordEquiv' False (r1->HJust r2) (r2->HNothing) HNothing where
     equivR' _ _ _ = HNothing
 
-{-
 
 -- --------------------------------------------------------------------------
--- Typeable instances
-
-hNilTcName :: TyCon
-hNilTcName = mkTyCon "HList.HNil"
-instance Typeable HNil
- where
-  typeOf _ = mkTyConApp hNilTcName []
-
-hConsTcName :: TyCon
-hConsTcName = mkTyCon "HList.HCons"
-instance (Typeable x, Typeable y) => Typeable (HCons x y)
- where
-  typeOf ~(HCons x y)
-   = mkTyConApp hConsTcName [ typeOf x, typeOf y ]
-
-recordTcName :: TyCon
-recordTcName = mkTyCon "HList.Record"
-instance Typeable x => Typeable (Record x)
- where
-  typeOf ~(Record x)
-   = mkTyConApp recordTcName [ typeOf x ]
-
-hFieldTcName :: TyCon
-hFieldTcName = mkTyCon "HList.F"
-instance (Typeable x, Typeable y) => Typeable (LVPair x y)
- where
-  typeOf _
-   = mkTyConApp hFieldTcName [ typeOf (undefined::x), typeOf (undefined::y)  ]
-
-proxyTcName :: TyCon
-proxyTcName = mkTyCon "HList.Proxy"
-instance Typeable x => Typeable (Proxy x)
- where
-  typeOf _
-   = mkTyConApp proxyTcName [ typeOf (undefined::x) ]
-
+-- * Typeable instances
+-- TODO: see Data.HList.Data
 
--}
 
diff --git a/Data/HList/broken/RecordOrd.hs b/Data/HList/broken/RecordOrd.hs
new file mode 100644
--- /dev/null
+++ b/Data/HList/broken/RecordOrd.hs
@@ -0,0 +1,146 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell #-}
+-- | records with Symbol labels that are sorted. Not enabled yet
+-- because CmpSymbol comes with ghc>=7.8 and ghc-7.6 is not dropped yet
+module Data.HList.RecordOrd where
+
+import Data.HList.HList
+import Data.HList.HListPrelude
+import Data.HList.FakePrelude
+import Data.Tagged
+
+import GHC.TypeLits
+import Data.Type.Equality
+
+import Data.HList.Record
+
+-- two-class version
+class InsertOrd1 kv l l' | kv l -> l' where
+    insertOrd1 :: kv -> HList l -> HList l'
+
+class InsertOrd2 (b :: Ordering) kv l l' | b kv l -> l' where
+    insertOrd2 :: Proxy b -> kv -> HList l -> HList l'
+
+
+instance InsertOrd2 LT kv l (kv ': l) where
+    insertOrd2 _ kv l = HCons kv l
+
+instance (x ~ (l1 ': rest),
+          y ~ (l1 ': rest'),
+          InsertOrd1 kv rest rest') => InsertOrd2 GT kv x y where
+    insertOrd2 _ kv (HCons l1 rest) = HCons l1 (insertOrd1 kv rest)
+    insertOrd2 _ _ _ = error "Data.HList.RecordOrd: ghc bug"
+
+
+instance Fail (DuplicatedLabel kv) => InsertOrd2 EQ kv x '[DuplicatedLabel kv] where
+    insertOrd2 _ _ _ = error "Data.HList.RecordOrd: there must be no instances of Fail"
+
+
+instance (  b ~ CmpSymbol k k',
+            taggedkv ~ Tagged k v,
+            taggedk'v' ~ Tagged k' v',
+            InsertOrd2 b taggedkv (taggedk'v' ': kvs) kvs')
+          => InsertOrd1 taggedkv (taggedk'v' ': kvs) kvs' where
+    insertOrd1 kv kvs = insertOrd2 (Proxy :: Proxy b) kv kvs
+
+instance InsertOrd1 x '[] '[x] where
+    insertOrd1 kv _ = HCons kv HNil
+
+data InsertOrd = InsertOrd
+instance (x ~ (e, HList r),
+          InsertOrd1 e r r',
+          y ~ HList r'
+        )=> ApplyAB InsertOrd x y where
+  applyAB _ (e,r) = insertOrd1 e r
+
+hSort' xs = hFoldr InsertOrd HNil xs
+
+
+class HSorted (r :: [*])
+
+instance HSorted '[]
+instance HSorted '[x]
+instance (HSorted (ty ': rest),
+          tx ~ Tagged x vx,
+          ty ~ Tagged y vy,
+          CmpSymbol x y ~ LT) 
+      => HSorted (tx ': ty ': rest)
+
+
+{-
+
+In principle this could be an appropriate proxy to use
+for Variant: once the field ordering and number of fields
+is fixed, as it would be for
+
+_left = Label :: Label "left"
+_right = Label :: Label "right"
+
+(v,w) = case sortedProxyLen (hSucc (hSucc hZero)) of
+              e -> (mkVariant _left 'x' e,
+                    mkVariant _right (5 :: Int) e)
+
+Then:
+
+v :: (HasField' b1 "right" '[Tagged x a, Tagged y a1] Int,
+      HasField' b "left" '[Tagged x a, Tagged y a1] Char,
+      HFind2 b1 "right" '[y] n1, HFind2 b "left" '[y] n,
+      HNat2Integral n1, HNat2Integral n, HEq "right" x b1,
+      HEq "left" x b, GHC.TypeLits.CmpSymbol x y ~ 'LT) =>
+     Variant '[Tagged x a, Tagged y a1]
+
+Does not reduce to v :: Variant '[Tagged "left" Char, Tagged "right" Int]
+
+If only ghc would backtrack after trying (x ~ "left") or (y ~ "right")
+and see that only one will satisfy @CmpSymbol x y ~ LT@,
+
+
+-}
+sortedProxyLen :: (HSorted r,
+                   SameLength' (HReplicateR n ()) r_,
+                   HMapCxt (HFmap (Fun '[] ())) r r_) 
+        => Proxy n -> Proxy r
+sortedProxyLen _ = Proxy
+
+class HSort xs xs' where
+    hSort :: SameLength xs xs' => Record xs -> Record xs'
+
+instance HFoldr InsertOrd (HList '[]) xs (HList xs') => HSort xs xs' where
+    hSort (Record xs) = Record (hSort' xs)
+
+{- two type family version
+type family InsertOrd1 kv kvs where
+  InsertOrd1 kv '[] = '[kv]
+  InsertOrd1 (Tagged k v) (Tagged k' v' ': kvs) =
+      InsertOrd2
+        (CmpSymbol k k')
+        (Tagged k v)
+        (Tagged k' v' ': kvs)
+
+type family InsertOrd2 (b :: Ordering) (kv :: *) (kvs :: [*]) where
+  InsertOrd2 b x '[] = '[x]
+  InsertOrd2 GT (Tagged k v) (Tagged k' v' ': kvs) =
+          InsertOrd1 (Tagged k v) (Tagged k' v' ': kvs)
+  InsertOrd2 LT kv kvs = kv ': kvs
+  InsertOrd2 EQ kv kvs = Err (DuplicatedLabel kv) '[]
+
+-- | Error messages: http://www.haskell.org/pipermail/haskell-cafe/2013-November/111549.html
+type family Err (x::k) (a :: j) :: j where
+   Err x a = Err x (Err "infinite loop to bring this to your attention: don't raise the context stack please" a)
+-}
+
+
+{-
+
+-- Narrowing records
+class (HRLabelSet a, HRLabelSet b) => Narrow a b where
+    narrow :: Record a -> Record b
+
+instance (HRLabelSet a,
+          HRLabelSet b,
+          HRLabelSet bOut,
+          H2ProjectByLabels (RecordLabels b :: [k]) a b bOut)
+      => Narrow a b where
+    narrow a = case hProjectByLabels2 (Proxy :: Proxy (RecordLabels b :: [k])) a
+      of (b :: Record b, _bOut :: Record bOut) -> b
+      -}
diff --git a/Data/HList/broken/RecordP.hs b/Data/HList/broken/RecordP.hs
--- a/Data/HList/broken/RecordP.hs
+++ b/Data/HList/broken/RecordP.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE PolyKinds #-}
 {-# LANGUAGE TypeOperators #-}
 {-# LANGUAGE DataKinds #-}
@@ -33,23 +34,18 @@
 -- --------------------------------------------------------------------------
 -- Record types as Phantom labels with values
 
-newtype RecordP (ls::[*]) vs = RecordP (HList vs)
+newtype RecordP (ls::[k]) vs = RecordP (HList vs)
 
 
 -- Build a record. I wonder if the 'ls' argument of mkRecordP can be
 -- removed. So far, we had no need for it...
 
-mkRecordP :: (HSameLength ls vs, HLabelSet ls) => ls -> vs -> RecordP ls vs
+mkRecordP :: (SameLength ls vs, HLabelSet ls) => proxy ls -> HList vs -> RecordP ls vs
 mkRecordP _ vs = RecordP vs
 
--- The contraint that two type level lists have the same length
-class HSameLength l1 l2
-instance HSameLength '[] '[]
-instance HSameLength l1 l2 => HSameLength (e1 ': l1) (e2 ': l2)
-
 -- Build an empty record
 emptyRecordP :: RecordP ('[]) ('[])
-emptyRecordP = mkRecordP HNil HNil
+emptyRecordP = mkRecordP Proxy HNil
 
 -- Converting between RecordP and Record (label/value pairs)
 
@@ -62,16 +58,14 @@
     record_r2p _ = emptyRecordP
     record_p2r _ = emptyRecord
 
-{-
-instance (RecordR2P r ls vs, HRLabelSet (HCons (LVPair l v) r),
-          HLabelSet (HCons l ls), HSameLength ls vs)
-    => RecordR2P (HCons (LVPair l v) r) (HCons l ls) (HCons v vs) where
-    record_r2p (Record (HCons f r)) = hExtend f (record_r2p (Record r))
-    record_p2r (RecordP (HCons v r)) = hExtend (LVPair v) (record_p2r (RecordP r))
-
-labels_of_recordp :: RecordP ls vs -> ls
-labels_of_recordp = undefined
+instance (RecordR2P r ls vs, HRLabelSet (Tagged l v ': r),
+          HLabelSet (l ': ls), SameLength ls vs)
+    => RecordR2P (Tagged l v ': r) (l ': ls) (v ': vs) where
+    record_r2p (Record (HCons f r)) = f  .*. record_r2p (Record r)
+    record_p2r (RecordP (HCons v r)) = Tagged v .*. record_p2r (RecordP r)
 
+labels_of_recordp :: RecordP ls vs -> Proxy ls
+labels_of_recordp _ = Proxy
 
 -- --------------------------------------------------------------------------
 -- A Show instance to appeal to normal records
@@ -85,24 +79,29 @@
 -- --------------------------------------------------------------------------
 -- Extension for records
 
-instance (HLabelSet (HCons l ls), HSameLength ls vs)
-    => HExtend (LVPair l v) (RecordP ls vs) (RecordP (HCons l ls) (HCons v vs))
+instance (HLabelSet (l ': ls), SameLength ls vs)
+    => HExtend (Tagged l v) (RecordP ls vs)
  where
-  hExtend (LVPair v) (RecordP vs) = mkRecordP undefined (HCons v vs)
+  type HExtendR (Tagged l v) (RecordP ls vs) = RecordP (l ': ls) (v ': vs)
+  Tagged v .*. RecordP vs = mkRecordP Proxy (HCons v vs)
 
 
+
 -- --------------------------------------------------------------------------
 -- Record concatenation
 
 instance ( HLabelSet ls''
-         , HAppend ls ls' ls''
-         , HAppend vs vs' vs''
-         , HSameLength ls'' vs''
+         , HAppendR (RecordP ls vs) (RecordP ls' vs') ~ RecordP ls'' vs''
+         , HAppendList ls ls' ~ ls''
+         , HAppendList vs vs' ~ vs''
+         , SameLength ls'' vs''
          )
-    => HAppend (RecordP ls vs) (RecordP ls' vs') (RecordP ls'' vs'')
+    => HAppend (RecordP ls vs) (RecordP ls' vs')
  where
-  hAppend (RecordP vs) (RecordP vs') = mkRecordP undefined (hAppend vs vs')
+  hAppend (RecordP vs) (RecordP vs') = mkRecordP Proxy (hAppend vs vs')
 
+type instance HAppendR (RecordP ls vs) (RecordP ls' vs') = RecordP (HAppendR ls ls') (HAppendR vs vs')
+
 -- --------------------------------------------------------------------------
 -- Lookup operation
 
@@ -110,22 +109,23 @@
 -- implement it separately. The algorithm is familiar assq,
 -- only the comparison operation is done at compile-time
 
-instance (HEq l l' b, HasFieldP' b l (RecordP (HCons l' ls) vs) v)
-    => HasField l (RecordP (HCons l' ls) vs) v where
-    hLookupByLabel = hLookupByLabelP' (undefined::b)
+instance (HEq l l' b, HasFieldP' b l (l' ': ls) vs v)
+    => HasField l (RecordP (l' ': ls) vs) v where
+    hLookupByLabel = hLookupByLabelP' (Proxy :: Proxy b)
 
-class HasFieldP' b l r v | b l r -> v where
-    hLookupByLabelP' :: b -> l -> r -> v
+class HasFieldP' b l ls vs v | b l ls vs -> v where
+    hLookupByLabelP' :: Proxy b -> Label l -> RecordP ls vs -> v
 
-instance HasFieldP' HTrue l (RecordP (HCons l ls) (HCons v vs)) v where
+instance HasFieldP' True l (l ': ls) (v ': vs) v where
     hLookupByLabelP' _ _ (RecordP (HCons v _)) = v
 
 instance HasField l (RecordP ls vs) v
-    => HasFieldP' HFalse l (RecordP (HCons l' ls) (HCons v' vs)) v where
+    => HasFieldP' False l (l' ': ls) (v' ': vs) v where
     hLookupByLabelP' _ l (RecordP (HCons _ vs)) =
         hLookupByLabel l ((RecordP vs)::RecordP ls vs)
 
 
+{-
 -- --------------------------------------------------------------------------
 -- Delete operation
 hDeleteAtLabelP :: HProjectByLabelP l ls vs lso v vso =>
@@ -140,6 +140,7 @@
 hUpdateAtLabelP l v rp@(RecordP vs) = RecordP (hUpdateAtHNat n v vs)
  where
   n       = hFind l (labels_of_recordp rp)
+-}
 
 -- --------------------------------------------------------------------------
 -- Projection for records
@@ -148,21 +149,21 @@
 
 -- Project by a single label
 class HProjectByLabelP l ls vs lso v vso | l ls vs -> lso v vso where
-    h2ProjectByLabelP :: l -> RecordP ls vs -> (v,RecordP lso vso)
+    h2ProjectByLabelP :: Label l -> RecordP ls vs -> (v,RecordP lso vso)
 
-instance (HEq l l' b, HProjectByLabelP' b l (HCons l' ls) vs lso v vso)
-    => HProjectByLabelP l (HCons l' ls) vs lso v vso where
-    h2ProjectByLabelP = h2ProjectByLabelP' (undefined::b)
+instance (HEq l l' b, HProjectByLabelP' b l (l' ': ls) vs lso v vso)
+    => HProjectByLabelP l (l' ': ls) vs lso v vso where
+    h2ProjectByLabelP = h2ProjectByLabelP' (Proxy :: Proxy b)
 
 class HProjectByLabelP' b l ls vs lso v vso | b l ls vs -> lso v vso where
-    h2ProjectByLabelP' :: b -> l -> RecordP ls vs -> (v,RecordP lso vso)
+    h2ProjectByLabelP' :: Proxy b -> Label l -> RecordP ls vs -> (v,RecordP lso vso)
 
-instance HProjectByLabelP' HTrue l (HCons l ls) (HCons v vs) ls v vs where
+instance HProjectByLabelP' True l (l ': ls) (v ': vs) ls v vs where
     h2ProjectByLabelP' _ _ (RecordP (HCons v vs)) = (v,RecordP vs)
 
 instance (HProjectByLabelP l ls vs lso' v vso')
-    => HProjectByLabelP' HFalse l (HCons l' ls) (HCons v' vs)
-       (HCons l' lso') v (HCons v' vso') where
+    => HProjectByLabelP' False l (l' ': ls) (v' ': vs)
+       (l' ': lso') v (v' ': vso') where
     h2ProjectByLabelP' _ l (RecordP (HCons v' vs)) =
         let (v,RecordP vso) = h2ProjectByLabelP l ((RecordP vs)::RecordP ls vs)
         in (v, RecordP (HCons v' vso))
@@ -173,34 +174,36 @@
 -- classes H2ProjectByLabels and H2ProjectByLabels' are declared in
 -- Record.hs
 
-instance H2ProjectByLabels (HCons l ls)
-                           (RecordP HNil HNil) (RecordP HNil HNil)
-                           (RecordP HNil HNil)
+{- need to change H2ProjectByLabels kind variables back to * from [*]
+instance H2ProjectByLabels (l ': ls)
+                           (RecordP '[] '[]) (RecordP '[] '[])
+                           (RecordP '[] '[])
     where
     h2projectByLabels _ _ = (emptyRecordP,emptyRecordP)
 
 instance (HMember l' ls b,
-          H2ProjectByLabels' b ls (RecordP (HCons l' ls') vs') rin rout)
-    => H2ProjectByLabels ls (RecordP (HCons l' ls') vs') rin rout where
-    h2projectByLabels = h2projectByLabels' (undefined::b)
+          H2ProjectByLabels' b ls (RecordP (l' ': ls') vs') rin rout)
+    => H2ProjectByLabels ls (RecordP (l' ': ls') vs') rin rout where
+    h2projectByLabels = h2projectByLabels' (Proxy :: Proxy b)
 
 instance H2ProjectByLabels ls (RecordP ls' vs') (RecordP lin vin) rout =>
-    H2ProjectByLabels' HTrue ls (RecordP (HCons l' ls') (HCons v' vs'))
-                             (RecordP (HCons l' lin) (HCons v' vin)) rout where
+    H2ProjectByLabels' True ls (RecordP (l' ': ls') (v' ': vs'))
+                             (RecordP (l' ': lin) (v' ': vin)) rout where
     h2projectByLabels' _ ls (RecordP (HCons v' vs')) =
         (RecordP (HCons v' vin), rout)
         where (RecordP vin,rout) =
                   h2projectByLabels ls ((RecordP vs')::RecordP ls' vs')
 
 instance H2ProjectByLabels ls (RecordP ls' vs') rin (RecordP lo vo) =>
-    H2ProjectByLabels' HFalse ls (RecordP (HCons l' ls') (HCons v' vs'))
-                              rin (RecordP (HCons l' lo) (HCons v' vo)) where
+    H2ProjectByLabels' False ls (RecordP (l' ': ls') (v' ': vs'))
+                              rin (RecordP (l' ': lo) (v' ': vo)) where
     h2projectByLabels' _ ls (RecordP (HCons v' vs')) =
         (rin, RecordP (HCons v' vo))
         where (rin,RecordP vo) =
                   h2projectByLabels ls ((RecordP vs')::RecordP ls' vs')
-
+-}
 
+{-
 -- --------------------------------------------------------------------------
 -- Subtyping for records
 
diff --git a/Data/HList/broken/RecordU.hs b/Data/HList/broken/RecordU.hs
new file mode 100644
--- /dev/null
+++ b/Data/HList/broken/RecordU.hs
@@ -0,0 +1,462 @@
+{- | Description: records where elements are stored in unboxed arrays
+
+XXX works with ghc-7.8 and 7.6. Broken with GHC-7.10 RC1 on account of ghc
+bug #10009
+
+The public interface is exported from <Data-HList-CommonMain.html#t:RecordU RecordU>
+
+Export list (from CommonMain.hs)
+
+@
+ -- ** Unpacked / Unboxed Records
+ , RecordU
+ , RecordUS
+ , SortForRecordUS(..)
+ , HUpdateMany(..)
+ , hMapRU
+
+ -- *** internals for types
+ , HFindMany, HNats2Integrals(..)
+
+ , RecordUSCxt
+ , HLookupByHNatUS, HLookupByHNatUS1
+ , HSubtract, HMapUnboxF, UnboxF
+ , BoxF, EqTagValue, GetElemTy, ElemTyEq
+ , RecordToRecordU, RecordUToRecord
+
+
+
+ -- ** Record and RecordU
+ , unboxed, unboxed'
+ -- ** Record and RecordUS
+ , unboxedS, unboxedS'
+@
+-}
+module Data.HList.RecordU where
+
+import Data.Array.Unboxed
+import Data.HList.FakePrelude
+import Data.HList.Record
+import Data.HList.HList
+
+import Data.HList.HArray
+import Data.HList.HOccurs
+import LensDefs
+
+import Data.HList.Labelable
+
+import Unsafe.Coerce
+import GHC.Exts (Any)
+
+-- * Type definitions
+-- ** RecordUS
+
+{- | 'RecordUS' is stored as a 'HList' of 'RecordU' 
+to allow the 'RecordUS' to contain elements of different
+types, so long all of the types can be put into an unboxed
+array ('UArray').
+
+It is advantageous to sort the record to keep elements
+with the same types elements adjacent. See 'SortForRecordUS'
+for more details.  -}
+newtype RecordUS (x :: [*]) =
+    RecordUS Any -- ^ Any here is the @HList u@
+                 -- given @'RecordUSCxt' x u@
+
+-- | connect the unpacked @x@ representation with the
+-- corresponding list of RecordU @u@ representation.
+class RecordUSCxt (x :: [*]) (u :: [*]) | x -> u, u -> x where
+  {- | @O(1)@ should be possible to implement this without
+  unsafeCoerce, but we want to hide the @u@ parameter _and_
+  keep the RecordUSCxt as a class (instead of a type
+  family) because of 'HEq'. In some cases it is possible
+  to have instances that do not actually respect the functional
+  dependency, but this should be safe if the check is not
+  disabled (by using @-XDysfunctionalDependencies@
+  <https://phabricator.haskell.org/D69>, or ghc-7.6) -}
+  recordUSToHList :: RecordUS x -> HList u
+  recordUSToHList (RecordUS x) = unsafeCoerce x
+
+  -- | @O(1)@ should be possible to implement this without
+  -- unsafeCoerce
+  hListToRecordUS :: HList u -> RecordUS x
+  hListToRecordUS x = RecordUS (unsafeCoerce x)
+
+-- | the only instance
+instance (HGroupBy EqTagValue x g, HMapUnboxF g u) => RecordUSCxt x u
+
+data EqTagValue
+instance HEqByFn EqTagValue
+instance (txv ~ Tagged x v,
+          tyw ~ Tagged y w,
+          HEq v w b) => HEqBy EqTagValue txv tyw b
+
+-- | proof that @'hMap' 'UnboxF' :: r xs -> r us@ can determine
+-- @xs@ from @us@ and @us@ from @xs@
+class HMapUnboxF (xs :: [*]) (us :: [*]) | xs -> us, us -> xs
+instance HMapUnboxF '[] '[]
+instance HMapUnboxF xs us => HMapUnboxF (HList x ': xs) (RecordU x ': us)
+
+
+instance (RecordUSCxt x u, Show (HList u)) => Show (RecordUS x) where
+    showsPrec n r = ("RecordUS " ++) . showsPrec n (recordUSToHList r)
+
+-- ** RecordU
+
+{- | A type which behaves similarly to 'Record', except
+all elements must fit in the same 'UArray'. A consequence of
+this is that @RecordU@ has the following properties:
+
+* it is strict in the element types
+
+* it cannot do type-changing updates of 'RecordU', except if
+  the function applies to all elements
+
+* it probably is slower to updates the very first elements
+  of the 'RecordU'
+
+The benefit is that lookups should be faster and records
+should take up less space. However benchmarks do not suggest
+that RecordU is faster than Record.
+-}
+newtype RecordU l = RecordU (UArray Int (GetElemTy l))
+
+type family GetElemTy (x :: [*]) :: *
+type instance GetElemTy (Tagged label v ': rest) = v
+
+deriving instance (Show (UArray Int (GetElemTy l))) => Show (RecordU l)
+deriving instance (Read (UArray Int (GetElemTy l))) => Read (RecordU l)
+deriving instance (Eq  (UArray Int (GetElemTy l))) => Eq  (RecordU l)
+deriving instance (Ord (UArray Int (GetElemTy l))) => Ord (RecordU l)
+
+{- | Reorders a 'Record' such that the 'RecordUS' made from it takes up
+less space
+
+'Bad' has alternating Double and Int fields
+
+>>> bad
+Record{x=1.0,i=2,y=3.0,j=4}
+
+4 arrays containing one element each are needed when this
+Record is stored as a RecordUS
+
+>>> recordToRecordUS bad
+RecordUS H[RecordU (array (0,0) [(0,1.0)]), RecordU (array (0,0) [(0,2)]), RecordU (array (0,0) [(0,3.0)]), RecordU (array (0,0) [(0,4)])]
+
+It is possible to sort the record
+
+>>> sortForRecordUS bad
+Record{x=1.0,y=3.0,i=2,j=4}
+
+This allows the same content to be stored in
+two unboxed arrays
+
+>>> recordToRecordUS (sortForRecordUS bad)
+RecordUS H[RecordU (array (0,1) [(0,1.0),(1,3.0)]), RecordU (array (0,1) [(0,2),(1,4)])]
+
+-}
+class SortForRecordUS x x' | x -> x' where
+    sortForRecordUS :: Record x -> Record x'
+
+instance SortForRecordUS '[] '[] where
+    sortForRecordUS = id
+
+instance (HPartitionEq EqTagValue x (x ': xs) xi xo,
+          SortForRecordUS xo xo',
+          sorted ~ HAppendListR xi xo',
+          HAppendList xi xo') =>
+  SortForRecordUS (x ': xs) sorted where
+  sortForRecordUS (Record xs) = Record (hAppendList xi xo')
+    where
+      f  = Proxy :: Proxy EqTagValue
+      x1 = Proxy :: Proxy x
+      (xi,xo) = hPartitionEq f x1 xs
+      Record xo' = sortForRecordUS (Record xo)
+
+-------------------------------------------------------------- 
+-- * Lookup
+
+-- | works expected. See examples attached to 'bad'.
+instance (HFindLabel l r n,
+          HLookupByHNatUS n u (Tagged l v),
+          HasField l (Record r) v,
+          RecordUSCxt r u) =>
+  HasField l (RecordUS r) v where
+  hLookupByLabel _ u = case hLookupByHNatUS n (recordUSToHList u) of Tagged v -> v
+    where n = Proxy :: Proxy n
+
+class HLookupByHNatUS (n :: HNat) (us :: [*]) (e :: *) | n us -> e where
+  hLookupByHNatUS :: Proxy n -> HList us -> e
+
+class HLookupByHNatUS1 (r :: Either HNat HNat) (n :: HNat) (u :: [*]) (us :: [*]) (e :: *)
+        | r n u us -> e where
+  hLookupByHNatUS1 :: Proxy r -> Proxy n -> RecordU u -> HList us -> e
+
+instance (r ~ HSubtract (HLength u) n,
+          RecordU u ~ ru,
+          HLookupByHNatUS1 r n u us e) =>
+  HLookupByHNatUS n (ru ': us) e where
+  hLookupByHNatUS n (HCons u us) = hLookupByHNatUS1 (Proxy :: Proxy r) n u us
+
+instance (HNat2Integral n,
+         HLookupByHNatR n u ~ le,
+         le ~ Tagged l e,
+         IArray UArray e,
+         e ~ GetElemTy u) => HLookupByHNatUS1 (Left t) n u us le where
+  hLookupByHNatUS1 _ n (RecordU u) _us = Tagged (u ! hNat2Integral n)
+
+instance HLookupByHNatUS t us e => HLookupByHNatUS1 (Right t) n u us e where
+  hLookupByHNatUS1 _ _ _ = hLookupByHNatUS (Proxy :: Proxy t)
+
+-- | @HSubtract a b@ is @Left (a-b)@, @Right (b-a)@ or @Right HZero@
+type family HSubtract (n1 :: HNat) (n2 :: HNat) :: Either HNat HNat
+
+type instance HSubtract HZero HZero = Right HZero
+type instance HSubtract (HSucc x) (HSucc y) = HSubtract x y
+type instance HSubtract HZero (HSucc y) = Right (HSucc y)
+type instance HSubtract (HSucc y) HZero = Left (HSucc y)
+
+
+
+
+-------------------------------------------------------------- 
+-- * Conversion of RecordUS
+
+-- ** with the actual representation
+
+-- | @Iso (HList s) (HList t) (RecordUS a) (Record b)@
+recordUS r = iso hListToRecordUS recordUSToHList r
+
+{- | @Iso (HList s) (RecordUS a)@
+
+@s@ is a HList of 'RecordU' while @a :: [*]@
+is list of @Tagged label value@
+
+-}
+recordUS' r = simple (recordUS r)
+
+-- ** with 'Record'
+
+-- | @view unboxedS@ or @^. unboxedS@ are preferred
+recordToRecordUS :: forall x g u.
+   (HMapCxt HList UnboxF g u,
+    HMapUnboxF g u,
+    HGroupBy EqTagValue x g,
+    RecordUSCxt x u)
+   => Record x -> RecordUS x
+recordToRecordUS (Record x) = hListToRecordUS u
+  where
+    u :: HList u
+    u = hMap UnboxF g 
+
+    g :: HList g
+    g = hGroupBy (Proxy :: Proxy EqTagValue) x
+
+-- | @^. from unboxedS@ is preferred
+recordUSToRecord :: forall u g x.
+  (HConcatR g ~ x,
+   HConcat g,
+   HMapCxt HList BoxF u g,
+   HMapUnboxF g u,
+   RecordUSCxt x u
+  ) => RecordUS x -> Record x
+recordUSToRecord rus = Record (hConcat g)
+  where
+    g :: HList g
+    g = hMap BoxF (recordUSToHList rus)
+
+-- | @Iso (Record x) (Record y) (RecordUS x) (RecordUS y)@
+unboxedS r = iso recordToRecordUS recordUSToRecord r
+
+-- | @Iso' (Record x) (RecordUS x)@
+unboxedS' r = simple (unboxedS r)
+
+
+
+-- | all elements of the list have the same type
+class ElemTyEq (xs :: [*])
+
+instance 
+ (t1v ~ Tagged t1 v,
+  t2v ~ Tagged t2 v,  
+  ElemTyEq (tv2 ': rest)) =>
+  ElemTyEq (tv1 ': tv2 ': rest)
+
+instance t1v ~ Tagged t v => ElemTyEq (t1v ': rest)
+instance ElemTyEq '[]
+
+
+instance (IArray UArray v,
+          v ~ GetElemTy ls,
+          HFindLabel l ls n,
+          HNat2Integral n)
+    => HasField l (RecordU ls) v where
+  hLookupByLabel _ (RecordU ls) = ls ! hNat2Integral (Proxy :: Proxy n)
+
+
+instance (r ~ r',
+          v ~ GetElemTy r,
+          HFindLabel l r n,
+          HNat2Integral n,
+          IArray UArray v,
+          HasField l (Record r') v)
+    => HUpdateAtLabel RecordU l v r r' where
+  hUpdateAtLabel _ v (RecordU r) = RecordU (r // [(hNat2Integral (Proxy :: Proxy n), v)])
+
+
+{- | analogous flip '//'. Similar to '.<++.', except it is restricted
+to cases where the left argument holds a subset of elements.
+
+-}
+class HUpdateMany lv rx where
+    hUpdateMany :: Record lv -> rx -> rx
+
+instance (RecordValues lv,
+          HList2List (RecordValuesR lv) v,
+          HFindMany (LabelsOf lv) (LabelsOf r) ixs,
+          IArray UArray v,
+          v ~ GetElemTy r,
+          HNats2Integrals ixs) =>
+  HUpdateMany lv (RecordU r) where
+  hUpdateMany lv (RecordU r) = RecordU (r // (zip ixs (hList2List (recordValues lv))))
+     where ixs = hNats2Integrals (Proxy :: Proxy ixs)
+
+-- | implementation in terms of '.<++.'
+instance (HLeftUnion lv x lvx,
+          HRLabelSet x,
+          HLabelSet (LabelsOf x),
+          HRearrange (LabelsOf x) lvx x)
+  => HUpdateMany lv (Record x) where
+    hUpdateMany lv x = hRearrange' (lv .<++. x)
+
+-- | behaves like @map 'HFind'@
+class HFindMany (ls :: [k]) (r :: [k]) (ns :: [HNat]) | ls r  -> ns
+instance (HFind l r n,
+          HFindMany ls r ns) => HFindMany (l ': ls) r (n ': ns)
+
+instance HFindMany '[] r '[]
+
+instance (ApplyAB f (GetElemTy x) (GetElemTy y),
+          IArray UArray (GetElemTy y),
+          IArray UArray (GetElemTy x)) => HMapAux RecordU f x y where
+    hMapAux f (RecordU x) = RecordU (amap (applyAB f) x)
+
+-- | 'hMap' specialized to 'RecordU'
+hMapRU :: HMapCxt RecordU f x y => f -> RecordU x -> RecordU y
+hMapRU f = hMap f
+
+
+-- | @Iso (Record x) (Record y) (RecordU x) (RecordU y)@
+unboxed :: forall x y f p.
+  (Profunctor p,
+   Functor f,
+   RecordToRecordU x,
+   RecordUToRecord y)
+  => RecordU x `p` f (RecordU y)
+  -> Record x `p` f (Record y)
+unboxed r = iso recordToRecordU recordUToRecord r
+
+-- | @Iso' (Record x) (RecordU x)@
+unboxed' x = simple (unboxed x)
+
+
+class RecordToRecordU x where
+    recordToRecordU :: Record x -> RecordU x
+
+instance (
+    RecordValues x,
+    HList2List (RecordValuesR x) (GetElemTy x),
+    HNat2Integral (HLength x),
+    IArray UArray (GetElemTy x)
+   ) => RecordToRecordU x where
+  recordToRecordU (rx @ (Record x)) = RecordU $ listArray
+          (0, hNat2Integral (hLength x) - 1)
+          (hList2List (recordValues rx))
+ 
+class RecordUToRecord x where
+    recordUToRecord :: RecordU x -> Record x
+
+instance (
+    HMapCxt HList TaggedFn (RecordValuesR x) x,
+    IArray UArray (GetElemTy x),
+    HList2List (RecordValuesR x) (GetElemTy x) 
+  ) => RecordUToRecord x where
+  recordUToRecord (RecordU b) = case list2HList $ elems b of
+          Nothing -> error "Data.HList.RecordU.recordUToRecord impossibly too few elements"
+          Just y0 -> Record $ hMap TaggedFn (y0 :: HList (RecordValuesR x))
+
+
+
+-- * definitions for doctest examples
+type Bad =
+         [Tagged "x" Double,
+          Tagged "i" Int,
+          Tagged "y" Double,
+          Tagged "j" Int]
+
+{- | HasField instances
+
+[@RecordUS@]
+
+>>> let r = recordToRecordUS (sortForRecordUS bad)
+>>> let s = recordToRecordUS bad
+
+>>> let x = Label :: Label "x"
+>>> let y = Label :: Label "y"
+>>> let i = Label :: Label "i"
+>>> let j = Label :: Label "j"
+
+>>> (r .!. x, r .!. i, r .!. y, r .!. j)
+(1.0,2,3.0,4)
+
+>>> (s .!. x, s .!. i, s .!. y, s .!. j)
+(1.0,2,3.0,4)
+
+
+[@RecordU@]
+
+>>> let t = recordToRecordU bad1
+>>> (t .!. x, t .!. y)
+(1.0,2.0)
+
+>>> hUpdateAtLabel x 3 t .!. x
+3.0
+
+-}
+bad :: Record Bad
+bad = Tagged 1 .*. Tagged 2 .*. Tagged 3 .*. Tagged 4 .*. emptyRecord
+
+bad1 :: Record [Tagged "x" Double, Tagged "y" Double]
+bad1 = Tagged 1 .*. Tagged 2 .*. emptyRecord
+
+-- * Implementation Details
+
+data UnboxF = UnboxF
+instance (hx ~ HList x, ux ~ RecordU x,
+          RecordToRecordU x) =>
+  ApplyAB UnboxF hx ux where
+  applyAB _ = recordToRecordU . Record
+
+data BoxF = BoxF
+
+instance (ux ~ RecordU x,
+         hx ~ HList x,
+         RecordUToRecord x) =>
+  ApplyAB BoxF ux hx where
+  applyAB _ ux = case recordUToRecord ux of Record hx -> hx
+
+
+-- | make a @Lens' (RecordU s) a@
+instance (s ~ t, a ~ b,
+          IArray UArray a, a ~ GetElemTy s,
+          HLensCxt x RecordU s t a b)
+        => Labelable x RecordU s t a b where
+            type LabelableTy RecordU = LabelableLens
+            hLens' = hLens
+
+{- TODO
+instance Labelable x RecordUS to p f s t a b where
+instance (r ~ r', HasField l (Record r) v)
+      => HUpdateAtLabel RecordUS l v r r' where
+  hUpdateAtLabel = error "recordus hupdateatlabel"
+-}
diff --git a/Data/HList/broken/VariantOld.hs b/Data/HList/broken/VariantOld.hs
new file mode 100644
--- /dev/null
+++ b/Data/HList/broken/VariantOld.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, UndecidableInstances #-}
+
+
+{- |
+   The HList library
+
+   (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke
+
+   Variants, i.e., labelled sums.
+
+   One approach to their implementation would be to consider both
+   the favoured label and the corresponding value as dynamics upon
+   variant construction. Since we are too lazy to programme some
+   Typeable instances for non-ghc systems (NB: in GHC, Typeable
+   is derivable), we rather model variants as (opaque) records
+   with maybies for the values. Only one value will actually hold
+   non-Nothing, as guaranteed by the constructor.
+
+   See VariantP.hs for a different approach to open sums.
+-}
+
+module Data.HList.Variant where
+
+import Data.HList.FakePrelude
+import Data.HList.Record
+import Data.HList.HList
+
+
+-- --------------------------------------------------------------------------
+-- | Variant types on the basis of label-maybe pairs.
+
+newtype Variant mr = Variant mr
+
+
+-- --------------------------------------------------------------------------
+-- | Turn proxy sequence into sequence of Nothings
+
+data HMaybeF = HMaybeF
+instance ((Tagged l (Proxy t) ~ a, b ~ Tagged l (Maybe t))) =>  ApplyAB HMaybeF a b   where
+    applyAB _ _ = Tagged Nothing
+
+hMaybied x = hMap HMaybeF x
+
+
+-- --------------------------------------------------------------------------
+-- | Public constructor
+-- it seems we can blame 'hUpdateAtLabel' (not 'HMap') for needing the asTypeOf?
+mkVariant x y (Record v) = let r1 = Record (hMaybied v) in
+    case hUpdateAtLabel x (Just y) r1 `asTypeOf` r1 of
+    Record t -> Variant t
+
+-- --------------------------------------------------------------------------
+-- | Public destructor
+
+unVariant x (Variant v) = hLookupByLabel x (Record v)
+
+
+-- --------------------------------------------------------------------------
+-- | Variants are opaque
+
+instance Show (Variant v)
+ where
+  show _ = "<Cannot show Variant content!>"
+
+
diff --git a/HList.cabal b/HList.cabal
--- a/HList.cabal
+++ b/HList.cabal
@@ -1,10 +1,17 @@
 Name:                HList
-Version:             0.3.4.1
+Version:             0.4.0.0
 Category:            Data
 Synopsis:            Heterogeneous lists
-Description:         HList is a record system providing strongly typed heterogenous lists, records,
-                     type-indexed products (TIP) and co-products; licensed under the MIT X License.
-                     .
+Description:         HList provides many operations to create and manipulate
+                     heterogenous lists (HLists) whose length and element
+                     types are known at compile-time. HLists are used to implement
+                    .
+                      * records
+                      * variants
+                      * type-indexed products (TIP)
+                      * type-indexed co-products (TIC)
+                      * keyword arguments
+                    .
                      User code should import "Data.HList" or
                      "Data.HList.CommonMain" for a slightly more limited scope
 License:             MIT
@@ -15,16 +22,16 @@
 
 Data-files:          README, ChangeLog
 Cabal-version:       >= 1.10
-Tested-With:         GHC==7.6.2, GHC==7.7
+Tested-With:         GHC==7.6.3, GHC==7.8.4, GHC==7.10.1
 Build-Type:          Simple
 
 Extra-Source-Files:
                      examples/*.hs,
+                     examples/Properties/*.hs,
                      examples/broken/*.hs,
                      examples/broken/*.lhs,
 
                      examples/*.ref,
-                     examples/*.out,
 
                      examples/broken/*.ref,
 
@@ -36,22 +43,38 @@
     location: http://code.haskell.org/HList
 
 
+flag new_type_eq
+  Default: False
+  Manual: True
+  Description: use Data.Type.Equality.== to define the instance of HEq
+               instead of overlapping instances (in Data.HList.TypeEqO)
+               (needs ghc >= 7.8)
+
 library
-  Build-Depends:       base >= 4 && < 5, template-haskell, ghc-prim, mtl,
-                       tagged
+  Build-Depends:       base >= 4.6 && < 4.9,
+                       template-haskell,
+                       ghc-prim,
+                       mtl,
+                       tagged,
+                       profunctors,
+                       array
 
+
   Exposed-modules:     Data.HList,
                        Data.HList.CommonMain,
                        Data.HList.Data,
                        Data.HList.FakePrelude,
                        Data.HList.HArray,
+                       Data.HList.HCurry,
                        Data.HList.HList,
                        Data.HList.HListPrelude,
                        Data.HList.HOccurs,
                        Data.HList.HTypeIndexed,
+                       Data.HList.HSort,
                        Data.HList.HZip,
                        Data.HList.Keyword,
                        Data.HList.Label3,
+                       Data.HList.Label5,
                        Data.HList.Label6,
                        Data.HList.Labelable,
                        Data.HList.MakeLabels,
@@ -59,44 +82,86 @@
                        Data.HList.RecordPuns,
                        Data.HList.TIC,
                        Data.HList.TIP,
+                       Data.HList.TIPtuple,
                        Data.HList.TypeEqO,
                        Data.HList.Variant
+  Other-modules:       LensDefs
   Default-Language:    Haskell2010
 
 
-  Ghc-Options:         -Wall -fno-warn-missing-signatures
+  Ghc-Options:         -Wall -fno-warn-missing-signatures -fno-warn-orphans
 
   Default-Extensions:  ConstraintKinds
                        DataKinds
+                       DeriveDataTypeable
                        EmptyDataDecls
                        FlexibleContexts
                        FlexibleInstances
                        FunctionalDependencies
+                       GeneralizedNewtypeDeriving
                        GADTs
                        KindSignatures
                        MultiParamTypeClasses
                        PolyKinds
-                       RankNTypes,
+                       RankNTypes
                        ScopedTypeVariables
+                       StandaloneDeriving
                        TypeFamilies
                        TypeOperators
                        UndecidableInstances
+  Other-Extensions:    CPP
+                       TemplateHaskell
+                       OverlappingInstances
 
+  if impl(ghc < 7.7)
+    Cpp-options:       -DOLD_TYPEABLE -DNO_CLOSED_TF
+
   if impl(ghc >= 7.7)
     Default-Extensions: AllowAmbiguousTypes
-                        StandaloneDeriving
+                        RoleAnnotations
 
+  if impl(ghc > 7.9)
+    Ghc-Options:      -fno-warn-unticked-promoted-constructors
+
+  if flag(new_type_eq)
+    Cpp-options:       -DNEW_TYPE_EQ
+    Build-Depends:     base >= 4.7
+
 Test-Suite examples
   Type:     exitcode-stdio-1.0
   Ghc-Options: -threaded
-  Main-Is: examples/runexamples.hs
+  Main-Is: runexamples.hs
   Default-Language:    Haskell2010
+  Hs-Source-Dirs:  examples
   Build-Depends:       base, hspec >= 1.7, directory, filepath,
                        process, syb, cmdargs, lens, HList, mtl
 
 Test-Suite doctests
   Type:     exitcode-stdio-1.0
   Ghc-Options: -threaded
-  Main-Is: examples/rundoctests.hs
+  if impl(ghc <= 7.9)
+    -- doctests include things like :t pred . maxBound, which
+    -- depending on the ghc version, comes out as one of
+    -- (Bounded a, Enum a) => ...
+    -- (Enum b, Bounded b) => ...
+    Buildable: False
+  Main-Is: rundoctests.hs
+  Hs-Source-Dirs:  examples
   Build-Depends: base, doctest >= 0.8, process
+  Default-Language:    Haskell2010
+
+
+Test-Suite properties
+  Type:     exitcode-stdio-1.0
+  Build-Depends: base,
+                 hspec >= 1.7,
+                 HList,
+                 lens,
+                 mtl,
+                 QuickCheck,
+                 template-haskell,
+                 array,
+                 syb
+  Main-Is:  Properties.hs
+  Hs-Source-Dirs: examples
   Default-Language:    Haskell2010
diff --git a/LensDefs.hs b/LensDefs.hs
new file mode 100644
--- /dev/null
+++ b/LensDefs.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE CPP #-}
+-- | parts of lens that would be imported if we depended on it
+module LensDefs
+  (module LensDefs,
+   module Control.Applicative,
+   Choice,
+   Profunctor,
+   Coercible)
+   where
+
+import Data.Profunctor
+import Data.Profunctor.Unsafe
+import Control.Applicative
+import Control.Monad.Identity
+
+import Unsafe.Coerce
+#if __GLASGOW_HASKELL__ > 707
+import GHC.Exts(Coercible)
+#else
+import GHC.Exts(Constraint)
+-- | for ghc-7.6 we don't have coercible
+type Coercible a b = (() :: Constraint)
+#endif
+
+simple :: p a (f a) -> p a (f a)
+simple = id
+
+
+-- Used by doctests (which should probably just import Control.Lens...)
+infixl 1 &
+x & f = f x
+
+infixr 4 %~
+l %~ f = \t -> runIdentity $ l (rmap Identity f) t
+
+iso :: (Profunctor p, Functor f)
+    => (s -> a) -> (b -> t)
+    -> p a (f b) -> p s (f t)
+iso sa bt = dimap sa (fmap bt)
+
+-- | iso, except assumes that the functions supplied could
+-- be 'Data.Coerce.coerce'
+isoNewtype :: (Profunctor p, Functor f,
+               Coercible b t, -- Coercible (f b) (f t) -- is really needed but that complicates types later on (since f is forall'd)
+               Coercible a s)
+    => (s -> a) -> (b -> t)
+    -> p a (f b) -> p s (f t)
+isoNewtype sa _bt x = coerceBT x .# sa
+  where coerceBT :: p a (f b) -> p a (f t)
+        coerceBT = unsafeCoerce
+
+prism :: (b -> t) -> (s -> Either t a)
+    -> (forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t))
+prism bt seta = dimap seta (either pure (fmap bt)) . right'
+
+prism' :: (a -> s) -> (s -> Maybe a)
+    -> (forall p f. (Choice p, Applicative f) => p a (f a) -> p s (f s))
+prism' bs sma = prism bs (\s -> maybe (Left s) Right (sma s))
diff --git a/README b/README
--- a/README
+++ b/README
@@ -45,7 +45,7 @@
 $ cd HList; cabal install
 
 The code works --- within the limits exercised in the source files ---
-for GHC-7.6 and GHC-7.7. Older compilers are not supported.
+for GHC-7.6 and GHC-7.8 and GHC-7.10. Older compilers are not supported.
 
 One may run "cabal test" to check the distribution.
 
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env runhaskell
-import Distribution.Simple
-main = defaultMainWithHooks defaultUserHooks
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,114 @@
+#!/usr/bin/runhaskell
+\begin{code}
+{-# OPTIONS_GHC -Wall #-}
+module Main (main) where
+
+import Language.Haskell.Extension
+    ( Extension(EnableExtension, UnknownExtension, DisableExtension) )
+import Data.List ( nub )
+import Data.Version ( showVersion )
+import Distribution.Package
+    ( PackageName(PackageName),
+      PackageId,
+      InstalledPackageId,
+      packageVersion,
+      packageName )
+import Distribution.PackageDescription ()
+import Distribution.Simple
+    ( defaultMainWithHooks, UserHooks(..), simpleUserHooks )
+import Distribution.Simple.Utils
+    ( rewriteFile, createDirectoryIfMissingVerbose )
+import Distribution.Simple.BuildPaths ( autogenModulesDir )
+import Distribution.Simple.Setup ()
+import Distribution.Simple.LocalBuildInfo ()
+import System.FilePath ( (</>) )
+import Distribution.Verbosity ( Verbosity )
+import Distribution.PackageDescription ()
+import Distribution.Simple.LocalBuildInfo ()
+import Distribution.PackageDescription
+    ( TestSuite(testName),
+      PackageDescription,
+      Library(libBuildInfo),
+      allExtensions )
+import Distribution.Simple.Compiler ()
+import Distribution.Simple.Setup
+    ( BuildFlags(buildVerbosity),
+      ConfigFlags(..),
+      Flag(Flag, NoFlag),
+      fromFlag )
+import Distribution.Simple.LocalBuildInfo
+    ( LocalBuildInfo(configFlags),
+      ComponentLocalBuildInfo(componentPackageDeps),
+      withTestLBI,
+      withLibLBI )
+import Data.Maybe
+
+main :: IO ()
+main = defaultMainWithHooks simpleUserHooks
+  { buildHook = \pkg lbi hooks flags -> do
+     generateBuildModule (fromFlag (buildVerbosity flags)) pkg lbi
+     buildHook simpleUserHooks pkg lbi hooks flags
+  }
+
+generateBuildModule :: Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
+generateBuildModule verbosity pkg lbi = do
+  let dir = autogenModulesDir lbi
+  createDirectoryIfMissingVerbose verbosity True dir
+  withLibLBI pkg lbi $ \ lib libcfg -> do
+    withTestLBI pkg lbi $ \suite suitecfg -> do
+      rewriteFile (dir </> "Build_" ++ testName suite ++ ".flags") $ unlines $
+        [ hc , unwords (formatdeps (testDeps libcfg suitecfg) ++ exts lib) ]
+  where
+    hc = case configHcPath (configFlags lbi) of
+            Flag a -> a
+            NoFlag -> fromMaybe "ghc" (lookup "ghc" (configProgramPaths (configFlags lbi)))
+    formatdeps = map (formatone . snd)
+    formatone p = case packageName p of
+      PackageName n -> "-package=" ++ n ++ "-" ++ showVersion (packageVersion p)
+
+    exts lib = [ "-X" ++ se
+                    | e <- allExtensions (libBuildInfo lib),
+                      Just se <- [case e of
+                        EnableExtension x -> Just (show x)
+                        UnknownExtension x -> Just x
+                        DisableExtension x -> Just ("No" ++ show x)
+                  ]
+                ]
+
+testDeps :: ComponentLocalBuildInfo -> ComponentLocalBuildInfo -> [(InstalledPackageId, PackageId)]
+testDeps xs ys = nub $ componentPackageDeps xs ++ componentPackageDeps ys
+
+
+\end{code}
+Adapted from:
+
+Copyright 2012-2015 Edward Kmett
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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/examples/Cabal.hs b/examples/Cabal.hs
new file mode 100644
--- /dev/null
+++ b/examples/Cabal.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE CPP #-}
+module Cabal where
+
+import System.Process
+import System.Exit
+import Control.Monad
+
+-- | > cabal args stdin = readProcessWithExitCode "cabal" args stdin
+--
+-- except it also tries to runghc Setup which can succeed (because
+-- if things have been configured with runghc Setup, then a different
+-- version of the Cabal library is chosen and cabal complains instead
+-- of reconfiguring / using an older Cabal library
+cabal :: [String] -> String
+  -> IO (ExitCode, String, String)
+cabal args stdin = do
+#if __GLASGOW_HASKELL__ > 707
+  cabalRepl @ (ec, _, _) <- readProcessWithExitCode "cabal" args stdin
+  if ec == ExitSuccess then return cabalRepl
+    else do
+      runghcRepl @ (ec2, _, _) <- readProcessWithExitCode "runghc" ("Setup.lhs":args) stdin
+      when (ec2 /= ExitSuccess) $ do
+        putStrLn "Could not \"cabal ...\" (exitCode,stdout,stderr):"
+        print cabalRepl
+        putStrLn "Could not \"runghc Setup ...\" (exitCode,stdout,stderr):"
+        print runghcRepl
+
+        putStrLn "\"...\" above is "
+        putStrLn "Command:"
+        print ("cabal" : args)
+        putStrLn "stdin:"
+        print stdin
+        return ()
+
+      return runghcRepl
+#else
+  -- we don't have a cabal repl (at least if we're using the Cabal that ghc
+  -- comes with), so read a file written by the ./Setup build
+  "repl" : tgt : args <- return args
+  ghc : rest <- lines `fmap` readFile ("dist/build/autogen/Build_" ++ tgt ++ ".flags")
+  let args2 = "--interactive"
+                                : "-v"
+                                : "-package-db" : "dist/package.conf.inplace"
+                                : concatMap words rest ++ args
+
+      args3 = case break (=="--ghc-options") args2 of
+                  (a, _:_:b) -> a ++ b
+  print args3
+  readProcessWithExitCode ghc args3 stdin
+#endif
diff --git a/examples/Joy.hs b/examples/Joy.hs
deleted file mode 100644
--- a/examples/Joy.hs
+++ /dev/null
@@ -1,287 +0,0 @@
-{-# OPTIONS -fglasgow-exts #-}
-{-# OPTIONS -fallow-overlapping-instances #-}
-{-# OPTIONS -fallow-undecidable-instances #-}
-
---Joy implemented in Haskell... extensible embedded language...
-
-module Joy where
-
-import MainGhcGeneric1
-
--- Building non-empty lists
-
-type HOne = HSucc HZero
-hOne :: HOne
-hOne = undefined
-type HTwo = HSucc HOne
-hTwo :: HTwo
-hTwo = undefined
-type HThree = HSucc HTwo
-hThree :: HThree
-hThree = undefined
-
-end :: HNil
-end = hNil
-
-instance HList s => Apply HNil s s where
-	apply _ s = s
-instance (HList s,HList s',HList l,Apply a s s',Apply l s' s'') => Apply (HCons a l) s s'' where
-	apply (HCons a l) s = apply l (apply a s :: s')
-instance HList s => Apply HZero s (HCons HZero s) where
-	apply _ s = hCons hZero s
-instance (HNat a,HList s) => Apply (HSucc a) s (HCons (HSucc a) s) where
-	apply a s = hCons a s
-
-data Lit a = Lit a
-lit :: a -> Lit a
-lit a = Lit a
-unl :: Lit a -> a
-unl (Lit a) = a
-instance Show a => Show (Lit a) where
-	showsPrec _ (Lit a) = showChar '[' . shows a . showChar ']'
-instance HList s => Apply (Lit a) s (HCons a s) where
-	apply (Lit a) s = hCons a s
-
-class (HBool b,HList s) => HIfte b t f s s' | b t f s -> s' where
-	hIfte :: b -> t -> f -> s -> s'
-instance (HList s,Apply t s s') => HIfte HTrue t f s s' where
-	hIfte _ t _ s = apply t s
-instance (HList s,Apply f s s') => HIfte HFalse t f s s' where
-	hIfte _ _ f s = apply f s
-
-data Ifte
-ifte :: Ifte
-ifte = undefined
-instance Show Ifte where
-	showsPrec _ _ = showString "If"
-instance (Apply b s r,HHead r b',HIfte b' t f s s')
-	=> Apply Ifte (f :*: t :*: b :*: s) s' where
-	apply _ (HCons f (HCons t (HCons b s))) = hIfte (hHead (apply b s :: r) :: b') t f s
-
-data Nul
-nul :: Nul
-nul = undefined
-instance Show Nul where
-	showsPrec _ _ = showString "Nul"
-instance HList s => Apply Nul (HCons HZero s) (HCons HTrue s) where
-	apply _ (HCons _ s) = hCons hTrue s
-instance HList s => Apply Nul (HCons (HSucc n) s) (HCons HFalse s) where
-	apply _ (HCons _ s) = hCons hFalse s
-
-data EQ
-eq :: EQ
-eq = undefined
-instance Show EQ where
-	showsPrec _ _ = showString "Eq"
-instance (HList s,TypeEq a b t) => Apply EQ (HCons a (HCons b s)) (HCons t s) where
-	apply _ (HCons a (HCons b s)) = hCons (typeEq a b) s
-
-data Dip
-dip :: Dip
-dip = undefined
-instance Show Dip where
-	showsPrec _ _ = showString "Dip"
-instance (HList s,HList s',Apply a s s') => Apply Dip (HCons a (HCons b s)) (HCons b s') where
-	apply _ (HCons a (HCons b s)) = hCons b (apply a s)
-
-data Dup 
-dup :: Dup
-dup = undefined
-instance Show Dup where
-	showsPrec _ _ = showString "Dup"
-instance HList s => Apply Dup (HCons a s) (HCons a (HCons a s)) where
-	apply _ s@(HCons a _) = hCons a s
-
-data Pop
-pop :: Pop
-pop = undefined
-instance Show Pop where
-	showsPrec _ _ = showString "Pop"
-instance HList s => Apply Pop (HCons a s) s where
-	apply _ (HCons _ s) = s
-
-data Swap
-swap :: Swap
-swap = undefined
-instance Show Swap where
-	showsPrec _ _ = showString "Swap"
-instance HList s => Apply Swap (HCons a (HCons b s)) (HCons b (HCons a s)) where
-	apply _ (HCons a (HCons b s)) = hCons b (hCons a s)
-
-data Suc
-suc :: Suc
-suc = undefined
-instance Show Suc where
-	showsPrec _ _ = showString "Suc"
-instance (HNat a,HList s) => Apply Suc (HCons a s) (HCons (HSucc a) s) where
-	apply _ (HCons _ s) = hCons (undefined::HSucc a) s
-
-data Pre
-pre :: Pre
-pre = undefined
-instance Show Pre where
-	showsPrec _ _ = showString "Pre"
-instance (HNat a,HList s) => Apply Pre (HCons (HSucc a) s) (HCons a s) where
-	apply _ (HCons _ s) = hCons (undefined::a) s
-
-data Add
-add :: Add
-add = undefined
-instance Show Add where
-	showsPrec _ _ = showString "Add"
-instance (HList s,HAdd a b c) => Apply Add (HCons a (HCons b s)) (HCons c s) where
-	apply _ (HCons _ (HCons _ s)) = hCons (hAdd (undefined::a) (undefined::b)) s
-
-class (HNat a,HNat b) => HAdd a b c | a b -> c where
-	hAdd :: a -> b -> c
-instance HAdd HZero HZero HZero where
-	hAdd _ _ = hZero
-instance HNat b => HAdd HZero (HSucc b) (HSucc b) where
-	hAdd _ b = b
-instance HNat a => HAdd (HSucc a) HZero (HSucc a) where
-	hAdd a _ = a
-instance (HNat (HSucc a),HNat (HSucc b),HNat c,HAdd a b c)
-	=> HAdd (HSucc a) (HSucc b) (HSucc (HSucc c)) where
-	hAdd _ _ = hSucc $ hSucc $ hAdd (undefined::a) (undefined::b)
-
-data Sub
-sub :: Sub
-sub = undefined
-instance Show Sub where
-	showsPrec _ _ = showString "Sub"
-instance (HList s,HSub a b c) => Apply Sub (HCons b (HCons a s)) (HCons c s) where
-	apply _ (HCons _ (HCons _ s)) = hCons (hSub (undefined::a) (undefined::b)) s
-
-class (HNat a,HNat b) => HSub a b c | a b -> c where
-	hSub :: a -> b -> c
-instance HSub HZero HZero HZero where
-	hSub _ _ = hZero
-instance HNat a => HSub (HSucc a) HZero (HSucc a) where
-	hSub a _ = a
-instance HNat a => HSub HZero (HSucc a) HZero where
-	hSub _ _ = hZero
-instance (HSub a b c) => HSub (HSucc a) (HSucc b) c where
-	hSub _ _ = hSub (undefined::a) (undefined::b)
-	
-data Mult
-mult :: Mult
-mult = undefined
-instance Show Mult where
-	showsPrec _ _ = showString "Mult"
-instance (HList s,HMult a b c) => Apply Mult (HCons a (HCons b s)) (HCons c s) where
-	apply _ (HCons _ (HCons _ s)) = hCons (hMult (undefined::a) (undefined::b)) s
-
-class (HNat a,HNat b) => HMult a b c | a b -> c where
-	hMult :: a -> b -> c
-instance HNat b => HMult HZero b HZero where
-	hMult _ _ = hZero
-instance (HMult a b s,HAdd b s s') => HMult (HSucc a) b s' where
-	hMult _ _ = hAdd (undefined::b) (hMult (undefined::a) (undefined::b) :: s)
-
-square = dup .*. mult .*. hNil
-cube = mult .*. mult .*. dup .*. dup .*. hNil
-
-data I
-i :: I
-i = undefined
-instance Show I where
-	showsPrec _ _ = showString "I"
-instance Apply I HNil HNil where
-	apply _ _ = hNil
-instance (HList s,Apply a s s') => Apply I (HCons a s) s' where
-	apply _ (HCons a s) = apply a s
-
-data Primrec = Primrec deriving Show
-primrec :: Primrec
-primrec = undefined
-instance Apply z s s' => Apply Primrec (HCons nz (HCons z (HCons HZero s))) s' where
-	apply _ (HCons _ (HCons z (HCons _ s))) = apply z s
-instance (HList s,Apply Primrec (HCons nz (HCons z (HCons n (HCons (HSucc n) s)))) s',Apply nz s' s'')
-	=> Apply Primrec (HCons nz (HCons z (HCons (HSucc n) s))) s'' where
-	apply _ (HCons nz (HCons z s@(HCons _ _))) = apply nz (apply Primrec (hCons nz (hCons z (hCons (undefined::n) s))))
-
-data Times
-times :: Times
-times = undefined
-instance Show Times where
-	showsPrec _ _ = showString "Times"
-instance HList s => Apply Times (HCons p (HCons HZero s)) s where
-	apply _ (HCons _ (HCons _ s)) = s
-instance (HNat n,HList s,HList s',Apply p s s',Apply Times (HCons p (HCons n s')) s'')
-	=> Apply Times (HCons p (HCons (HSucc n) s)) s'' where
-	apply _ (HCons p (HCons _ s)) = apply times (hCons p (hCons (undefined::n) (apply p s)))
-
-class (HBool f,HList s) => HGenrec f r1 r2 b t s s'' | f r1 r2 b t s -> s'' where
-	hGenrec :: f -> r1 -> r2 -> b -> t -> s -> s''
-instance (HList s,Apply t s s') => HGenrec HTrue r1 r2 b t s s' where
-	hGenrec _ _ _ _ t s = apply t s
-instance (HList s,HList s',Apply r1 s s',
-	Apply (HCons (Lit (HCons (Lit b) (HCons (Lit t) (HCons (Lit r1) (HCons (Lit r2) (HCons Genrec HNil)))))) (HCons r2 HNil)) s' s'')
-	=> HGenrec HFalse r1 r2 b t s s'' where
-	hGenrec _ r1 r2 b t s = apply (hCons (lit (hCons (lit b) (hCons (lit t) (hCons (lit r1) (hCons (lit r2) (hCons genrec hNil)))))) (hCons r2 hNil)) (apply r1 s :: s') 
-
-data Genrec
-genrec :: Genrec
-genrec = undefined
-instance Show Genrec where
-	showsPrec _ _ = showString "Genrec"
-instance (Apply b s s',HHead s' b',HGenrec b' r1 r2 b t s s'')
-	=> Apply Genrec (HCons r2 (HCons r1 (HCons t (HCons b s)))) s'' where
-	apply _ (HCons r2 (HCons r1 (HCons t (HCons b s))))
-		= hGenrec (hHead (apply b s :: s') :: b') r1 r2 b t s
-
-class (HBool f,HList s) => HLinrec f b t r1 r2 s s' | f b t r1 r2 s -> s' where
-	hLinrec :: f -> b -> t -> r1 -> r2 -> s -> s'
-instance (HList s,Apply t s s') => HLinrec HTrue b t r1 r2 s s' where
-	hLinrec _ _ t _ _ s = apply t s
-instance (HList s,HList s',Apply r1 s s',
-	Apply Linrec (HCons r2 (HCons r1 (HCons t (HCons b s')))) s'',Apply r2 s'' s''')
-	=> HLinrec HFalse b t r1 r2 s s''' where
-	hLinrec _ b t r1 r2 s = apply r2 (apply linrec (hCons r2 (hCons r1 (hCons t (hCons b (apply r1 s :: s'))))) :: s'')
-
-data Linrec
-linrec :: Linrec
-linrec = undefined
-instance Show Linrec where
-	showsPrec _ _ = showString "Linrec"
-instance (Apply b s s',HHead s' b',HLinrec b' b t r1 r2 s s'') => Apply Linrec (HCons r2 (HCons r1 (HCons t (HCons b s)))) s'' where
-	apply _ (HCons r2 (HCons r1 (HCons t (HCons b s)))) = hLinrec (hHead (apply b s :: s') :: b') b t r1 r2 s
-
-data Fact
-fact :: Fact
-fact = undefined
-instance Show Fact where
-	showsPrec _ _ = showString "Fact"
-instance (HList s,Apply (HCons (Lit (HCons (Lit HZero) (HCons EQ HNil)))
-	(HCons (Lit (HCons Pop (HCons (Lit HOne) HNil)))
-	(HCons (Lit (HCons Dup
-	(HCons (Lit HOne)
-	(HCons Sub (HCons Fact (HCons Mult HNil))))))
-	(HCons Ifte HNil)))) s s') => Apply Fact s s' where
-	apply _ s = apply fac1 s
-
-fac1 = hCons (lit (hCons (lit hZero) (hCons eq hNil)))
-	(hCons (lit (hCons pop (hCons (lit hOne) hNil)))
-	(hCons (lit (hCons dup (hCons (lit hOne) (hCons sub (hCons fact (hCons mult hNil))))))
-	(hCons ifte hNil)))
-
-fac2 = lit (hOne .*. hOne .*. end)
-	.*. dip .*. lit (dup .*. lit mult .*. dip .*. suc .*. end)
-	.*. times .*. pop .*. end
-
-fac3 = lit nul .*. lit suc .*. lit (dup .*. pre .*. end)
-	.*. lit (i .*. mult .*. end) .*. genrec .*. end
-
-fac4 = lit nul .*. lit suc .*. lit (dup .*. pre .*. end)
-	.*. lit mult .*. linrec .*. end
-
-fac5 = lit hOne .*. lit mult .*. primrec .*. end
-
-main :: IO ()
-main = do
-	putStrLn $ show $ apply (lit hThree .*. fac1 .*. end) end
-	putStrLn $ show $ apply i (fac2 .*. hThree .*. end)
-	putStrLn $ show $ apply i (fac3 .*. hThree .*. end)
-	putStrLn $ show $ apply i (fac4 .*. hThree .*. end)
-	putStrLn $ show $ apply i (fac5 .*. hThree .*. end)
-
diff --git a/examples/MainGhcGeneric1.hs b/examples/MainGhcGeneric1.hs
--- a/examples/MainGhcGeneric1.hs
+++ b/examples/MainGhcGeneric1.hs
@@ -1,13 +1,9 @@
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE PolyKinds #-}
 {-# LANGUAGE DataKinds #-}
-{-# LANGUAGE TypeOperators #-}
-{-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE OverlappingInstances#-}
-{-# LANGUAGE UndecidableInstances#-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell #-}
 
 {-
 
@@ -17,32 +13,16 @@
    cast and generic type equality. Because of generic type equality,
    this model works with GHC but it does not work with Hugs.
 
-   Note: even though there are no overlapping instances in *this*
-   module, one must still enable overlapping instances here; otherwise
-   overlapping (for type equality) is not resolved properly for the
-   imported modules.
-
 -}
 
-module MainGhcGeneric1 (
-
-{-
- module Datatypes2,
- module Data.HList.CommonMain,
- module Data.HList.TypeEqO,
- module Data.HList.Label3,
--- mainExport
--}
-
-) where
+module MainGhcGeneric1 where
 
 import Datatypes2
-import Data.HList.CommonMain -- hiding (HDeleteMany, hDeleteMany)
+import Data.HList.CommonMain
+import Control.Lens
 
-{-
-import Data.HList.RecordAdv
-import Data.HList.RecordP
--}
+asProxy :: proxy a -> Proxy a
+asProxy _ = Proxy
 
 
 -- --------------------------------------------------------------------------
@@ -108,11 +88,12 @@
 	    angus
 -- (H[Key 42, Name "Angus"],H[Cow, Price 75.5])
 
+
 testHOccurs = do
   putStrLn "\ntestHOccurs"
   print (hOccurs angus :: Breed)
-  print $ hOccurs (TIP (HCons 1 HNil))
-  print $ null $ hOccurs (TIP (HCons [] HNil))
+  print $ hOccurs $ hBuild 1 ^. from tipHList
+  print $ null $ hOccurs $ hBuild [] ^. from tipHList
   print (hProject angus :: HList '[Key, Name])
 
 
@@ -132,31 +113,21 @@
 -- This example from the TIR paper challenges singleton lists.
 -- Thanks to the HW 2004 reviewer who pointed out the value of this example.
 -- We note that the explicit type below is richer than the inferred type.
--- This richer type is needed for making this operation more polymorphic.
+-- This richer type was needed for making this operation more polymorphic.
 -- That is, /a)/ would not work without the explicit type, 
 -- while /b/ would:
 --
 -- >  a)  ((+) (1::Int)) $ snd $ tuple oneTrue
 -- >  b)  ((+) (1::Int)) $ fst $ tuple oneTrue
+--
+-- Currently (2014) type signatures are not needed to define tipyTuple.
 
-tuple :: forall e1 e2 n l n2.
-    (HDeleteAtHNat n l,
-     HOccurs e1 (TIP l),
-     HOccurs e2 (TIP (HDeleteAtHNatR n l)),
-     HType2HNat e1 l n,
-     -- extra, not inferred
-     HType2HNat e2 l n2,
-     HOccurs e1 (TIP (HDeleteAtHNatR n2 l))
-    ) => TIP l -> (e1,e2)
-tuple (TIP l) = let
-                 x  = hOccurs (TIP l)
-                 l' = hDeleteAt (undefined::Proxy e1) l
-                 y  = hOccurs (TIP l')
-                in (x,y)
+tuple l = tipyTuple l
 
+
 -- | A specific tuple
 -- Need to import an instance of TypeEq to be able to run the examples
-oneTrue :: TIP '[Int, Bool]		-- inferred
+-- oneTrue :: TIP (TagR [Int, Bool])		-- inferred
 oneTrue = (1::Int) .*. True .*. emptyTIP
 
 testTuple = do
@@ -165,11 +136,10 @@
   print $ let b = not $ fst $ tuple oneTrue in (1::Int,b)
   print $ tuple oneTrue == (1::Int,True)
   print $ ((+) (1::Int)) $ fst $ tuple oneTrue
-  -- requires explicit type for tuple
   print $ ((+) (1::Int)) $ snd $ tuple oneTrue
 
 
-myTipyCow = TIP angus
+myTipyCow = angus ^. from tipHList
 
 animalKey :: ( SubType l (TIP Animal) -- explicit
              , HOccurs Key l          -- implicit
@@ -184,7 +154,7 @@
   putStrLn "\ntestTIP"
   print $ (hOccurs myTipyCow :: Breed)
   print $ BSE .*. myTipyCow
-  -- print $ hExtend Sheep $ myTipyCow
+  -- print $ Sheep .*. myTipyCow
   {- if we uncomment the line above, we get the type error
      about the violation of the TIP condition: Breed type
      occurs twice.
@@ -192,7 +162,7 @@
     No instance for (Fail * (TypeFound Breed))
       arising from a use of `hExtend'
   -}
-  print $ Sheep .*. tipyDelete (undefined::Proxy Breed) myTipyCow
+  print $ Sheep .*. hDeleteAtLabel (Label::Label Breed) myTipyCow
   print $ tipyUpdate Sheep myTipyCow
 
 {-
@@ -227,7 +197,7 @@
   print $ test3
   print $ test4
   print $ test5
-  print $ hProjectByLabels (hLabels (breed `HCons` price `HCons` HNil)) test5
+  print $ hProjectByLabels (labelsOf (breed `HCons` price `HCons` HNil)) test5
  where
   test3 = hDeleteAtLabel breed unpricedAngus
   test4 = breed .=. Sheep .@. unpricedAngus
@@ -276,13 +246,14 @@
 
 -}
 
-type AnimalCol = [Key,Name,Breed,Price]
 
+type AnimalCol = TagR [Key,Name,Breed,Price]
+
 testTIC = do
   putStrLn "\ntestTIC"
   print $ myCol
-  print $ (unTIC myCol :: Maybe Breed)
-  print $ (unTIC myCol :: Maybe Price)
+  print $ (hOccurs myCol :: Maybe Breed)
+  print $ (hOccurs myCol :: Maybe Price)
  where
   myCol = mkTIC Cow :: TIC AnimalCol
 
@@ -290,27 +261,32 @@
 
 myCol = mkTIC Cow :: TIC AnimalCol
 
-*TIC> unTIC myCol :: Maybe Breed
+*TIC> hOccurs myCol :: Maybe Breed
 Just Cow
-*TIC> unTIC myCol :: Maybe Price
+*TIC> hOccurs myCol :: Maybe Price
 Nothing
 *TIC> mkTIC "42" :: TIC AnimalCol
 Type error ...
-*TIC> unTIC myCol :: Maybe String
+*TIC> hOccurs myCol :: Maybe String
 Type error ...
 
 -}
 
-testVariant = (testVar1,(testVar2,(testVar3)))
+testVariant = do
+    putStrLn "\ntestVariant"
+    print testVar1
+    print testVar2
+    print testVar3
  where
-  animalVar =  key   .=. (Proxy::Proxy Integer)
-           .*. name  .=. (Proxy::Proxy String)
-           .*. breed .=. (Proxy::Proxy Breed)
-           .*. emptyRecord
   testVar1 = mkVariant name "angus" animalVar
-  testVar2 = unVariant key testVar1
-  testVar3 = unVariant name testVar1
+  testVar2 = testVar1 .!. key
+  testVar3 = testVar1 .!. name
 
+animalVar = asProxy $
+               key   .=. (undefined :: Integer)
+           .*. name  .=. (undefined :: String)
+           .*. breed .=. (undefined :: Breed)
+           .*. emptyRecord
 {-
 -- --------------------------------------------------------------------------
 
@@ -339,3 +315,4 @@
        testTIP
        testRecords
        testTIC
+       testVariant
diff --git a/examples/MainGhcGeneric1.out b/examples/MainGhcGeneric1.out
deleted file mode 100644
--- a/examples/MainGhcGeneric1.out
+++ /dev/null
@@ -1,54 +0,0 @@
-
-Basic HList tests
-Key 42
-Name "Angus"
-Cow
-Price 75.5
-H[Key 42, Name "Angus", Cow, Price 75.5, Key 42, Name "Angus", Cow, Price 75.5]
-
-testHArray
-H[Key 42]
-H[Key 42, Name "Angus"]
-H[Key 42, Name "Angus"]
-H[Name "Angus", Cow, Price 75.5]
-(H[Key 42, Name "Angus"],H[Cow, Price 75.5])
-
-testHOccurs
-Cow
-1
-True
-H[Key 42, Name "Angus"]
-
-testTypeIndexed
-H[Key 42, Cow, Price 75.5]
-H[BSE, Key 42, Name "Angus", Cow, Price 75.5]
-H[Key 42, Sheep, Price 75.5]
-H[BSE, Key 42, Name "Angus", Price 75.5]
-H[Cow]
-(H[Cow],H[Key 42, Name "Angus", Price 75.5])
-
-testTuple
-(2,False)
-(1,False)
-True
-2
-2
-
-testTIP
-Cow
-TIPH[BSE, Key 42, Name "Angus", Cow, Price 75.5]
-TIPH[Sheep, Key 42, Name "Angus", Price 75.5]
-TIPH[Key 42, Name "Angus", Sheep, Price 75.5]
-
-testRecords
-Record{key=42,name="Angus",breed=Cow}
-Cow
-Record{key=42,name="Angus"}
-Record{key=42,name="Angus",breed=Sheep}
-Record{price=8.8,key=42,name="Angus",breed=Cow}
-Record{price=8.8,breed=Cow}
-
-testTIC
-<Cannot show TIC content!>
-Just Cow
-Nothing
diff --git a/examples/MainGhcGeneric1.ref b/examples/MainGhcGeneric1.ref
--- a/examples/MainGhcGeneric1.ref
+++ b/examples/MainGhcGeneric1.ref
@@ -4,28 +4,28 @@
 Name "Angus"
 Cow
 Price 75.5
-H[Key 42, Name "Angus", Cow, Price 75.5, Key 42, Name "Angus", Cow, Price 75.5]
+H[Key 42,Name "Angus",Cow,Price 75.5,Key 42,Name "Angus",Cow,Price 75.5]
 
 testHArray
 H[Key 42]
-H[Key 42, Name "Angus"]
-H[Key 42, Name "Angus"]
-H[Name "Angus", Cow, Price 75.5]
-(H[Key 42, Name "Angus"],H[Cow, Price 75.5])
+H[Key 42,Name "Angus"]
+H[Key 42,Name "Angus"]
+H[Name "Angus",Cow,Price 75.5]
+(H[Key 42,Name "Angus"],H[Cow,Price 75.5])
 
 testHOccurs
 Cow
 1
 True
-H[Key 42, Name "Angus"]
+H[Key 42,Name "Angus"]
 
 testTypeIndexed
-H[Key 42, Cow, Price 75.5]
-H[BSE, Key 42, Name "Angus", Cow, Price 75.5]
-H[Key 42, Sheep, Price 75.5]
-H[BSE, Key 42, Name "Angus", Price 75.5]
+H[Key 42,Cow,Price 75.5]
+H[BSE,Key 42,Name "Angus",Cow,Price 75.5]
+H[Key 42,Sheep,Price 75.5]
+H[BSE,Key 42,Name "Angus",Price 75.5]
 H[Cow]
-(H[Cow],H[Key 42, Name "Angus", Price 75.5])
+(H[Cow],H[Key 42,Name "Angus",Price 75.5])
 
 testTuple
 (2,False)
@@ -36,9 +36,9 @@
 
 testTIP
 Cow
-TIPH[BSE, Key 42, Name "Angus", Cow, Price 75.5]
-TIPH[Sheep, Key 42, Name "Angus", Price 75.5]
-TIPH[Key 42, Name "Angus", Sheep, Price 75.5]
+TIPH[BSE,Key 42,Name "Angus",Cow,Price 75.5]
+TIPH[Sheep,Key 42,Name "Angus",Price 75.5]
+TIPH[Key 42,Name "Angus",Sheep,Price 75.5]
 
 testRecords
 Record{key=42,name="Angus",breed=Cow}
@@ -49,6 +49,11 @@
 Record{price=8.8,breed=Cow}
 
 testTIC
-<Cannot show TIC content!>
+TIC{breed=Cow}
 Just Cow
 Nothing
+
+testVariant
+V{name="angus"}
+Nothing
+Just "angus"
diff --git a/examples/MainGhcGeneric2.out b/examples/MainGhcGeneric2.out
deleted file mode 100644
--- a/examples/MainGhcGeneric2.out
+++ /dev/null
@@ -1,1 +0,0 @@
-(HTrue,HFalse)
diff --git a/examples/MainPatternMatch.hs b/examples/MainPatternMatch.hs
--- a/examples/MainPatternMatch.hs
+++ b/examples/MainPatternMatch.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE FlexibleInstances #-}
@@ -31,7 +33,7 @@
 
 module Main where
 
-import Data.HList
+import Data.HList.CommonMain
 
 makeLabels ["px","py"]
 
@@ -46,7 +48,7 @@
 -}
 
 
-accessor r f = r # f
+accessor r f = r .!. f
 
 -- 1D points
 point1 x = 
@@ -62,7 +64,7 @@
 
 -- Record-polymorphic function, which illustrates record pattern-matching,
 -- with the help of generalized guards
-foo p | 0 <- p # px = "X is zero"
+foo p | 0 <- p .!. px = "X is zero"
 foo _ = "something else"
 
 test1  = foo (point1 0)     -- X is zero
diff --git a/examples/MainPatternMatch.out b/examples/MainPatternMatch.out
deleted file mode 100644
--- a/examples/MainPatternMatch.out
+++ /dev/null
@@ -1,4 +0,0 @@
-X is zero
-something else
-something else
-X is zero
diff --git a/examples/MainPosting-040607.hs b/examples/MainPosting-040607.hs
--- a/examples/MainPosting-040607.hs
+++ b/examples/MainPosting-040607.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE UndecidableInstances #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE DataKinds #-}
@@ -22,7 +23,8 @@
 
 module Main where
 
-import Data.HList
+import Data.HList.CommonMain
+import Data.Typeable
 
 -- These are your two "implementations".
 
diff --git a/examples/MainPosting-040607.out b/examples/MainPosting-040607.out
deleted file mode 100644
--- a/examples/MainPosting-040607.out
+++ /dev/null
@@ -1,1 +0,0 @@
-(H[MyImplementation1 10, MyImplementation2 20],(True,(True,(False,False))))
diff --git a/examples/MainPosting-040607.ref b/examples/MainPosting-040607.ref
--- a/examples/MainPosting-040607.ref
+++ b/examples/MainPosting-040607.ref
@@ -1,1 +1,1 @@
-(H[MyImplementation1 10, MyImplementation2 20],(True,(True,(False,False))))
+(H[MyImplementation1 10,MyImplementation2 20],(True,(True,(False,False))))
diff --git a/examples/MainPosting-051106.out b/examples/MainPosting-051106.out
deleted file mode 100644
--- a/examples/MainPosting-051106.out
+++ /dev/null
@@ -1,2 +0,0 @@
-8
-8
diff --git a/examples/Properties.hs b/examples/Properties.hs
new file mode 100644
--- /dev/null
+++ b/examples/Properties.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -fcontext-stack=100 #-}
+{-# OPTIONS_GHC -fno-warn-deprecations #-} -- ghc-7.8 has no Typeable (x :: Symbol), so use OldTypeable
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE UndecidableInstances #-}
+-- | Description: quickcheck tests
+--
+-- Many of the tests here use quickcheck. The lengths of the hlists
+-- involved exhaustively cover a small range, while the elements
+-- are random samples.
+module Main where
+
+import Test.Hspec
+
+import Properties.LengthDependentSplice
+import Properties.LengthIndependent
+import Properties.KW
+
+main = hspec $ do
+   hl0
+   hl1_2_3
+   kwSpecs
+
+
+
+
+
+
+
+
diff --git a/examples/Properties/Common.hs b/examples/Properties/Common.hs
new file mode 100644
--- /dev/null
+++ b/examples/Properties/Common.hs
@@ -0,0 +1,139 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeOperators #-}
+module Properties.Common where
+
+import Data.HList.CommonMain
+import Test.QuickCheck
+import Data.Array.Unboxed
+import Data.HList.Variant
+import Data.Monoid
+import Control.Lens
+import Control.Applicative
+import GHC.TypeLits (Symbol)
+import Language.Haskell.TH
+
+hListT :: [TypeQ] -> TypeQ
+hListT = foldr (\a b -> [t| $a ': $b |]) promotedNilT
+
+hListE :: [ExpQ] -> ExpQ
+hListE = foldr (\a b -> [| $a `HCons` $b |]) [| HNil |]
+
+hNatE :: Int -> ExpQ
+hNatE n = foldr appE [| hZero |] (replicate n [| hSucc |])
+
+hNatT :: Int -> TypeQ
+hNatT n = foldr appT [t| HZero |] (replicate n [t| HSucc |])
+
+
+lx = Label :: Label "x"
+ly = Label :: Label "y"
+lz = Label :: Label "z"
+
+data BinF b = BinF (b -> b -> b)
+
+instance (bb ~ (b, b), b ~ b') => ApplyAB (BinF b') bb b where
+    applyAB (BinF f) = uncurry f
+
+
+-- | A more general type than @===@ used to
+-- ensure that both sides can infer the same type
+eq :: (Show a, Show b, HCast a b, HCast b a, Eq a, Eq b) => a -> b -> Property
+eq x y = hCast x === Just y .&&. Just x === hCast y
+infix 4 `eq`
+
+data HSuccF = HSuccF
+
+instance (psn ~ Proxy (HSucc n),
+        pn ~ Proxy n) => ApplyAB HSuccF pn psn where
+    applyAB _ = hSucc
+
+
+data HSplitAtAppend l = HSplitAtAppend (HList l)
+instance (pn ~ Proxy n,
+          HSplitAt n l a b,
+          HAppend (HList a) (HList b),
+          y ~ HAppendR (HList a) (HList b)) => ApplyAB (HSplitAtAppend l) pn y where
+    applyAB (HSplitAtAppend l) n = case hSplitAt n l of
+                                     (a,b) -> hAppend a b
+
+
+
+data ConstTrue
+instance HEqByFn ConstTrue
+instance HEqBy ConstTrue x y True
+
+data ConstFalse
+instance HEqByFn ConstFalse
+instance HEqBy ConstFalse x y False
+
+
+instance Arbitrary a => Arbitrary (Tagged t a) where
+    arbitrary = fmap Tagged arbitrary
+
+instance Arbitrary (HList '[]) where
+    arbitrary = return HNil
+
+instance (Arbitrary x, Arbitrary (HList xs)) => Arbitrary (HList (x ': xs)) where
+    arbitrary = do
+      x <- arbitrary
+      xs <- arbitrary
+      return $ x `HCons` xs
+
+instance (Arbitrary x, Arbitrary (Variant (Tagged t y ': ys)),
+          HExtend (Tagged s (Maybe x)) (Variant (Tagged t y ': ys)),
+          HNat2Integral (HLength ys))
+        => Arbitrary (Variant (Tagged s x ': Tagged t y ': ys)) where
+    arbitrary = do
+      let nys = hNat2Integral (Proxy :: Proxy (HLength ys))
+      x :: Maybe x <- frequency [ (1, Just <$> arbitrary), (nys+1, return Nothing) ]
+      yys :: Variant (Tagged t y ': ys) <- arbitrary
+      return $ Tagged x .*. yys
+
+instance Arbitrary z => Arbitrary (Variant '[Tagged t z]) where
+    arbitrary = do
+      z <- arbitrary
+      return $ mkVariant1 Label z
+
+instance (CoArbitrary x, CoArbitrary (Variant (y ': z)),
+         HNat2Integral n, n ~ HLength (y ': z)) => CoArbitrary (Variant (x ': y ': z)) where
+    coarbitrary v = case splitVariant1' v of
+          Left x -> variant (hNat2Integral (Proxy :: Proxy n) :: Int) . coarbitrary x
+          Right v' -> coarbitrary v'
+
+instance (CoArbitrary v, Unvariant '[Tagged t v] v) => CoArbitrary (Variant '[Tagged t v]) where
+    coarbitrary v = coarbitrary (unvariant v)
+
+-- | This type is used to make unique types with two members.
+--
+-- > (BoolN True :: BoolN "x") /= (BoolN True :: BoolN "y")
+--
+-- is a type error
+newtype BoolN (n :: Symbol) = BoolN Bool
+  deriving (Eq,CoArbitrary,Arbitrary,Show,Read,Ord)
+
+boolN next = simple $ iso (\(BoolN x) -> x) BoolN next
+
+instance Monoid (BoolN n) where
+    mempty = BoolN (getAll mempty)
+    mappend (BoolN x) (BoolN y) = BoolN (getAll (mappend (All x) (All y)))
+
+instance Arbitrary (Identity (BoolN n)) where
+    arbitrary = fmap return arbitrary
+
+
+data HSortF = HSortF
+instance (x ~ Record xs,
+          y ~ Record ys,
+          HRLabelSet ys,
+          HSort xs ys) => ApplyAB HSortF x y where
+  applyAB _ (Record x) = mkRecord (hSort x)
diff --git a/examples/Properties/KW.hs b/examples/Properties/KW.hs
new file mode 100644
--- /dev/null
+++ b/examples/Properties/KW.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE NoMonoLocalBinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE DataKinds #-}
+module Properties.KW where
+
+
+import Properties.Common
+import Test.QuickCheck
+import Data.HList.CommonMain
+import Test.Hspec
+
+
+kwSpecs = describe "kw" $ do
+    it "f1" $ property $ do
+      (f1 :: BoolN "x" -> BoolN "y") <- arbitrary
+      x :: BoolN "x" <- arbitrary
+      x2 :: BoolN "x" <- arbitrary
+      let f2 (Label :: Label "x") x () = f1 x
+          f = f2 .*. recToKW [pun| x |]
+      return $ conjoin
+        [ kw f lx x2 () `eq` f1 x2,
+          kw f () `eq` f1 x ]
+
+    -- a function of two arguments can be made into a keyword function
+    it "f2" $ property $ do
+      (f1 :: BoolN "x" -> BoolN "y" -> BoolN "z") <- arbitrary
+      x :: BoolN "x" <- arbitrary
+      x2 :: BoolN "x" <- arbitrary
+      y :: BoolN "y" <- arbitrary
+      y2 :: BoolN "y" <- arbitrary
+
+      let f2 (_ :: Label "x") x (_ :: Label "y") y () = f1 x y
+          f = f2 .*. recToKW [pun| x y |]
+
+      return $ conjoin
+        [ kw f lx x2 ly y2 () `eq` f1 x2 y2,
+          kw f ly y2 lx x2 () `eq` f1 x2 y2,
+          kw f ly y2 () `eq` f1 x y2,
+          kw f lx x2 () `eq` f1 x2 y,
+          kw f () `eq` f1 x y ]
+
+    -- alternatively, a function taking a record is pretty much
+    -- a keyword argument. Error messages for missing keywords
+    -- are a bit worse (blame hRearrange')
+    it "f2Alt" $ property $ do
+      (f1 :: BoolN "x" -> BoolN "y" -> BoolN "z") <- arbitrary
+      x :: BoolN "x" <- arbitrary
+      x2 :: BoolN "x" <- arbitrary
+      y :: BoolN "y" <- arbitrary
+      y2 :: BoolN "y" <- arbitrary
+
+      let addDef new = hRearrange' (new .<++. [pun| x y |])
+          f2 (addDef  -> [pun| (x y) |]) = f1 x y
+      return $ conjoin
+        [ f2 emptyRecord `eq` f1 x y,
+          f2 (lx .=. x2 .*. emptyRecord) `eq` f1 x2 y,
+          f2 (ly .=. y2 .*. emptyRecord) `eq` f1 x y2,
+          f2 (lx .=. x2 .*. ly .=. y2 .*. emptyRecord) `eq` f1 x2 y2,
+          f2 (ly .=. y2 .*. lx .=. x2 .*. emptyRecord) `eq` f1 x2 y2
+        ]
+
diff --git a/examples/Properties/LengthDependent.hs b/examples/Properties/LengthDependent.hs
new file mode 100644
--- /dev/null
+++ b/examples/Properties/LengthDependent.hs
@@ -0,0 +1,411 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TemplateHaskell #-}
+-- NOTE:
+--
+-- To be able to compile with ghc-7.6 functions like foo are sometimes
+-- called
+--
+-- $(varE 'foo) because this prevents ghc-7.6 from failing to typecheck
+-- the expression (which fails because the number of elements in the
+-- supplied HList isn't known until Properties.LengthDependentSplice)
+module Properties.LengthDependent where
+
+
+import Data.HList.HSort (hMSortBy)
+import Data.HList.Variant (eqVariant)
+import Data.HList.Record (hZipRecord2)
+import Data.HList.CommonMain
+
+
+import Language.Haskell.TH
+import Test.QuickCheck
+import Properties.Common
+import Test.Hspec
+import Control.Lens
+import Data.List (sort,permutations)
+import Data.Monoid
+
+hlN :: Int -> ExpQ
+hlN n = [| \proxy -> hSequence
+              $ $(varE 'hReplicate) $(hNatE n)
+                    (arbitrary `asTypeOf` return proxy) |]
+
+-- > $(rKN id n) (undefined :: t) :: Arbitrary t => Gen (HList [Record t1, Record t2, ... ])
+--
+-- where
+--    t1 ~ '[Tagged 1 t, Tagged 2 t, Tagged 3 t, ... , Tagged n t]
+--    t2 ~ '[Tagged 2 t, Tagged 1 t, Tagged 3 t, ... , Tagged n t]
+--    tN ~ nth permutation of t1
+rKN :: (forall a. [a] -> [a]) -- ^ take some subset of the permutations of 1 .. n
+    -> Int
+    -> ExpQ
+rKN takeK n = [| \proxy -> do
+        $(recs [| arbitrary `asTypeOf` return proxy |])
+            `asTypeOf` return $sig
+         |]
+    where sig = [| undefined |] `sigE` quantify [t| HList $(hListT rss) |]
+
+
+          ti :: Int -> Name
+          ti i = mkName ("t" ++ show i)
+
+          recs gen = doE $
+               [ bindS (varP (ti i)) gen   | i <- [1 .. n] ] ++
+               [ noBindS
+                  [| return $ $(hListE
+                      [ [| unlabeled # $(hListE (map (varE . ti) is)) |]
+                        | is <- takeK $ permutations [1 .. n] ])
+                   |]
+                ]
+
+          quantify :: TypeQ -> TypeQ
+          quantify ty = forallT [ PlainTV (mkName ("x" ++ show i)) | i <- [1 .. n]] (return []) ty
+
+
+          rss :: [TypeQ]
+          rss = takeK $
+                [ [t| (Record :: [*] -> *) $(hListT (map taggedN ns)) |]
+                   | ns <- permutations [1 .. fromIntegral n] ]
+
+          -- taggedN 1 == [t| Tagged 1 x1 |]
+          taggedN :: Integer -> TypeQ
+          taggedN i = [t| Tagged $(litT (numTyLit i)) $(varT (mkName ("x"++show i))) |]
+
+rN n = [| \proxy -> $(varE 'hHead) `fmap` $(rKN (take 1) n) proxy |]
+
+vN :: Int -> ExpQ
+vN n = [| \proxy -> do
+       let toV :: Gen (Record a) -> Variant a
+           toV = undefined
+       v <- arbitrary
+       return (v `asTypeOf` toV ($(rN n) proxy))
+  |]
+
+-- specs for 1 HList of length >= 1
+hl1 n1 = [| do
+  let -- | generate a HList of length nMax containing elements
+      -- selected from there
+      genHL proxy = $(hlN n1) proxy
+
+  it "hConcat/hAppend" $
+      property $ do
+        x <- genHL True
+        y <- genHL True
+        return $ conjoin [$(varE 'hConcat) ($(varE 'hBuild) x y) == hAppend x y,
+                          $(varE 'hConcat) (hBuild x) == x]
+
+  it "partition" $
+      property $ do
+        x <- genHL True
+        return $ conjoin
+          [hPartitionEq (Proxy :: Proxy ConstTrue) (Proxy :: Proxy ()) x `eq` (x, HNil),
+           hPartitionEq (Proxy :: Proxy ConstFalse) (Proxy :: Proxy ()) x `eq` (HNil, x)]
+
+
+  it "listAsHList/hList2List" $ do
+      property $ do
+        x <- genHL True
+        return $ conjoin [
+            review listAsHList x `eq` hList2List x,
+            review listAsHList' x `eq` hList2List x]
+
+  it "read/show" $
+      property $ do
+        xs <- genHL True
+        return $ read (show xs) == xs
+
+  it "hLength/hReplicate" $
+      property $ do
+        xs <- genHL True
+        return $ hNat2Integral (hLength xs) == hNat2Integral $(hNatE n1)
+
+  it "hInits last id" $
+      property $ do
+        xs <- genHL True
+        return $ $(varE 'hLast) (hInits xs) == xs
+
+  it "hInits head empty" $
+      property $ do
+        xs <- genHL True
+        return $ hHead (hInits xs) == HNil
+
+  it "hTails head id" $
+      property $ do
+        xs <- genHL True
+        return $ hHead (hTails xs) == xs
+
+  it "hTails last empty" $
+      property $ do
+        xs <- genHL True
+        return $ $(varE 'hLast) (hTails xs) == HNil
+
+  it "hScanr equals scanr" $
+      property $ do
+        f <- arbitrary
+        a <- arbitrary
+        hl <- genHL True
+        return $ hList2List (hScanr (BinF f) a hl)
+                == scanr f a (hList2List hl)
+
+  it "hFoldr equals foldr" $
+      property $ do
+        f <- arbitrary
+        a <- arbitrary
+        hl <- genHL True
+        return $ hFoldr (BinF f) a hl == foldr f a (hList2List hl)
+
+  it "hFoldr1 equals foldr1" $
+      property $ do
+        f <- arbitrary
+        hl <- genHL True
+        return $ hFoldr1 (BinF f) hl == foldr1 f (hList2List hl)
+
+  it "hFoldl equals foldl" $
+      property $ do
+        f <- arbitrary
+        a <- arbitrary
+        hl <- genHL True
+        return $ hFoldl (BinF f) a hl == foldl f a (hList2List hl)
+
+  it "hSplitAt" $
+      property $ do
+        hl <- genHL True
+        let n = hLength hl
+            l = hList2List hl
+        -- hList2List doesn't like empty lists, and hMapOut id needs
+        -- annotations, so the following cases are easier to construct
+        -- than a direct comparison with splitAt
+        return $ conjoin
+          [ case hSplitAt hZero hl of
+              (hNil, hl') -> (hNil `eq` HNil) .&&. (hl' `eq` hl),
+            case $(varE 'hSplitAt) n hl of
+              (hl', hNil) -> (hNil `eq` HNil) .&&. (hl' `eq` hl),
+
+            $(varE 'hMap) (HSplitAtAppend hl) ($(varE 'hIterate) (hSucc n) HSuccF hZero) `eq` $(varE 'hReplicate) (hSucc n) hl ,
+            map (\n -> uncurry (++) $ splitAt n l) [0 .. length l]      === replicate (length l+1) l
+              -- the equivalent list-version
+           ]
+
+  it "hAppend empty is identity" $
+      property $ do
+        x <- genHL (BoolN True :: BoolN "x")
+        return $ all (== x) [$(varE 'hAppend) HNil x, $(varE 'hAppend) x HNil]
+
+  it "hReverse involution" $ do
+      property $ do
+        x <- genHL True
+        return $ x == $(varE 'hReverse) (hReverse x)
+
+  it "hReverse does nothing for ()" $
+      let xs = $(varE 'hReplicate) $(hNatE n1) ()
+      in xs `shouldBe` $(varE 'hReverse) xs
+
+  it "hInit == tail on reverse" $
+      property $ do
+        let hInitReference xs = hReverse (hTail (hReverse xs))
+        hl <- genHL True
+        return $ $(varE 'hInit) hl `eq` $(varE 'hInitReference) hl
+
+  it "hList2List/list2HList" $ property $ do
+      x <- genHL True
+      return $ list2HList (hList2List x) === Just x
+
+  it "hMap equals map" $ property $ do
+      f  <- arbitrary
+      hl <- genHL True
+      return $ hList2List (hMap f hl) `eq` map (f :: Bool -> BoolN "f") (hList2List hl)
+
+  it "hZip" $ property $ do
+      x <- genHL (BoolN True :: BoolN "x")
+      y <- genHL (BoolN True :: BoolN "y")
+      return $ hList2List (hZip x y) `eq` hList2List x `zip` hList2List y
+
+  it "hZipRecord" $ property $ do
+      x <- $(rN n1) (BoolN True :: BoolN "x")
+      y <- $(rN n1) (BoolN True :: BoolN "y")
+      let r1 = hZip x y ^. unlabeled & hList2List
+          r2 = hZipRecord2 x y ^. unlabeled & hList2List
+          r_ = hList2List (x ^. unlabeled) `zip` hList2List (y ^. unlabeled)
+
+      return $ conjoin [
+          r1 `eq` r_,
+          r2 `eq` r_,
+          hUnzip (hZip x y) `eq` (x,y) ]
+
+  it "hZip/hUnZip" $ property $ do
+      x <- genHL (BoolN True :: BoolN "x")
+      y <- genHL (BoolN True :: BoolN "y")
+      return $ hUnzip (hZip x y) == (x,y)
+
+  it "hUnzip/hZip" $ property $ do
+      xy <- genHL (BoolN True :: BoolN "x", BoolN True :: BoolN "y")
+      let (x,y) = hUnzip xy
+      return $ xy `eq` hZip x y
+
+#if __GLASGOW_HASKELL__ < 710
+ -- XXX doesn't work with ghc-7.10 RC1
+  it "hZip/hZip2" $ property $ do
+      x <- genHL (BoolN True :: BoolN "x")
+      y <- genHL (BoolN True :: BoolN "y")
+      return $ hZip x y `eq` hZip2 x y
+#endif
+
+  it "HList monoid assoc" $
+    property $ do
+      x <- genHL (BoolN True :: BoolN "x")
+      y <- genHL (BoolN True :: BoolN "x")
+      z <- genHL (BoolN True :: BoolN "x")
+      return $ ((x `mappend` y) `mappend` z) `eq` (x `mappend` (y `mappend` z))
+
+  it "HList monoid unit" $
+    property $ do
+      x <- genHL (BoolN True :: BoolN "x")
+      return $ conjoin
+        [ x === (x `mappend` mempty),
+          x === (mempty `mappend` x) ]
+
+  it "Variant monoid assoc" $ property $ do
+    x <- $(vN n1) (BoolN True :: BoolN "x")
+    y <- $(vN n1) (BoolN True :: BoolN "x")
+    z <- $(vN n1) (BoolN True :: BoolN "x")
+    return $ ((x `mappend` y) `mappend` z) `eq` (x `mappend` (y `mappend` z))
+
+  it "Variant == /eqVariant" $ property $ do
+    x <- $(vN n1) (BoolN True :: BoolN "x")
+    y <- $(vN n1) (BoolN True :: BoolN "x")
+    return $ conjoin [ eqVariant x y == (x == y),
+                       (x == y) == (y == x) ]
+
+  it "Variant ord" $ property $ do
+    x <- $(vN n1) (BoolN True :: BoolN "x")
+    y <- $(vN n1) (BoolN True :: BoolN "x")
+    z <- $(vN n1) (BoolN True :: BoolN "x")
+    let xyz = [x,y,z]
+        s:ss = map sort (permutations xyz)
+    return $ all (s ==) ss
+
+#if __GLASGOW_HASKELL__ > 707
+  -- ghc-7.6 has no ordering for Nat (only for HNat)
+  it "hSort (the labels)" $ property $ do
+    x <- $(rN n1) True
+    let rx = x & from hListRecord %~ hReverse
+    -- rN generates a record that has labels in ascending order already
+    return $ conjoin [
+         x `eq` (x  & from hListRecord %~ hSort),
+         x `eq` (rx & from hListRecord %~ hSort),
+         x `eq` (x  & from hListRecord %~ hMSortBy (Proxy :: Proxy HLeFn)),
+         x `eq` (rx & from hListRecord %~ hMSortBy (Proxy :: Proxy HLeFn))
+         ]
+
+  -- restrict to lists of length 4 (since then the number of permutations
+  -- is a manageable 24 not 120)
+  it "hSort permutations" $ property $ do
+    xs <- $(rKN id (min 4 n1)) True
+    return $ all (== hHead xs) (hMapOut HSortF xs)
+
+#endif
+
+  it "hRenameLabel" $ property $ do
+    r <- $(rN n1) True
+    return $ conjoin
+        $(listE [ [| hRenameLabel $ln lx r .!. lx === r .!. $ln |]
+              | i <- [1 .. n1],
+                let ln = [| Label :: Label $(litT (numTyLit (fromIntegral i))) |]
+            ])
+  it "rearranged / hMapR" $ property $ do
+    r <- $(rN n1) True
+    let revR = r & from hListRecord %~ hReverse
+        asT :: x -> As x
+        asT _ = id
+    -- hMap works on the reversed list
+    return $ hMapR not r === (r & rearranged' . asT revR . unlabeled %~ hMap not)
+
+
+  it "hOccurs" $ property $ do
+    w <- arbitrary :: Gen (BoolN "w")
+    x <- genHL (BoolN True :: BoolN "x")
+    y <- genHL (BoolN True :: BoolN "y")
+    z <- genHL (BoolN True :: BoolN "z")
+    let xyz = hConcat (hBuild x y z)
+        hxyz = hEnd (hBuild (hHead x) (hHead y) (hHead z))
+        hM v = hOccursMany xyz === hList2List v
+    return $ conjoin
+      [ hM x, hM y, hM z,
+        hOccurs (hConcat (hBuild x (HCons w HNil) z)) === w,
+        hOccursOpt xyz === (Nothing `asTypeOf` Just w)
+        -- hProject hxyz === hBuild (hHead x) (hHead y)
+       ]
+
+ |]
+
+hl2 n1 n2 = [| do
+  it "splitVariant" $ property $ do
+    x <- $(vN (n1 + n2)) True
+    let testV :: forall n x yin yout.
+               (Eq (Variant x),
+                SplitVariant x yin yout,
+                HSplitAt n x yin yout,
+                ExtendsVariant yin x,
+                ExtendsVariant yout x) =>
+                Proxy n -> Variant x -> Bool
+        testV n v = case $(varE 'splitVariant) v of
+                      Left a -> extendsVariant (a :: Variant yin) == v
+                      Right a -> extendsVariant (a :: Variant yout) == v
+    return $ $(varE 'testV) $(hNatE n1) x
+
+
+  it "hAppend equals ++" $
+    property $ do
+      x <- $(hlN n1) True
+      y <- $(hlN n2) True
+      return $ hList2List (hAppend x y) === hList2List x ++ hList2List y
+
+  it "hTranspose involution" $ property $ do
+    x <- return (error "hTranspose involution") `asTypeOf` $(hlN n1) True
+    xx <- $(hlN n2) x
+    return $ $(varE 'hTranspose) ($(varE 'hTranspose) xx) === xx
+
+  it "leftUnion / unionSR" $
+    property $ do
+      x <- $(rN n1) True
+      y <- $(rN n2) True
+      let asL r = r ^. unlabeled . to hList2List
+          asLs (r1,r2) = (asL r1, asL r2)
+          merge xs ys = xs ++ drop (length xs) ys
+          mergeSym xs ys = (merge xs ys, merge ys xs)
+          eqSorted (a,b) (c,d) = sort a === sort c .&&. sort b === sort d
+      return $ conjoin [
+        asL (x .<++. y) === asL x `merge` asL y,
+        ($(varE '(.<++.)) x x) === x,
+        ($(varE '(.<++.)) y y) === y,
+        asLs (unionSR x y) `eqSorted` mergeSym (asL x) (asL y),
+        (x `unionSR` x) === (x,x),
+        (y `unionSR` y) === (y,y)]
+
+    |]
+
+hl3 n1 n2 n3 = [| do
+  it "hAppend/hAppendList assoc" $
+    property $ do
+      x <- $(hlN n1) (BoolN True :: BoolN "x")
+      y <- $(hlN n2) (BoolN True :: BoolN "y")
+      z <- $(hlN n3) (BoolN True :: BoolN "z")
+      return $ conjoin
+#if __GLASGOW_HASKELL__ < 707
+        [ $([| (x `hAppend` y) `hAppend` z |]) === $([| x `hAppend` (y `hAppend` z) |]),
+          $([| (x `hAppendList` y) `hAppendList` z|]) === $([| x `hAppendList` (y `hAppendList` z)|])
+        ]
+#else
+        [ ((x `hAppend` y) `hAppend` z) === (x `hAppend` (y `hAppend` z)),
+          ((x `hAppendList` y) `hAppendList` z) === (x `hAppendList` (y `hAppendList` z))
+        ]
+#endif
+  |]
+
+
diff --git a/examples/Properties/LengthDependentSplice.hs b/examples/Properties/LengthDependentSplice.hs
new file mode 100644
--- /dev/null
+++ b/examples/Properties/LengthDependentSplice.hs
@@ -0,0 +1,26 @@
+{-# OPTIONS_GHC -fcontext-stack=1000 #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TemplateHaskell #-}
+module Properties.LengthDependentSplice where
+import Properties.LengthDependent
+import Language.Haskell.TH
+import Test.Hspec
+
+hl1_2_3 :: Spec
+hl1_2_3 = $(doE $
+        [ noBindS
+               [| describe $(stringE (show n)) $(hl1 n) |]
+            | n <- [1 .. 5]]
+    ++ [ noBindS [| describe $(stringE (show (n1,n2))) $(hl2 n1 n2) |]
+      | n1 <- [1 .. 3],
+        n2 <- [1 .. 3] ]
+    ++ [ noBindS [| describe $(stringE (show (n1,n2,n3))) $(hl3 n1 n2 n3) |]
+      | n1 <- [0 .. 2],
+        n2 <- [0 .. 1],
+        n3 <- [0 .. 2],
+        not $ all (==0) [n1,n2,n3] ]
+  )
diff --git a/examples/Properties/LengthIndependent.hs b/examples/Properties/LengthIndependent.hs
new file mode 100644
--- /dev/null
+++ b/examples/Properties/LengthIndependent.hs
@@ -0,0 +1,635 @@
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE DataKinds #-}
+module Properties.LengthIndependent where
+import Properties.Common
+import Control.Lens
+import Data.HList.CommonMain
+import Test.Hspec
+import Test.QuickCheck
+import Data.Monoid
+import Data.Maybe
+import Control.Applicative
+import Control.Monad
+
+import Data.Generics
+
+makeLabels3 "lengthindependent" (words "lx_ ly_")
+
+
+-- | tests for a fixed length
+hl0 = describe "0 -- length independent"  $ do
+  hTuples
+
+  it "listAsHList" $ property $ do
+    (f :: Bool -> Bool) <- arbitrary
+    let bools = [True,False]
+        mapF (a `HCons` b `HCons` HNil) = f a `HCons` f b `HCons` HNil
+        len3 = id :: As (HList '[a,b,c])
+        len2 = id :: As (HList [a,b])
+        len1 = id :: As (HList '[a])
+    return $ conjoin
+      [ (bools & listAsHList %~ mapF) `eq` map f bools,
+        (bools & listAsHList' . len2 %~ hMap f ) `eq` map f bools,
+        (bools & listAsHList' . len3 %~ hMap f ) `eq` ([] :: [Bool]),
+        (bools & listAsHList' . len1 %~ hMap f ) `eq` ([] :: [Bool])]
+
+  it "read0" $ read "H[]" `shouldBe` HNil
+
+  it "Fun" $ property $ do
+    let plusF = Fun (+1) :: Fun Num '()
+    x :: Int <- arbitrary
+    y :: Double <- arbitrary
+    return $ hMap plusF (hBuild x y) === hEnd (hBuild (x+1) (y+1))
+
+  it "Fun 2" $ property $ do
+    let showSuccF = Fun (show . (+1)) :: Fun [Num,Show] String
+    x :: Int <- arbitrary
+    y :: Double <- arbitrary
+    return $ hMapOut showSuccF (hBuild x y) === [ show (x+1), show (y+1)]
+
+  it "Fun'" $ property $ do
+    x :: Bool <- arbitrary
+    return $ applyAB (Fun' read :: Fun' Read String) (show x) === x
+
+  it "HComp" $ property $ do
+    let f = Fun (+1) :: Fun Num '()
+        g = Fun show :: Fun Show String
+        gof = g `HComp` f
+
+    x :: Int <- arbitrary
+    y :: Double <- arbitrary
+
+    let ref = [show (x+1), show (y+1)]
+
+    return $ conjoin [
+        hMapOut gof (hBuild x y) `eq` ref,
+        hMapOut g (hMap f (hBuild x y)) `eq` ref ]
+
+  it "toLabel 1" $
+    case hCast (toLabel (hLens' lx)) of
+      l -> True `const` (l `asTypeOf` Just lx)
+
+  it "toLabel 2" $ case hCast (toLabel lx) of
+      l -> True `const` (l `asTypeOf` Just lx)
+
+  it "HCast is ==" $ property $ do
+    x :: BoolN "x" <- arbitrary
+    x' :: BoolN "x" <- arbitrary
+    return $ (x == x') ==> (x `eq` x')
+
+  it "HCast neq" $ property $ do
+    x :: BoolN "x" <- arbitrary
+    y :: BoolN "y" <- arbitrary
+    return (expectFailure $ x `eq` y)
+
+  let mkXYvariant = do
+        (x :: Bool) <- arbitrary
+        (my :: Maybe Bool) <- arbitrary
+        return $ (ly .=. my .*. mkVariant1 lx x,
+                  my)
+
+  it "variant lookup/extend" $ do
+    property $ do
+      (v, my) <- mkXYvariant
+      return $ conjoin [
+          v .!. ly == my,
+          v ^? hLens' ly == my,
+          v ^? hPrism ly == my ]
+
+  it "variant update" $ property $ do
+    x :: Maybe (BoolN "x") <- arbitrary
+    x' :: BoolN "x'" <- arbitrary
+    y :: BoolN "y" <- arbitrary
+    let v = lx .=. x .*. mkVariant1 ly y
+        v' | isJust x = lx .=. Just x' .*. mkVariant1 ly y
+           | otherwise = lx .=. Nothing .*. mkVariant1 ly y
+    return $ conjoin [
+        hUpdateAtLabel lx x' v === v',
+        (v & hLens' lx .~ x') === v',
+        (v & hPrism lx .~ x') === v']
+
+
+  it "unvariant" $ do
+    property $ do
+      (v, _) <- mkXYvariant
+      return $ unvariant v == fromJust (msum [v .!. ly, v .!. lx])
+
+  it "unvarianted" $ property $ do
+    x :: Maybe Bool <- arbitrary
+    y :: Bool <- arbitrary
+    let v = lx .=. x .*. mkVariant1 ly y
+        vUnitExpected = lx .=. (() <$ x) .*. mkVariant1 ly ()
+        vUnit = v & unvarianted .~ ()
+        vNot = lx .=. (not <$> x) .*. mkVariant1 ly (not y)
+    return $ conjoin [
+        v ^. unvarianted === fromMaybe y x,
+        (v & unvarianted %~ not) === vNot,
+        vUnit === vUnitExpected,
+        vUnit ^. unvarianted === ()  ]
+
+  it "HMapOutV" $ do
+    property $ do
+      (v, _) <- mkXYvariant
+      return $ hMapOutV not v `eq` not (fromJust (msum [v .!. ly, v .!. lx]))
+
+  it "zipVR" $ property $ do
+    (f :: BoolN "x" -> BoolN "x'",
+     g :: BoolN "y" -> BoolN "y'",
+     b,x,y) <- arbitrary
+    let p = lx .*. ly .*. emptyProxy
+        v  | b = mkVariant lx x Proxy `asLabelsOf` p
+           | otherwise = mkVariant ly y Proxy
+        v' | b = mkVariant lx (f x) Proxy `asLabelsOf` p
+           | otherwise = mkVariant ly (g y) Proxy
+
+        fun = lx .=. f .*. ly .=. g .*. emptyRecord
+
+    return $ zipVR fun v `eq` v'
+
+  it "zipVariant" $ property $ do
+    x1 :: Maybe (BoolN "x1") <- arbitrary
+    x2 :: Maybe (BoolN "x2") <- arbitrary
+    y1 :: BoolN "y1" <- arbitrary
+    y2 :: BoolN "y2" <- arbitrary
+
+    let v1 = lx .=. x1 .*. mkVariant1 ly y1
+        v2 = lx .=. x2 .*. mkVariant1 ly y2
+
+        vrT = Proxy :: Proxy '[ Tagged "x" (BoolN "x1", BoolN "x2"),
+                                Tagged "y" (BoolN "y1", BoolN "y2") ]
+        vr = case (x1,x2) of
+               (Just a, Just b) -> Just $ mkVariant lx (a,b) vrT
+               (Nothing, Nothing) -> Just $ mkVariant ly (y1,y2) vrT
+               _ -> Nothing
+
+    return $ zipVariant v1 v2 `eq` vr
+
+  it "variant Eq" $ property $ do
+    x1 :: Maybe (BoolN "x") <- arbitrary
+    x2 :: Maybe (BoolN "x") <- arbitrary
+    y1 :: BoolN "y" <- arbitrary
+    y2 :: BoolN "y" <- arbitrary
+
+    let v1 = lx .=. x1 .*. mkVariant1 ly y1
+        v2 = lx .=. x2 .*. mkVariant1 ly y2
+    return $ (v1 == v2) === (x1 == x2 && (isJust x1 || y1 == y2))
+
+
+  it "projectVariant" $ property $ do
+    a <- arbitrary
+    b <- arbitrary
+    c <- arbitrary
+    z <- arbitrary
+
+    let vFull :: Variant [Tagged "a" (BoolN "a"),
+                          Tagged "b" (BoolN "b"),
+                          Tagged "c" (BoolN "c"),
+                          Tagged "z" (BoolN "z")]
+        vFull = a .*. b .*. c .*. mkVariant1 Label z
+
+        isN x = isNothing (untag x)
+        vZJ :: Variant '[Tagged "z" (BoolN "z")]
+        vZJ = mkVariant1 Label z
+
+        vZ | isN a, isN b, isN c = Just vZJ
+           | otherwise = Nothing
+
+    return $ conjoin [
+       vFull ^? projected === vZ,
+       -- XXX maybe projected can be made to work instead of projected'
+       ((projected' # vZJ) `asTypeOf` vFull) ^? projected === Just vZJ
+       ]
+
+  it "variant/tic extend" $ do
+    property $ do
+      x :: BoolN "x" <- arbitrary
+      my :: Maybe (BoolN "y") <- arbitrary
+      let v = ly .=. my .*. mkVariant1 lx x
+          tic1 = my .*. mkTIC1 x
+      return $ conjoin
+        [ -- v == tic1 ^. from typeIndexed,
+          v^. typeIndexed == tic1
+          ]
+
+
+
+  it "variant/typeIndexed" $ do
+    property $ do
+      x :: BoolN "x" <- arbitrary
+      y :: Maybe (BoolN "y") <- arbitrary
+      let v = ly .=. y .*. mkVariant1 lx x
+      let tic = v ^. typeIndexed
+      return $ conjoin
+        [ v .!. ly === hOccurs tic,
+          v .!. ly === tic ^? ticPrism,
+          v .!. ly `eq` tic .!. (Label :: Label (BoolN "y")),
+
+          v .!. lx === hOccurs tic,
+          v .!. lx === tic ^? ticPrism,
+          v .!. lx `eq` tic .!. (Label :: Label (BoolN "x"))
+        ]
+
+  it "Record/typeIndexed" $ do
+    property $ do
+      x :: BoolN "x" <- arbitrary
+      y :: BoolN "y" <- arbitrary
+      let r = ly .=. y .*. lx .=. x .*. emptyRecord
+          tip = r ^. typeIndexed
+
+          asX :: As (BoolN "x")
+          asX = id
+          asY :: As (BoolN "y")
+          asY = id
+
+      return $ conjoin
+        [ r .!. lx === hOccurs tip,
+          r .!. lx === tip ^. tipyLens,
+          r .!. lx `eq` tip .!. (Label :: Label (BoolN "x")),
+
+          -- two ways to apply 'not' to the 'x' field
+          (r & hLens lx . boolN %~ not) `eq`
+              (r & typeIndexed %~ ttip (asX . boolN %~ not)),
+
+          -- and repeat everything for the other field
+          r .!. ly === hOccurs tip,
+          r .!. ly === tip ^. tipyLens,
+          r .!. ly `eq` tip .!. (Label :: Label (BoolN "y")),
+          (r & hLens ly . boolN %~ not) `eq`
+              (r & typeIndexed %~ ttip (asY . boolN %~ not))
+        ]
+
+  -- other operations union, projection etc.
+  it "Record lookup mixing labels" $ do
+    property $ do
+      v1 :: BoolN "v1" <- arbitrary
+      v2 :: BoolN "v2" <- arbitrary
+      v3 :: BoolN "v3" <- arbitrary
+      let l1 = Label :: Label ()
+          l2 = Label :: Label 2
+          l3 = Label :: Label "3"
+          p = Proxy :: Proxy '[Label (), Label 2 , Label "3"]
+
+          p1 = consl1 $ l2 .*. l3 .*. emptyProxy
+
+          -- HExtend doesn't support Label5
+          consl1 :: Proxy x -> Proxy (Label () ': x)
+          consl1 _ = Proxy
+
+          r = hEndR (hBuild v1 v2 v3) `asLabelsOf` p
+      return $ conjoin
+        [ r.!.l1 `eq` v1,
+          r.!.l2 `eq` v2,
+          p1 `eq` p ]
+
+  it "HOccurs HList" $ do
+    property $ do
+      x <- arbitrary
+      return $ hOccurs (hEnd (hBuild x)) == (x :: Bool)
+
+  it "HOccurs TIP" $ do
+    property $ do
+      x <- arbitrary
+      return $ hOccurs (hEnd (hBuild x) ^. from tipHList) == (x :: Bool)
+
+  it "HOccurs TIP inference" $
+    hOccurs (HCons True HNil^. from tipHList)
+      `eq` True
+
+  it "ttip 3" $ do
+    property $ do
+      f <- arbitrary
+      (a :: BoolN "a") <- arbitrary
+      (b :: BoolN "b") <- arbitrary
+      (c :: BoolN "c") <- arbitrary
+      let tp = a .*. b .*. c .*. emptyTIP
+      return $ hOccurs (ttip f tp) == (f a b c :: BoolN "a")
+
+  it "ttipM 3" $ do
+    property $ do
+      f <- arbitrary
+      (a :: BoolN "a") <- arbitrary
+      (b :: BoolN "b") <- arbitrary
+      (c :: BoolN "c") <- arbitrary
+      let tp = a .*. b .*. c .*. emptyTIP
+      return $ hOccurs (runIdentity (ttipM f tp)) == (runIdentity (f a b c) :: BoolN "a")
+
+  it "Show/Read instances" $ do
+    show (hEnd (hBuild 1 2 3)) `shouldBe` "H[1,2,3]"
+
+    let r = lx .=. 'x' .*. ly .=. "y" .*. emptyRecord
+    show r `shouldBe` "Record{x='x',y=\"y\"}"
+    read (show r) `shouldBe` r
+
+    show (r ^. unlabeled . from tipHList) `shouldBe` "TIPH['x',\"y\"]"
+
+    v <- return $ map ($ r) [mkVariant lx 'a', mkVariant ly "ly"]
+
+    show v `shouldBe` "[V{x='a'},V{y=\"ly\"}]"
+    read (show v) `shouldBe` v
+
+    show (map (^. typeIndexed') v) `shouldBe` "[TIC{char='a'},TIC{[Char]=\"ly\"}]"
+
+  it "Data instances gread/gshow" $ do
+    property $ do
+      a :: Maybe Bool <- arbitrary
+      b :: Bool <- arbitrary
+      let h = hEnd $ hBuild a b
+          v = lx_ .=. a .*. mkVariant1 ly_ b
+      return $ conjoin
+        [ gread (gshow h) === [(h, "")],
+          gread (gshow v) === [(v, "")] ]
+
+  it "Enum" $ do
+    show [ mkVariant lx False (Proxy :: Proxy '[Tagged "x" Bool, Tagged "y" Bool]) .. maxBound ]
+      `shouldBe` "[V{x=False},V{x=True},V{y=False},V{y=True}]"
+
+  it "minBound" $ do
+    mkVariant lx False (Proxy :: Proxy '[Tagged "x" Bool, Tagged "y" Bool])
+      `shouldBe` minBound
+
+
+
+  it "projected" $ do
+    property $ do
+      (f :: Bool -> Bool -> Bool) <- arbitrary
+      x :: Bool <- arbitrary
+      y :: Bool <- arbitrary
+      let r = lx .=. x .*. ly .=. y .*. lz .=. () .*. emptyRecord
+          g1 [pun| (x y) |] = case f x y of z -> [pun| z |]
+          g2 [pun| (y x) |] = case f x y of z -> [pun| z |]
+
+          rExpect = lx .=. x .*. ly .=. y .*. lz .=. f x y .*. emptyRecord
+
+      containX :: Bool <- arbitrary
+      let
+          v p | containX = mkVariant lx x p
+            | otherwise = mkVariant ly y Proxy
+
+
+          v1 = v (Proxy :: Proxy '[Tagged "x" Bool, Tagged "y" Bool, Tagged "z" Char])
+          v2 = v (Proxy :: Proxy '[Tagged "x" Bool, Tagged "y" Bool])
+
+          v1not = v1 & sameLength . sameLabels . projected %~ hMapV Just . (`asLabelsOf` labelsOf v2)
+
+
+      return $ conjoin
+        [ (r & sameLabels . projected %~ g1) `eq` rExpect
+        , (r & sameLabels . projected %~ g2) `eq` rExpect
+        , (v1 ^? projected) === Just v2
+        , review projected v2 === v1
+          ]
+
+
+
+{- RecordU is disabled for now
+  it "unboxed" $ do
+    property $ do
+      (x :: Bool) <- arbitrary
+      (y :: Bool) <- arbitrary
+      (z :: Bool) <- arbitrary
+      let r = [pun| x y z |]
+          ru = r ^. unboxed
+      return $ conjoin
+       [ ru .!. lx === x,
+         ru .!. ly === y,
+         ru .!. lz === z,
+         hUpdateMany r ru === ru,
+         hMapRU not ru ^. from unboxed . unlabeled . re listAsHList'
+                === map not [x,y,z],
+         r === ru ^. from unboxed ]
+
+  it "unboxedS" $ do
+    property $ do
+      (x :: Bool) <- arbitrary
+      (y :: Int) <- arbitrary
+      (z :: Int) <- arbitrary
+      let r = [pun| x y z |]
+          ru = r ^. unboxedS
+      return $ conjoin
+        [ ru .!. lx === x,
+          ru .!. ly === y,
+          ru .!. lz === z,
+          r === ru ^. from unboxedS ]
+         -}
+
+  it "monoid0" $ do
+    mempty `shouldBe` HNil
+    mempty `shouldBe` emptyRecord
+    mempty `shouldBe` emptyTIP
+    mempty `shouldBe` mkVariant1 lx ()
+    mempty `shouldBe` (mkVariant ly () (Proxy :: Proxy '[Tagged "x" [Int], Tagged "y" ()]))
+
+
+  it "identity: rearranged relabeled unlabeled" $ do
+    let r = lx .=. True .*.
+            ly .=. () .*. emptyRecord
+    (r ^. rearranged) `shouldBe` r
+    (r ^. relabeled) `shouldBe` r
+    (r & unlabeled %~ id) `shouldBe` r
+
+  it "rearranged" $ do
+    let r = lx .=. True .*.
+            ly .=. () .*. emptyRecord
+    let r2 = ly .=. () .*.
+            lx .=. True .*. emptyRecord
+    (r ^. rearranged) `shouldBe` r2
+
+  it "relabeled" $ do
+    let r = lx .=. True .*.
+            ly .=. () .*. emptyRecord
+    let r2 = ly .=. True .*.
+            lx .=. () .*. emptyRecord
+    (r ^. relabeled) `shouldBe` r2
+
+  it "hMaybied" $ property $ do
+    mx :: Maybe Bool <- arbitrary
+    my :: Maybe Bool <- arbitrary
+    let r = lx .=. mx .*. ly .=. my .*. emptyRecord
+        vT = Proxy :: Proxy [Tagged "x" Bool, Tagged "y" Bool]
+        (val, v)  = case (mx,my) of
+          (Just x, Nothing) -> (Just x, Just (mkVariant lx x vT))
+          (Nothing, Just y) -> (Just y, Just (mkVariant ly y vT))
+          _ -> (Nothing, Nothing)
+
+    return $ conjoin [
+      (r^?hMaybied <&> unvariant) `eq` val,
+      isJust v ==> ( hMaybied' # fromJust v === r ) ]
+
+  it "hMaybied 2" $ property $ do
+    x :: BoolN "x"  <- arbitrary
+    my :: Maybe (BoolN "y") <- arbitrary
+    let v = ly .=. my .*. mkVariant1 lx x
+        r = ly .=. my .*. lx .=. Just x .*. emptyRecord
+
+    return $ (r ^? hMaybied) `eq` do
+      guard $ isNothing my
+      Just v
+
+  it "hMaybied update" $ property $ do
+    (f :: BoolN "x" -> BoolN "w",y :: BoolN "y") <- arbitrary
+    let x  = lx .=. (Nothing :: Maybe (BoolN "x")) .*. ly .=. Just y .*. emptyRecord
+        x' = lx .=. (Nothing :: Maybe (BoolN "w")) .*. ly .=. Just y .*. emptyRecord
+
+    return $ (x & sameLength . hMaybied . hPrism lx %~ f) === x'
+
+  it "hPrism" $ property $ do
+    x :: Bool <- arbitrary
+    my :: Maybe (Maybe ()) <- arbitrary
+    let v  = ly .=. my .*. mkVariant1 lx x
+        v' = ly .=. my .*. mkVariant1 lx (not x)
+
+        tic = my .*. mkTIC1 x
+        tic' = my .*. mkTIC1 (not x)
+
+    return $ conjoin
+      [ v' `eq` (v & hPrism lx %~ not),
+        tic' `eq` (tic & hLens' (Label :: Label Bool) %~ not),
+        -- should work, but it doesn't
+        -- tic' `eq` (tic & hLens' Label %~ not),
+        tic' `eq` (tic & ticPrism %~ not)
+      ]
+
+  it "hDeleteAtLabel" $ property $ do
+    vx :: BoolN "x" <- arbitrary
+    vy :: BoolN "y" <- arbitrary
+    vz :: BoolN "z" <- arbitrary
+    let r  = lx .=. vx .*. ly .=. vy .*. lz .=. vz .*. emptyRecord
+        ry =               ly .=. vy .*. lz .=. vz .*. emptyRecord
+        rx = lx .=. vx .*.               lz .=. vz .*. emptyRecord
+
+    return $ conjoin [
+      (r .-. lx) `eq` ry,
+      (r .-. ly) `eq` rx ]
+
+  it "hBuild/hEndR" $ property $ do
+    vx :: BoolN "x" <- arbitrary
+    vy :: BoolN "y" <- arbitrary
+    vz :: BoolN "z" <- arbitrary
+    let r = hEndR (hBuild vx vy vz) `asLabelsOf` (lx .*. ly .*. lz .*. emptyProxy)
+        r_ = lx .=. vx .*. ly .=. vy .*. lz .=. vz .*. emptyRecord
+
+    return $ r `eq` r_
+
+
+  it "hUncurry" $ property $ do
+    vx :: BoolN "x" <- arbitrary
+    vy :: BoolN "y" <- arbitrary
+    vz :: BoolN "z" <- arbitrary
+    return $ conjoin
+      [ hUncurry (,,) (hBuild vx vy vz) `eq` (vx,vy,vz),
+        hCurry (hUncurry (,,)) vx vy vz `eq` (vx,vy,vz),
+        hCurry (hUncurry id) vx `eq` vx,
+        hCurry ( \(a `HCons` b `HCons` HNil) -> (b,a)) vx vy `eq` (vy,vx)
+      ]
+
+  it "hCompose" $ property $ do
+    vx :: BoolN "x" <- arbitrary
+    vy :: BoolN "y" <- arbitrary
+    vz :: BoolN "z" <- arbitrary
+    return $ conjoin
+      [ hCompose (,) (,) vx vy vz `eq` ((vx,vy), vz),
+        hCompose id (,) vx vy `eq` (vx,vy),
+        hCompose (,) id vx vy `eq` (vx,vy) ]
+
+
+hTuples = do
+  it "HTuple0" $ do
+    HNil ^. hTuple `shouldBe` ()
+    (HNil & hTuple %~ id) `shouldBe` HNil
+
+  it "HTuple2" $ property $ do
+    a <- arbitrary
+    b <- arbitrary
+    let ab = (a :: BoolN "a",b :: BoolN "b")
+    return $ hBuild a b ==  ab ^. from hTuple
+        && hBuild a b ^. hTuple == ab
+
+  it "HTuple3" $ property $ do
+    a <- arbitrary
+    b <- arbitrary
+    c <- arbitrary
+    let abc = (a :: BoolN "a",b :: BoolN "b",c :: BoolN "c")
+    return $ hBuild a b c ==  abc ^. from hTuple
+      && hBuild a b c ^. hTuple == abc
+
+  it "HTuple4" $ property $ do
+    a <- arbitrary
+    b <- arbitrary
+    c <- arbitrary
+    d <- arbitrary
+    let abc = (a :: BoolN "a",b :: BoolN "b",c :: BoolN "c",
+               d :: BoolN "d")
+    return $ hBuild a b c d ==  abc ^. from hTuple
+      && hBuild a b c d ^. hTuple == abc
+
+  it "HTuple5" $ property $ do
+    a <- arbitrary
+    b <- arbitrary
+    c <- arbitrary
+    d <- arbitrary
+    e <- arbitrary
+    let abc = (a :: BoolN "a",b :: BoolN "b",c :: BoolN "c",
+               d :: BoolN "d", e :: BoolN "e")
+    return $ hBuild a b c d e ==  abc ^. from hTuple
+      && hBuild a b c d e ^. hTuple == abc
+
+  it "HTuple6" $ property $ do
+    a <- arbitrary
+    b <- arbitrary
+    c <- arbitrary
+    d <- arbitrary
+    e <- arbitrary
+    f <- arbitrary
+    let abc = (a :: BoolN "a",b :: BoolN "b",c :: BoolN "c",
+               d :: BoolN "d", e :: BoolN "e", f :: BoolN "f")
+    return $ hBuild a b c d e f ==  abc ^. from hTuple
+      && hBuild a b c d e f ^. hTuple == abc
+
+
+
+
+-- XXX projected
+v =  mkVariant (Label :: Label "x") () (Proxy :: Proxy '[Tagged "x" (), Tagged "y" Double])
+vy = mkVariant (Label :: Label "y") 2.4 (Proxy :: Proxy '[Tagged "x" (), Tagged "y" Double])
+
+vp1 :: Maybe (Variant '[Tagged "x" ()])
+vp1 =  projectVariant v
+
+
+vp1_ = fromJust vp1
+
+
+vp2 = extendsVariant vp1_ `asTypeOf` v
+vp3 = extendsVariant vp1_ `asLabelsOf` v
+
+vp4 = (v ^? projected) `asTypeOf` vp1
+
+vp5 = (projected # fromJust vp1) `asTypeOf` v
+
+
+vm1 = v & sameLength . sameLabels . projected %~ (\x -> x :: Variant '[Tagged "x" ()])
+
+vm2 = v & sameLength . sameLabels . projected . sameLabels %~ f2
+vm3 = vy & sameLength . sameLabels . projected . sameLabels %~ f2
+
+
+f2 (review hMaybied -> [pun| (x) |]) = hBuild (Just (show x)) ^?! hMaybied
+
+f3 :: Show a => Variant '[Tagged "x" a] -> Variant '[Tagged "x" String]
+f3 = unvarianted %~ show
+
+
+hm1 = Proxy :: HMemberM (Tagged "y" Double) '[Tagged "x" (), Tagged "y" Char] inY => Proxy inY
+
+v2 = fmap (`asLabelsOf` (Proxy :: Proxy '[Label "y"])) (projectVariant v)
+v_id = fmap (`asLabelsOf` v) (projectVariant v)
+v_id2 = fmap (`asLabelsOf` labelsOf v) (projectVariant v)
+
diff --git a/examples/TIPTransform.hs b/examples/TIPTransform.hs
--- a/examples/TIPTransform.hs
+++ b/examples/TIPTransform.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE DataKinds #-}
@@ -21,11 +23,15 @@
 module TIPTransform where
 
 import Data.HList
+import Data.Typeable
 
 -- We start with the examples
 
-newtype MyVal = MyVal Int deriving Show
+newtype MyVal = MyVal Int deriving (Show, Typeable)
 
+-- or if no typeable, use
+-- instance ShowLabel MyVal where showLabel _ = "MyVal"
+
 -- A sample TIP
 tip1 = MyVal 20 .*. (1::Int) .*. True .*. emptyTIP
 -- TIP (HCons (MyVal 20) (HCons 1 (HCons True HNil)))
@@ -54,27 +60,7 @@
 -- TIP (HCons (MyVal 21) (HCons 1 (HCons True HNil)))
 
 -- The implementation
-
-class TransTIP op db where
-    ttip :: op -> db -> db
-
-instance (HMember op db b, TransTIP' b op (TIP db)) 
-    => TransTIP op (TIP db) where
-    ttip = ttip' (Proxy ::Proxy b)
-
-class TransTIP' (b :: Bool) op db where
-    ttip' :: Proxy b -> op -> db -> db
-
--- If op is found in a TIP, update the TIP with op
-instance (HTypeIndexed db, HUpdateAtHNat n op db, HUpdateAtHNatR n op db ~ db, HType2HNat op db n)
-    => TransTIP' True op (TIP db) where
-    ttip' _ = tipyUpdate
-
--- If op is not found in a TIP, it must be a function. Look up
--- its argument in a TIP and recur.
-instance (HOccurs arg db, TransTIP op db) 
-    => TransTIP' False (arg -> op) db where
-    ttip' _ f db = ttip (f (hOccurs db)) db
+-- part of HList proper now
 
 
 main = mapM_ putStrLn [show tip1, show tip2, show tip3, show tip4,
diff --git a/examples/TIPTransform.out b/examples/TIPTransform.out
deleted file mode 100644
--- a/examples/TIPTransform.out
+++ /dev/null
@@ -1,6 +0,0 @@
-TIPH[MyVal 20, 1, True]
-TIPH[MyVal 20, 2, True]
-TIPH[MyVal 20, 1, False]
-TIPH[MyVal 20, 21, True]
-TIPH[MyVal 21, 1, True]
-TIPH[MyVal 21, 1, True]
diff --git a/examples/TIPTransform.ref b/examples/TIPTransform.ref
--- a/examples/TIPTransform.ref
+++ b/examples/TIPTransform.ref
@@ -1,6 +1,6 @@
-TIPH[MyVal 20, 1, True]
-TIPH[MyVal 20, 2, True]
-TIPH[MyVal 20, 1, False]
-TIPH[MyVal 20, 21, True]
-TIPH[MyVal 21, 1, True]
-TIPH[MyVal 21, 1, True]
+TIPH[MyVal 20,1,True]
+TIPH[MyVal 20,2,True]
+TIPH[MyVal 20,1,False]
+TIPH[MyVal 20,21,True]
+TIPH[MyVal 21,1,True]
+TIPH[MyVal 21,1,True]
diff --git a/examples/TIPTransformM.hs b/examples/TIPTransformM.hs
--- a/examples/TIPTransformM.hs
+++ b/examples/TIPTransformM.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE TypeOperators #-}
 {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
@@ -25,12 +26,13 @@
 
 module TIPTransformM where
 
-import Data.HList
+import Data.HList.CommonMain
+import Data.Typeable
 import Control.Monad.Identity
 
 -- We start with the examples
 
-newtype MyVal = MyVal Int deriving Show
+newtype MyVal = MyVal Int deriving (Show,Typeable)
 
 -- A specialized version of return for the Identity monad.
 -- It is needed only for the Simple version of the code,
@@ -75,7 +77,7 @@
 
 -- Andrew Frank's test
 -- tip6 :: IO (TIP (HCons MyVal (HCons Int (HCons Bool (HCons Float HNil)))))
-tip6 :: IO (TIP (MyVal ': Int ': Bool ': Float ': '[]))
+tip6 :: IO (TIP (TagR [MyVal,Int,Bool, Float]))
 tip6 = ttipM op6 tip1
 
 op6 :: MyVal -> Bool -> IO MyVal
@@ -115,38 +117,7 @@
 -} -- !Simple
 
 -- {- -- !TF
--- The TF implementation. When specifying the operation to perform over
--- a TIP, we can leave it polymorphic over the monad. The type checker
--- will instantiate the monad based on the context.
-
-class Monad m => TransTIPM m op db where
-    ttipM :: op -> db -> m db
-
--- Check to see if the operation is a computation whose result
--- is in the TIP. The type variable m' of the kind *->* below 
--- can be instantiated either to a monad type constructor, or (arg->).
-instance (Monad m, HMember op db b, TransTIPM' b m (m' op) (TIP db))
-    => TransTIPM m (m' op) (TIP db) where
-    ttipM = ttipM' (Proxy :: Proxy b)
-
-class Monad m => TransTIPM' (b :: Bool) m op db where
-    ttipM' :: Proxy b -> op -> db -> m db
-
--- If op is found in a TIP, update the TIP with op.
--- The type variable m' must be equal to the type of the monad
--- in which the final result is reported.
-instance (Monad m, m ~ m',
-	  HTypeIndexed db, HUpdateAtHNat n op db, HUpdateAtHNatR n op db ~ db, HType2HNat op db n)
-    => TransTIPM' True m (m' op) (TIP db) where
-    ttipM' _ op db = do
-                     op' <- op
-		     return $ tipyUpdate op' db
-
--- If op is not found in a TIP, it must be a function. Look up
--- its argument in a TIP and recur.
-instance (Monad m, HOccurs arg db, TransTIPM m op db)
-    => TransTIPM' False m (arg-> op) db where
-    ttipM' _ f db = ttipM (f (hOccurs db)) db
+-- Moved to TIP.hs
 -- -} -- !TF
 
 main :: IO ()
diff --git a/examples/TIPTransformM.out b/examples/TIPTransformM.out
deleted file mode 100644
--- a/examples/TIPTransformM.out
+++ /dev/null
@@ -1,8 +0,0 @@
-TIPH[MyVal 20, 1, True, 3.5]
-TIPH[MyVal 20, 2, True, 3.5]
-TIPH[MyVal 20, 1, False, 3.5]
-TIPH[MyVal 20, 21, True, 3.5]
-TIPH[MyVal 21, 1, True, 3.5]
-TIPH[MyVal 21, 1, True, 3.5]
-MyVal is now MyVal 5
-tip2 isTIPH[MyVal 5, 1, True, 3.5]
diff --git a/examples/TIPTransformM.ref b/examples/TIPTransformM.ref
--- a/examples/TIPTransformM.ref
+++ b/examples/TIPTransformM.ref
@@ -1,8 +1,8 @@
-TIPH[MyVal 20, 1, True, 3.5]
-TIPH[MyVal 20, 2, True, 3.5]
-TIPH[MyVal 20, 1, False, 3.5]
-TIPH[MyVal 20, 21, True, 3.5]
-TIPH[MyVal 21, 1, True, 3.5]
-TIPH[MyVal 21, 1, True, 3.5]
+TIPH[MyVal 20,1,True,3.5]
+TIPH[MyVal 20,2,True,3.5]
+TIPH[MyVal 20,1,False,3.5]
+TIPH[MyVal 20,21,True,3.5]
+TIPH[MyVal 21,1,True,3.5]
+TIPH[MyVal 21,1,True,3.5]
 MyVal is now MyVal 5
-tip2 isTIPH[MyVal 5, 1, True, 3.5]
+tip2 isTIPH[MyVal 5,1,True,3.5]
diff --git a/examples/broken/Joy.hs b/examples/broken/Joy.hs
new file mode 100644
--- /dev/null
+++ b/examples/broken/Joy.hs
@@ -0,0 +1,287 @@
+{-# OPTIONS -fglasgow-exts #-}
+{-# OPTIONS -fallow-overlapping-instances #-}
+{-# OPTIONS -fallow-undecidable-instances #-}
+
+--Joy implemented in Haskell... extensible embedded language...
+
+module Joy where
+
+import MainGhcGeneric1
+
+-- Building non-empty lists
+
+type HOne = HSucc HZero
+hOne :: HOne
+hOne = undefined
+type HTwo = HSucc HOne
+hTwo :: HTwo
+hTwo = undefined
+type HThree = HSucc HTwo
+hThree :: HThree
+hThree = undefined
+
+end :: HNil
+end = hNil
+
+instance HList s => Apply HNil s s where
+	apply _ s = s
+instance (HList s,HList s',HList l,Apply a s s',Apply l s' s'') => Apply (HCons a l) s s'' where
+	apply (HCons a l) s = apply l (apply a s :: s')
+instance HList s => Apply HZero s (HCons HZero s) where
+	apply _ s = hCons hZero s
+instance (HNat a,HList s) => Apply (HSucc a) s (HCons (HSucc a) s) where
+	apply a s = hCons a s
+
+data Lit a = Lit a
+lit :: a -> Lit a
+lit a = Lit a
+unl :: Lit a -> a
+unl (Lit a) = a
+instance Show a => Show (Lit a) where
+	showsPrec _ (Lit a) = showChar '[' . shows a . showChar ']'
+instance HList s => Apply (Lit a) s (HCons a s) where
+	apply (Lit a) s = hCons a s
+
+class (HBool b,HList s) => HIfte b t f s s' | b t f s -> s' where
+	hIfte :: b -> t -> f -> s -> s'
+instance (HList s,Apply t s s') => HIfte HTrue t f s s' where
+	hIfte _ t _ s = apply t s
+instance (HList s,Apply f s s') => HIfte HFalse t f s s' where
+	hIfte _ _ f s = apply f s
+
+data Ifte
+ifte :: Ifte
+ifte = undefined
+instance Show Ifte where
+	showsPrec _ _ = showString "If"
+instance (Apply b s r,HHead r b',HIfte b' t f s s')
+	=> Apply Ifte (f :*: t :*: b :*: s) s' where
+	apply _ (HCons f (HCons t (HCons b s))) = hIfte (hHead (apply b s :: r) :: b') t f s
+
+data Nul
+nul :: Nul
+nul = undefined
+instance Show Nul where
+	showsPrec _ _ = showString "Nul"
+instance HList s => Apply Nul (HCons HZero s) (HCons HTrue s) where
+	apply _ (HCons _ s) = hCons hTrue s
+instance HList s => Apply Nul (HCons (HSucc n) s) (HCons HFalse s) where
+	apply _ (HCons _ s) = hCons hFalse s
+
+data EQ
+eq :: EQ
+eq = undefined
+instance Show EQ where
+	showsPrec _ _ = showString "Eq"
+instance (HList s,TypeEq a b t) => Apply EQ (HCons a (HCons b s)) (HCons t s) where
+	apply _ (HCons a (HCons b s)) = hCons (typeEq a b) s
+
+data Dip
+dip :: Dip
+dip = undefined
+instance Show Dip where
+	showsPrec _ _ = showString "Dip"
+instance (HList s,HList s',Apply a s s') => Apply Dip (HCons a (HCons b s)) (HCons b s') where
+	apply _ (HCons a (HCons b s)) = hCons b (apply a s)
+
+data Dup 
+dup :: Dup
+dup = undefined
+instance Show Dup where
+	showsPrec _ _ = showString "Dup"
+instance HList s => Apply Dup (HCons a s) (HCons a (HCons a s)) where
+	apply _ s@(HCons a _) = hCons a s
+
+data Pop
+pop :: Pop
+pop = undefined
+instance Show Pop where
+	showsPrec _ _ = showString "Pop"
+instance HList s => Apply Pop (HCons a s) s where
+	apply _ (HCons _ s) = s
+
+data Swap
+swap :: Swap
+swap = undefined
+instance Show Swap where
+	showsPrec _ _ = showString "Swap"
+instance HList s => Apply Swap (HCons a (HCons b s)) (HCons b (HCons a s)) where
+	apply _ (HCons a (HCons b s)) = hCons b (hCons a s)
+
+data Suc
+suc :: Suc
+suc = undefined
+instance Show Suc where
+	showsPrec _ _ = showString "Suc"
+instance (HNat a,HList s) => Apply Suc (HCons a s) (HCons (HSucc a) s) where
+	apply _ (HCons _ s) = hCons (undefined::HSucc a) s
+
+data Pre
+pre :: Pre
+pre = undefined
+instance Show Pre where
+	showsPrec _ _ = showString "Pre"
+instance (HNat a,HList s) => Apply Pre (HCons (HSucc a) s) (HCons a s) where
+	apply _ (HCons _ s) = hCons (undefined::a) s
+
+data Add
+add :: Add
+add = undefined
+instance Show Add where
+	showsPrec _ _ = showString "Add"
+instance (HList s,HAdd a b c) => Apply Add (HCons a (HCons b s)) (HCons c s) where
+	apply _ (HCons _ (HCons _ s)) = hCons (hAdd (undefined::a) (undefined::b)) s
+
+class (HNat a,HNat b) => HAdd a b c | a b -> c where
+	hAdd :: a -> b -> c
+instance HAdd HZero HZero HZero where
+	hAdd _ _ = hZero
+instance HNat b => HAdd HZero (HSucc b) (HSucc b) where
+	hAdd _ b = b
+instance HNat a => HAdd (HSucc a) HZero (HSucc a) where
+	hAdd a _ = a
+instance (HNat (HSucc a),HNat (HSucc b),HNat c,HAdd a b c)
+	=> HAdd (HSucc a) (HSucc b) (HSucc (HSucc c)) where
+	hAdd _ _ = hSucc $ hSucc $ hAdd (undefined::a) (undefined::b)
+
+data Sub
+sub :: Sub
+sub = undefined
+instance Show Sub where
+	showsPrec _ _ = showString "Sub"
+instance (HList s,HSub a b c) => Apply Sub (HCons b (HCons a s)) (HCons c s) where
+	apply _ (HCons _ (HCons _ s)) = hCons (hSub (undefined::a) (undefined::b)) s
+
+class (HNat a,HNat b) => HSub a b c | a b -> c where
+	hSub :: a -> b -> c
+instance HSub HZero HZero HZero where
+	hSub _ _ = hZero
+instance HNat a => HSub (HSucc a) HZero (HSucc a) where
+	hSub a _ = a
+instance HNat a => HSub HZero (HSucc a) HZero where
+	hSub _ _ = hZero
+instance (HSub a b c) => HSub (HSucc a) (HSucc b) c where
+	hSub _ _ = hSub (undefined::a) (undefined::b)
+	
+data Mult
+mult :: Mult
+mult = undefined
+instance Show Mult where
+	showsPrec _ _ = showString "Mult"
+instance (HList s,HMult a b c) => Apply Mult (HCons a (HCons b s)) (HCons c s) where
+	apply _ (HCons _ (HCons _ s)) = hCons (hMult (undefined::a) (undefined::b)) s
+
+class (HNat a,HNat b) => HMult a b c | a b -> c where
+	hMult :: a -> b -> c
+instance HNat b => HMult HZero b HZero where
+	hMult _ _ = hZero
+instance (HMult a b s,HAdd b s s') => HMult (HSucc a) b s' where
+	hMult _ _ = hAdd (undefined::b) (hMult (undefined::a) (undefined::b) :: s)
+
+square = dup .*. mult .*. hNil
+cube = mult .*. mult .*. dup .*. dup .*. hNil
+
+data I
+i :: I
+i = undefined
+instance Show I where
+	showsPrec _ _ = showString "I"
+instance Apply I HNil HNil where
+	apply _ _ = hNil
+instance (HList s,Apply a s s') => Apply I (HCons a s) s' where
+	apply _ (HCons a s) = apply a s
+
+data Primrec = Primrec deriving Show
+primrec :: Primrec
+primrec = undefined
+instance Apply z s s' => Apply Primrec (HCons nz (HCons z (HCons HZero s))) s' where
+	apply _ (HCons _ (HCons z (HCons _ s))) = apply z s
+instance (HList s,Apply Primrec (HCons nz (HCons z (HCons n (HCons (HSucc n) s)))) s',Apply nz s' s'')
+	=> Apply Primrec (HCons nz (HCons z (HCons (HSucc n) s))) s'' where
+	apply _ (HCons nz (HCons z s@(HCons _ _))) = apply nz (apply Primrec (hCons nz (hCons z (hCons (undefined::n) s))))
+
+data Times
+times :: Times
+times = undefined
+instance Show Times where
+	showsPrec _ _ = showString "Times"
+instance HList s => Apply Times (HCons p (HCons HZero s)) s where
+	apply _ (HCons _ (HCons _ s)) = s
+instance (HNat n,HList s,HList s',Apply p s s',Apply Times (HCons p (HCons n s')) s'')
+	=> Apply Times (HCons p (HCons (HSucc n) s)) s'' where
+	apply _ (HCons p (HCons _ s)) = apply times (hCons p (hCons (undefined::n) (apply p s)))
+
+class (HBool f,HList s) => HGenrec f r1 r2 b t s s'' | f r1 r2 b t s -> s'' where
+	hGenrec :: f -> r1 -> r2 -> b -> t -> s -> s''
+instance (HList s,Apply t s s') => HGenrec HTrue r1 r2 b t s s' where
+	hGenrec _ _ _ _ t s = apply t s
+instance (HList s,HList s',Apply r1 s s',
+	Apply (HCons (Lit (HCons (Lit b) (HCons (Lit t) (HCons (Lit r1) (HCons (Lit r2) (HCons Genrec HNil)))))) (HCons r2 HNil)) s' s'')
+	=> HGenrec HFalse r1 r2 b t s s'' where
+	hGenrec _ r1 r2 b t s = apply (hCons (lit (hCons (lit b) (hCons (lit t) (hCons (lit r1) (hCons (lit r2) (hCons genrec hNil)))))) (hCons r2 hNil)) (apply r1 s :: s') 
+
+data Genrec
+genrec :: Genrec
+genrec = undefined
+instance Show Genrec where
+	showsPrec _ _ = showString "Genrec"
+instance (Apply b s s',HHead s' b',HGenrec b' r1 r2 b t s s'')
+	=> Apply Genrec (HCons r2 (HCons r1 (HCons t (HCons b s)))) s'' where
+	apply _ (HCons r2 (HCons r1 (HCons t (HCons b s))))
+		= hGenrec (hHead (apply b s :: s') :: b') r1 r2 b t s
+
+class (HBool f,HList s) => HLinrec f b t r1 r2 s s' | f b t r1 r2 s -> s' where
+	hLinrec :: f -> b -> t -> r1 -> r2 -> s -> s'
+instance (HList s,Apply t s s') => HLinrec HTrue b t r1 r2 s s' where
+	hLinrec _ _ t _ _ s = apply t s
+instance (HList s,HList s',Apply r1 s s',
+	Apply Linrec (HCons r2 (HCons r1 (HCons t (HCons b s')))) s'',Apply r2 s'' s''')
+	=> HLinrec HFalse b t r1 r2 s s''' where
+	hLinrec _ b t r1 r2 s = apply r2 (apply linrec (hCons r2 (hCons r1 (hCons t (hCons b (apply r1 s :: s'))))) :: s'')
+
+data Linrec
+linrec :: Linrec
+linrec = undefined
+instance Show Linrec where
+	showsPrec _ _ = showString "Linrec"
+instance (Apply b s s',HHead s' b',HLinrec b' b t r1 r2 s s'') => Apply Linrec (HCons r2 (HCons r1 (HCons t (HCons b s)))) s'' where
+	apply _ (HCons r2 (HCons r1 (HCons t (HCons b s)))) = hLinrec (hHead (apply b s :: s') :: b') b t r1 r2 s
+
+data Fact
+fact :: Fact
+fact = undefined
+instance Show Fact where
+	showsPrec _ _ = showString "Fact"
+instance (HList s,Apply (HCons (Lit (HCons (Lit HZero) (HCons EQ HNil)))
+	(HCons (Lit (HCons Pop (HCons (Lit HOne) HNil)))
+	(HCons (Lit (HCons Dup
+	(HCons (Lit HOne)
+	(HCons Sub (HCons Fact (HCons Mult HNil))))))
+	(HCons Ifte HNil)))) s s') => Apply Fact s s' where
+	apply _ s = apply fac1 s
+
+fac1 = hCons (lit (hCons (lit hZero) (hCons eq hNil)))
+	(hCons (lit (hCons pop (hCons (lit hOne) hNil)))
+	(hCons (lit (hCons dup (hCons (lit hOne) (hCons sub (hCons fact (hCons mult hNil))))))
+	(hCons ifte hNil)))
+
+fac2 = lit (hOne .*. hOne .*. end)
+	.*. dip .*. lit (dup .*. lit mult .*. dip .*. suc .*. end)
+	.*. times .*. pop .*. end
+
+fac3 = lit nul .*. lit suc .*. lit (dup .*. pre .*. end)
+	.*. lit (i .*. mult .*. end) .*. genrec .*. end
+
+fac4 = lit nul .*. lit suc .*. lit (dup .*. pre .*. end)
+	.*. lit mult .*. linrec .*. end
+
+fac5 = lit hOne .*. lit mult .*. primrec .*. end
+
+main :: IO ()
+main = do
+	putStrLn $ show $ apply (lit hThree .*. fac1 .*. end) end
+	putStrLn $ show $ apply i (fac2 .*. hThree .*. end)
+	putStrLn $ show $ apply i (fac3 .*. hThree .*. end)
+	putStrLn $ show $ apply i (fac4 .*. hThree .*. end)
+	putStrLn $ show $ apply i (fac5 .*. hThree .*. end)
+
diff --git a/examples/broken/nn.hs b/examples/broken/nn.hs
new file mode 100644
--- /dev/null
+++ b/examples/broken/nn.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# OPTIONS_GHC -fcontext-stack=100 #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE DataKinds #-}
+
+import Data.HList.CommonMain
+import Control.Lens
+
+
+makeLabelable (unwords [ "r" ++ show n | n <- [0 .. 9 :: Int] ])
+r :: RecordU '[Tagged "r0" Int ,
+              Tagged "r1" Int,
+              Tagged "r2" Int,
+              Tagged "r3" Int,
+              Tagged "r4" Int,
+              Tagged "r5" Int,
+              Tagged "r6" Int,
+              Tagged "r7" Int,
+              Tagged "r8" Int,
+              Tagged "r9" Int]
+r = [1 .. 10] ^?! listAsHList . from unlabeled . unboxed
+
+
+
+main = print $
+  r^.r0 +
+  r^.r1 +
+  r^.r2 +
+  r^.r3 +
+  r^.r4 +
+  r^.r5 +
+  r^.r6 +
+  r^.r7 +
+  r^.r8 +
+  r^.r9
diff --git a/examples/cmdargs.hs b/examples/cmdargs.hs
--- a/examples/cmdargs.hs
+++ b/examples/cmdargs.hs
@@ -1,44 +1,96 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE PolyKinds #-}
 {-# LANGUAGE NoMonomorphismRestriction #-}
 {-# LANGUAGE TemplateHaskell #-}
 module Main where
 import System.Console.CmdArgs
 import Data.HList.CommonMain
 import Data.Generics
-
+import Control.Lens
+import System.Environment
+import GHC.TypeLits (Symbol)
+import Data.HList.Labelable(LabeledTo,ToSym)
 
 {-
 
-An example showing off the data instance for Record
+An example showing off the data instance for Record / Variant / TIP / TIC
 
 Also a use of cmdArgs
 
-NOTE: the data instances do not work with ghc-7.8 because of
-<http://ghc.haskell.org/trac/ghc/ticket/8486>
-
+Note that ghc-7.8.2 does not have (or can produce) instances of typeable
+for types of kind Symbol (ie. promoted strings):
+<https://ghc.haskell.org/trac/ghc/ticket/9111>, so for now use the Label3
+style
 
 -}
 
-makeLabels6 (words "x y z")
+#define USE_LABEL3 __GLASGOW_HASKELL__ == 708
 
+#if USE_LABEL3
+makeLabels3 "examples_cmdargs" (words "x y z tic")
+makeLabels3 "optV" (words "optA optB optC")
+#else
+-- works for ghc-7.6
+makeLabels6 (words "x y z tic")
+makeLabels6 (words "optA optB optC")
+#endif
 
+makeLabelable "abc df"
+
+#if USE_LABEL3
+-- XXX remove extra Label?
+v = (optA .*. optB .*. optC .*. emptyProxy)
+      `zipTagged` (Proxy :: Proxy '[Int,Char,Double])
+#else
+v = Proxy :: Proxy '[Tagged "optA" Int, Tagged "optB" Char, Tagged "optC" Double]
+#endif
+
+type Z' = TagR [Int, Char, Double]
+
 d0 = x .=. (5 :: Int)
     .*. y .=. True
-    .*. z .=. False
+    .*. z .=. mkVariant optC (1 :: Double) v
+    .*. tic .=. mkTIC' 'x' (Proxy :: Proxy Z')
     .*. emptyRecord
 
+-- the equivalent ordinary record for reference
+data E = E { a :: Int, b :: Bool }
+    deriving (Show, Data, Typeable)
 
-data E = E { a :: Int, b, c :: Bool }
+data Opt = OptA Int | OptB Char | OptC Double
     deriving (Show, Data, Typeable)
-e0 = E 5 True False
 
+e0 = E 5 True
+
 main = do
     print d0
+    print $ gmapT (mkT ((+1) :: Double -> Double)) (mkVariant optC 1 v)
+    print $  (mkVariant optC 1 v)
+
     print $ gmapT (mkT not) d0
     print $ gmapT (mkT (+(1::Int))) d0
 
-    print $ fromConstrB (undefined `extB` (1::Int) `extB` True) undefined `asTypeOf` d0
+    let theB :: Typeable a => a
+        theB = error "theB"
+              `extB` (1::Int)
+              `extB` True
+              `extB` (2.5::Double)
+              `extB` 'b'
+              `extB` mkVariant optC theB v
+              `extB` mkTIC' (theB :: Char) (Proxy :: Proxy Z')
 
-    rc <- cmdArgs d0
-    print rc
+    print $ fromConstrB theB undefined `asTypeOf` d0
 
+    putStrLn "Cmdargs"
+    print =<< withArgs ["-a=4", "-b=False" ] (cmdArgs e0)
+
+    -- drop the tic and z fields (which cmdargs doesn't handle)
+    let dRec = d0 & from hListRecord %~ (hInit . hInit)
+    print =<< withArgs ["-x=4", "-y=False"] (cmdArgs dRec)
diff --git a/examples/cmdargs.out b/examples/cmdargs.out
deleted file mode 100644
--- a/examples/cmdargs.out
+++ /dev/null
@@ -1,5 +0,0 @@
-Record{x=5,y=True,z=False}
-Record{x=5,y=False,z=True}
-Record{x=6,y=True,z=False}
-Record{x=1,y=True,z=True}
-Record{x=5,y=True,z=False}
diff --git a/examples/cmdargs.ref b/examples/cmdargs.ref
--- a/examples/cmdargs.ref
+++ b/examples/cmdargs.ref
@@ -1,5 +1,9 @@
-Record{x=5,y=True,z=False}
-Record{x=5,y=False,z=True}
-Record{x=6,y=True,z=False}
-Record{x=1,y=True,z=True}
-Record{x=5,y=True,z=False}
+Record{x=5,y=True,z=V{optC=1.0},tic=TIC{char='x'}}
+V{optC=2.0}
+V{optC=1.0}
+Record{x=5,y=False,z=V{optC=1.0},tic=TIC{char='x'}}
+Record{x=6,y=True,z=V{optC=1.0},tic=TIC{char='x'}}
+Record{x=1,y=True,z=V{optC=2.5},tic=TIC{char='b'}}
+Cmdargs
+E {a = 4, b = False}
+Record{x=4,y=False}
diff --git a/examples/labelable.hs b/examples/labelable.hs
--- a/examples/labelable.hs
+++ b/examples/labelable.hs
@@ -1,4 +1,6 @@
-{-# LANGUAGE FlexibleContexts, TemplateHaskell, DataKinds, PolyKinds #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE FlexibleContexts, TemplateHaskell, DataKinds, PolyKinds,
+  GADTs, ConstraintKinds #-}
 {- | Demonstrates @hLens'@
 
 may be worthwhile to have a lens-free test suite, doing stuff like:
@@ -10,8 +12,16 @@
 import Data.HList.CommonMain
 import Control.Lens
 
+import Text.Read
+
 makeLabelable "x y"
 
+#if __GLASGOW_HASKELL__ < 707
+#define INT_SIG_76 :: Int
+#else
+#define INT_SIG_76
+#endif
+
 r = x .==. "hi" .*.
     y .==. (y .==. 321 .*. x .==. 123 .*. emptyRecord) .*.
     emptyRecord
@@ -20,5 +30,18 @@
     print (r ^. x)
     print (r & x .~ ())
 
-    print (r ^. y . y)
+    -- ghc-7.6 doesn't default when r is involved lower down,
+    -- while 7.8.2 does
+    print (r ^. y . y  INT_SIG_76)
+    print (r ^. y . x  INT_SIG_76)
+
     print (r & y . y .~ "xy")
+
+    putStrLn "\nread-show"
+    print (readMaybe (show r) `asTypeOf` Just r)
+    print (readMaybe "Record{x=\"hi\",y=Record{y=321,x=123}}" `asTypeOf` Just r)
+
+    -- there is no permuting of labels
+    print (readMaybe "Record{y=Record{y=321,x=123},x=\"hi\"}" `asTypeOf` Just r)
+
+    print $ (r ^. rearranged) `asTypeOf` (undefined :: Record '[Tagged "y" t, Tagged "x" s])
diff --git a/examples/labelable.out b/examples/labelable.out
deleted file mode 100644
--- a/examples/labelable.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"hi"
-Record{x=(),y=Record{y=321,x=123}}
-321
-Record{x="hi",y=Record{y="xy",x=123}}
diff --git a/examples/labelable.ref b/examples/labelable.ref
--- a/examples/labelable.ref
+++ b/examples/labelable.ref
@@ -1,4 +1,11 @@
 "hi"
 Record{x=(),y=Record{y=321,x=123}}
 321
+123
 Record{x="hi",y=Record{y="xy",x=123}}
+
+read-show
+Just Record{x="hi",y=Record{y=321,x=123}}
+Just Record{x="hi",y=Record{y=321,x=123}}
+Nothing
+Record{y=Record{y=321,x=123},x="hi"}
diff --git a/examples/lens.hs b/examples/lens.hs
--- a/examples/lens.hs
+++ b/examples/lens.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE TemplateHaskell, DataKinds, PolyKinds #-}
 {- | Demonstrates @hLens@. See also labelable.hs which is more "convenient"
 
@@ -6,12 +8,26 @@
 import Data.HList.CommonMain
 import Control.Lens
 
-makeLabels6 (words "x y")
+makeLabels6 (words "x y z")
 
+
+#if __GLASGOW_HASKELL__ > 707
+yRec = y .=. 321 .*. x .=. 123 .*. emptyRecord
+#else
+-- defaulting doesn't work in ghc-7.6.3
+yRec = y .=. (321 :: Integer) .*. x .=. (123 :: Integer) .*. emptyRecord
+#endif
+
 r = x .=. "hi" .*.
-    y .=. (y .=. 321 .*. x .=. 123 .*. emptyRecord) .*.
+    y .=. yRec .*.
     emptyRecord
 
+
+_ = (r^.y') `asTypeOf` hRearrange'
+    (x .=. 1 .*. y .=. 1 .*. emptyRecord)
+
+rSmall = x .=. "" .*. emptyRecord
+
 x' a = hLens x a
 y' a = hLens y a
 
@@ -32,3 +48,15 @@
     print (r ^. y' . y')
     print (r & y' . y' .~ "xy")
 
+    putStrLn "\n\nIsos"
+    print (r & sameLength . unlabeled . hTuple . _1 .~ ())
+    print (r & sameLength . unlabeled . hTuple . _2 .~ ())
+    print (z .=. () .*. r
+              & unlabeled' . from tipHList %~ ttip (\x z -> x ++ show (z :: ())))
+
+    r ^. unlabeled . from tipHList & tipPutStrLn
+
+
+tipPutStrLn tip = ttipM ?? tip $ \x -> do
+  putStrLn x
+  return x
diff --git a/examples/lens.out b/examples/lens.out
deleted file mode 100644
--- a/examples/lens.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"hi"
-Record{x=(),y=Record{y=321,x=123}}
-123
-Record{x="hi",y=Record{y="xy",x=123}}
-
-
-and repeat:
-"hi"
-Record{x=(),y=Record{y=321,x=123}}
-321
-Record{x="hi",y=Record{y="xy",x=123}}
diff --git a/examples/lens.ref b/examples/lens.ref
--- a/examples/lens.ref
+++ b/examples/lens.ref
@@ -9,3 +9,11 @@
 Record{x=(),y=Record{y=321,x=123}}
 321
 Record{x="hi",y=Record{y="xy",x=123}}
+
+
+Isos
+Record{x=(),y=Record{y=321,x=123}}
+Record{x="hi",y=()}
+Record{z=(),x="hi()",y=Record{y=321,x=123}}
+hi
+TIPH["hi",Record{y=321,x=123}]
diff --git a/examples/prism.hs b/examples/prism.hs
new file mode 100644
--- /dev/null
+++ b/examples/prism.hs
@@ -0,0 +1,120 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+{-# LANGUAGE QuasiQuotes #-} -- for pun
+{-# LANGUAGE TemplateHaskell #-}
+module Main where
+import Data.HList.CommonMain
+import Data.HList.Labelable (hLens')
+import Control.Lens
+
+-- generate left = Label :: Label "left"
+makeLabels6 (words "left right up down")
+
+--- define the Labelable labels manually
+left_ = hLens' left
+right_ = hLens' right
+up_ = hLens' up
+down_ = hLens' down
+
+-- this definition is needed to decide what order
+-- to put the fields in, as well as their initial types
+r = [pun|right left up|] where
+  left = 'a'
+  right = 2 :: Int
+  up = 2.3 :: Double
+
+v = mkVariant left 'x' r
+
+main = do
+    putStrLn "Lookup and set"
+    inspectV
+
+    putStrLn "\nrepeat with Labelable"
+    inspectV_
+
+    putStrLn "\nSetting the missing tag does nothing:"
+    print $ set right_ () v
+    Left 'x' <- set _Right () (Left 'x') -- prisms for Either do the same thing
+                & return
+
+    putStrLn "\nLenses compose:"
+    Nothing  <- v3 ^? up_.up_ & return
+    Just 'x' <- v3 ^? left_ & return
+    v4 ^? left_.left_ & print
+
+    putStrLn "\nLenses+prisms compose:"
+    inspectRV
+
+    putStrLn "\n\"extension\""
+    extensionTests
+
+    putStrLn "\nInternal Structure:"
+    putStrLn $ indent $ show vs
+
+
+
+lazyX = mkVariant (Label :: Label "x") 'a' lazyProto
+lazyY = mkVariant (Label :: Label "y") (2.5 :: Double) lazyProto
+
+lazyProto = undefined :: Record
+  '[Tagged "x" x, Tagged "y" y]
+
+vs = [pun| v v2 v2' v3 v4 v5 v6 v7 |]
+
+inspectV = do
+    v ^? hPrism left & print
+    v ^? hPrism right & print
+    v ^? hPrism up & print
+    v2 ^? hPrism left & print
+
+-- note that we can change the type of the 'x' field
+-- from Char to ()
+v2 = set (hPrism left) () v
+
+inspectV_ = do
+    v ^? left_ & print
+    v ^? right_ & print
+    v ^? up_ & print
+    v2' ^? left_ & print
+
+
+inspectRV = do
+    r2 ^? down_.left_ & print
+    r2 ^? down_.right_ & print
+    r2 ^? du & print
+
+du = down_.up_
+
+r2 = down_ .==. v .*. r
+
+
+-- or with the "better" label
+v2' = set left_ () v
+
+
+v3 = v & up_ .~ v & up_.up_ .~ "upup"
+v4 = v & left_ .~ v & left_.left_ .~ "leftleft"
+
+
+extensionTests = do
+    Just "hi" <- v5 ^? down_ & return
+    Just "hi" <- v6 ^? down_ & return
+    Nothing   <- v7 ^? down_ & return
+    Just 'x'  <- v7 ^? left_ & return
+    return ()
+
+
+v5 = down .=. Just "hi" .*. v
+v6 = down_ .==. Just "hi" .*. v
+v7 = down .=. (Nothing :: Maybe String) .*. v
+
+-- start a newline after every } to make the results readable
+indent :: String -> String
+indent xs = indent' 0 xs
+
+indent' n xs | n < 0 = indent' 0 xs
+indent' n ('}' : xs) = "}\n" ++ replicate n ' ' ++ indent' (n-4) xs
+indent' n ('{' : xs) = '{' : indent' (n+4) xs
+indent' n (x : xs) = x : indent' n xs
+indent' _ [] = []
diff --git a/examples/prism.ref b/examples/prism.ref
new file mode 100644
--- /dev/null
+++ b/examples/prism.ref
@@ -0,0 +1,37 @@
+Lookup and set
+Just 'x'
+Nothing
+Nothing
+Just ()
+
+repeat with Labelable
+Just 'x'
+Nothing
+Nothing
+Just ()
+
+Setting the missing tag does nothing:
+V{left='x'}
+
+Lenses compose:
+Just "leftleft"
+
+Lenses+prisms compose:
+Just 'x'
+Nothing
+Nothing
+
+"extension"
+
+Internal Structure:
+Record{v=V{left='x'}
+        ,v2=V{left=()}
+        ,v2'=V{left=()}
+        ,v3=V{left='x'}
+        ,v4=V{left=V{left="leftleft"}
+            }
+        ,v5=V{down="hi"}
+        ,v6=V{down="hi"}
+        ,v7=V{left='x'}
+        }
+    
diff --git a/examples/pun.hs b/examples/pun.hs
--- a/examples/pun.hs
+++ b/examples/pun.hs
@@ -1,3 +1,6 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE NoMonomorphismRestriction #-}
 {-# LANGUAGE QuasiQuotes #-}
 {-# LANGUAGE TemplateHaskell #-}
diff --git a/examples/pun.out b/examples/pun.out
deleted file mode 100644
--- a/examples/pun.out
+++ /dev/null
@@ -1,9 +0,0 @@
-similar:
-(3,Record{a=3})
-(3,Record{a=3})
-3
-
-expression QQ:
-Record{r=Record{b=Record{a=3},c="c",x=1,y="hi"}}
-Record{r=Record{b=Record{a=3},c=1,y="hi"}}
-Record{r=Record{x=1,y="hi"}}
diff --git a/examples/pun.ref b/examples/pun.ref
--- a/examples/pun.ref
+++ b/examples/pun.ref
@@ -4,6 +4,6 @@
 3
 
 expression QQ:
-Record{r=Record{b=Record{a=3},c="c",x=1,y="hi"}}
-Record{r=Record{b=Record{a=3},c=1,y="hi"}}
+Record{r=Record{x=1,y="hi",c="c",b=Record{a=3}}}
+Record{r=Record{c=1,y="hi",b=Record{a=3}}}
 Record{r=Record{x=1,y="hi"}}
diff --git a/examples/rundoctests.hs b/examples/rundoctests.hs
--- a/examples/rundoctests.hs
+++ b/examples/rundoctests.hs
@@ -1,10 +1,12 @@
+import Data.List
 import System.Process
 import System.Exit
 import System.IO
-import Test.DocTest
 import Data.Char
 import System.Environment
 
+import Cabal
+
 -- | tests that all the >>> comments are followed by correct output. Easiest is to
 --
 -- > cabal test
@@ -20,21 +22,26 @@
 -- you need Cabal >= 1.18 since that's around when cabal repl got added.
 main = do
     as <- getArgs
-    o <- readProcess
-        "cabal" ["repl","--ghc-options","-v0 -w"]
+    (ExitSuccess, o, _) <- cabal
+        ["repl","--ghc-options","-v0 -w"]
         ":show packages\n:show language"
-    let flags = words $ unlines $ filter ((=="-") . take 1 . dropWhile isSpace)
+    let flags = words $ unlines
+                    $ filter (\f -> not $ "package" `isInfixOf` f)
+                    $ filter ((=="-") . take 1 . dropWhile isSpace)
                     $ lines o
 
     let files = case as of
             [] -> ["Data/HList/CommonMain.hs",
                    "Data/HList/HList.hs",
                    "Data/HList/Record.hs",
-                   "Data/HList/Labelable.hs"]
+                   "Data/HList/Labelable.hs",
+                   "Data/HList/HSort.hs"]
             _ -> as
 
-    doctest $ "-i.": "-idist/build/autogen":
+    let args = "-i.": "-idist/build/autogen":
              "-optP-include":
              "-optPdist/build/autogen/cabal_macros.h" :
              "-Idist/build/autogen" : "-w":
              files ++ flags
+    readProcess "doctest" args ""
+
diff --git a/examples/runexamples.hs b/examples/runexamples.hs
--- a/examples/runexamples.hs
+++ b/examples/runexamples.hs
@@ -1,10 +1,11 @@
 module Main where
 
+import Cabal
 import Control.Exception
 import System.FilePath
 import Test.Hspec
-import System.Process
 import System.Exit
+import System.Process
 import System.Directory
 import Data.Maybe
 import Control.Monad
@@ -28,17 +29,24 @@
     let ex = ("examples" </>)
     let inFile = ex (takeBaseName f)
         outFile = dropExtension inFile ++ ".out"
+        errFile = dropExtension inFile ++ ".err"
         refFile = dropExtension inFile ++ ".ref"
 
-    (ec, stdout, stderr) <- readProcessWithExitCode "cabal" ["repl","examples",
-        "-v0", "--ghc-options", "-w -fcontext-stack=50 -iexamples -v0"]
-        (":set -i\n:set -iexamples\n:load " ++ inFile ++ "\nmain")
+    (ec, stdout, stderr) <- cabal
+            ["repl","examples",
+              "-v0", "--ghc-options", "-w -fcontext-stack=50 -iexamples -v0"]
+              (":set -i\n:set -iexamples\n:load " ++ inFile ++ "\nmain")
 
     writeFile outFile stdout
 
     ofe <- doesFileExist refFile
-    diff <- if ofe then fmap Just $
-        readProcess "diff" ["-b", outFile, refFile] "" else return Nothing
+    diff <- if ofe
+      then fmap Just $
+        readProcess "diff" ["-b", outFile, refFile] ""
+          `finally` writeFile errFile stderr
+      else return Nothing
+
+    unless (diff == Just "") $ writeFile errFile stderr
 
     return (ec, stderr, diff)
  `shouldReturn` (ExitSuccess, "", Just "")
