diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,6 @@
+## 1.0.2
+
+- Migrated documentation from the old independent user manual into Haddocks
+- Fixed the benchmark
+- Added GHC 9.12.4 to CI (9.14.1 is still failing due to dependencies not being
+  available)
diff --git a/src/bench/Main.hs b/src/bench/Main.hs
--- a/src/bench/Main.hs
+++ b/src/bench/Main.hs
@@ -56,7 +56,7 @@
 newtype Value a = Value a   deriving newtype (NFData)
 
 newtype VariantNode a
-   = VariantNode (V '[Value a, Plus (VariantNode a), Minus (VariantNode a)])
+   = VariantNode (V [Value a, Plus (VariantNode a), Minus (VariantNode a)])
 
 deriving newtype instance (NFData a) => NFData (VariantNode a)
 
@@ -83,10 +83,10 @@
    let
       evalEnv n = do
          tree1 <- generate (resize n (arbitrary :: Gen (Node Int)))
-         let tree2 = nodeToVariantNode tree1
+         let !tree2 = force (nodeToVariantNode tree1)
          return  (n,tree1,tree2)
 
-      evalTest (n,tree1,tree2) = bgroup ("Tree Eval at size=" ++ show n)
+      evalTest ~(n,tree1,tree2) = bgroup ("Tree Eval at size=" ++ show n)
          [ bench "ADT"                      $ whnf evalNode tree1
          , bench "Variant ADT - V"          $ whnf evalVariantNode tree2
          , bench "Variant ADT - Safe match" $ whnf evalVariantNodeSafe tree2
diff --git a/src/lib/Data/Variant.hs b/src/lib/Data/Variant.hs
--- a/src/lib/Data/Variant.hs
+++ b/src/lib/Data/Variant.hs
@@ -17,7 +17,498 @@
 {-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE ViewPatterns #-}
 
--- | Open sum type
+{- | Open sum type
+
+'V' (for Variant) is a sum type, i.e. a wrapper for a value which can be of
+different types. For instance in the following code @x@ is a variant whose value
+can be an @Int@, a @Float@ or a @String@:
+
+> import Data.Variant
+>
+> x :: V [Int,Float,String]
+
+We use a type-level list of types to statically constrain the possible value
+types. Compared to usual sum types (e.g. @Either Int Float@) it allows us to
+have variants which can contain any number of types and to manipulate
+(extend\/filter\/etc.) the list in a type-safe way and without requiring new data
+types.
+
+__See also__
+
+* "Data.Variant.VEither" is a variant biased towards the first type in the
+  list, just like @Either a b@ is biased towards the second type (@b@), allowing
+  instances such as @instance Functor (VEither a)@ which we do not have for 'V'.
+
+* "Data.Variant.Excepts" is a multi-exception monad transformer wrapping
+  'Data.Variant.VEither.VEither'.
+
+* "Data.Variant.EADT" supports recursive sum types based on Variant (Extensible
+  ADTs).
+
+== Why Variant?
+
+In the functional programming world we use algebraic data types (ADT), more
+specifically sum types, to indicate that a value can be of two or more different
+types:
+
+> x,y :: Either String Int
+> x = Left "yo"
+> y = Right 10
+
+What if we want to support more than two types?
+
+__Solution 1: sum types__
+
+We could use different sum types with different constructors for each arity
+(number of different types that the value can have).
+
+> data SumOf3 a b c   = S3_0 a | S3_1 b | S3_2 c
+> data SumOf4 a b c d = S4_0 a | S4_1 b | S4_2 c | S4_3 d
+
+But it is quite hard to work with that many different types and constructors as
+we cannot easily define generic functions working on different sum types without
+a combinatorial explosion.
+
+__Solution 2: recursive ADT__
+
+Instead of adding new sum types we can use a nest of @Either@:
+
+> type SumOf3 a b c   = Either a (Either b c)
+> type SumOf4 a b c d = Either a (Either b (Either c d))
+
+Or more generically:
+
+> data Union (as :: [Type]) where
+>   Union :: Either (Union as) a -> Union (a : as)
+
+This time we can define generic functions without risking a combinatorial
+explosion. The drawback however is that we have changed the representation:
+instead of @tag + value@ where @tag@ is in the range [0,arity-1] we have a
+nest of @tag + (tag + (... (tag + value)))@ where @tag@ is in the range
+[0,1]. It is both inefficient in space and in time (accessing the tag value is
+in O(arity)).
+
+__Solution 3: variant__
+
+'V' gets the best of both approaches: it has the generic interface of
+the \"recursive ADT\" solution and the efficient representation of the \"sum types\"
+solution.
+
+> data Variant (types :: [Type]) = Variant {-# UNPACK #-} !Word Any
+>
+> type role Variant representational
+
+The efficient representation is ensured by the definition of the 'V'
+datatype: an unpacked @Word@ for the tag and a \"pointer\" to the value.
+
+The phantom type list @types@ contains the list of possible types for the value.
+The tag value is used as an index into this list to know the effective type of the
+value.
+
+== Creating Variant values
+
+The easiest way to create a variant value is to use the 'V' pattern synonym:
+
+> x,y :: V [String,Int]
+> x = V "test"
+> y = V @Int 10
+
+Note: for now the compiler cannot use the variant value type list to infer the
+type of the variant value! In the previous example we have to specify the @Int@
+type. Even if it is clear (for us) that it is the obvious unique possibility, it
+is ambiguous for the compiler.
+
+We can also explicitly create a variant by specifying the index (starting from
+0) of the value type with 'toVariantAt':
+
+> x :: V [Int,String,Float]
+> x = toVariantAt @2 5.0
+
+It is especially useful if for some reason we want to have the same type more
+than once in the variant value type list:
+
+> y :: V [Int,Int,String,Int,Float]
+> y = toVariantAt @1 5
+
+== Pattern matching
+
+=== Direct pattern matching with V
+
+Matching a variant value can be done with the 'V' pattern synonym too:
+
+> f :: V [String,Int] -> String
+> f = \case
+>    V s            -> "Found string: " ++ s
+>    V (i :: Int)   -> "Found int: " ++ show i
+>    _              -> undefined
+
+Note: for now the compiler cannot use the variant value type list to infer that
+the pattern-match is complete. Hence we need the wildcard match to avoid a warning.
+
+See "Data.Variant.ContFlow" for safe alternatives that do not require a wildcard
+match and that provide better type inference.
+
+__Basic errors__
+
+If you try to set or match a value type that is not valid, you get a compile-time
+error:
+
+> x :: V [String,Int]
+> x = V @Float 10
+>
+> -- error: `Float' is not a member of [String, Int]
+
+=== Safe pattern matching with continuations
+
+See "Data.Variant.ContFlow" for safe pattern matching using multi-continuations
+('>:>' and '>%:>') that ensure completeness at compile time.
+
+== Operations by index
+
+We can retrieve values by index with 'fromVariantAt':
+
+> x :: V [Int,String,Float]
+> x = toVariantAt @2 5.0
+>
+> > fromVariantAt @0 x
+> Nothing
+> > fromVariantAt @1 x
+> Nothing
+> > fromVariantAt @2 x
+> Just 5.0
+
+== Generic variant functions (variant-polymorphic functions)
+
+=== Splitting variants
+
+We can chose to handle only a subset of the possible value types of a Variant
+by using 'splitVariant'. This is very useful when your variant is open (e.g. an
+exception type) and you want to perform an action for some particular types
+while ignoring the others (e.g. passing the unhandled exceptions to the caller).
+
+For instance in the following example we only handle @Int@ and @Float@
+values. The other ones are considered as left-overs:
+
+> printNum v = case splitVariant @[Float,Int] v of
+>    Right v -> v >%:>
+>       ( \f -> putStrLn ("Found float: " ++ show (f :: Float))
+>       , \i -> putStrLn ("Found int: " ++ show (i :: Int))
+>       )
+>    Left leftovers -> putStrLn "Not a supported number!"
+
+Note that the @printNum@ function above is generic and can be applied to any
+Variant type.
+
+=== Membership constraints: '(:<)', '(:<<)', '(:<?)' #membership
+
+The @c :< cs@ constraint statically ensures that the type @c@ is in the @cs@
+type list and that we can set and match it in a variant with type @V cs@. For
+example:
+
+> newtype Error = Error String
+>
+> showError :: (Error :< cs) => V cs -> String
+> showError = \case
+>    V (Error s) -> "Found error: " ++ s
+>    _           -> "Not an Error!"
+
+Note that to shorten a list of constraints such as @(A :< xs, B :< xs, C :< xs)@
+you can use the '(:<<)' operator: @[A,B,C] :<< xs@.
+
+The @c :< cs@ constraint statically ensures that the type @c@ is in the @cs@
+type list. However in some cases we want to write generic functions that work on
+variants even if they cannot contain the given type.
+
+The '(:<?)'  constraint and the 'VMaybe' pattern can be used for this:
+
+> showErrorMaybe :: (Error :<? cs) => V cs -> String
+> showErrorMaybe = \case
+>    VMaybe (Error s) -> "Found error: " ++ s
+>    _                -> "Not an Error!"
+
+=== Shrinking variants with 'popVariant'
+
+A very common use of variants is to pattern match on a specific value type they
+can contain and to get a new variant containing the left-over value types. This
+is done with 'popVariant' or 'popVariantMaybe' and the 'Remove' type family.
+For example:
+
+> filterError :: Error :<? cs => V cs -> V (Remove Error cs)
+> filterError v = case popVariantMaybe v of
+>    Right (Error s) -> error ("Found error: " ++ s)
+>    Left  v'        -> v' -- left-over variant!
+
+Notice how an @Error@ value cannot be present anymore in the variant type
+returned by @filterError@ and how this function is generic as it supports any
+variant as an input.
+
+== Conversions
+
+=== Singleton conversion
+
+We can easily convert between a variant with a single value type and this value
+type with 'variantToValue' and 'variantFromValue':
+
+> intV :: V [Int]
+> intV = V @Int 10
+>
+> > variantToValue intV
+> 10
+>
+> > :t variantFromValue "Test"
+> variantFromValue "Test" :: V [String]
+
+=== Either conversion
+
+'variantFromEither' and 'variantToEither' can be used to convert between a
+variant of arity 2 and the @Either@ data type:
+
+> eith :: Either Int String
+> eith = Left 10
+>
+> > :t variantFromEither eith
+> variantFromEither eith :: V [String, Int]
+>
+> x,y :: V [String,Int]
+> x = V "test"
+> y = V @Int 10
+>
+> > variantToEither x
+> Right "test"
+>
+> > variantToEither y
+> Left 10
+
+== Extending the list of supported types
+
+We can extend the value types of a variant by appending or prepending a list of
+types with 'appendVariant' and 'prependVariant':
+
+> x :: V [String,Int]
+> x = V "test"
+>
+> data A = A
+> data B = B
+>
+> px = prependVariant @[A,B] x
+> ax = appendVariant @[A,B] x
+>
+> > :t ax
+> ax :: V [String, Int, A, B]
+>
+> > :t px
+> px :: V [A, B, String, Int]
+
+Appending and prepending are very cheap operations: appending just messes with
+types and performs nothing at runtime; prepending only increases the tag value
+at runtime by a constant number.
+
+=== Variant lifting (extending and reordering)
+
+We can extend and reorder the value types of a variant with 'liftVariant':
+
+> x :: V [String,Int]
+> x = V "test"
+>
+> -- adding Double and Float, and reordering
+> y :: V [Double,Int,Float,String]
+> y = liftVariant x
+
+You can use the 'LiftVariant' constraint to write generic code and to ensure
+that the type list @is@ is a subset of @os@:
+
+> liftX :: (LiftVariant is (Double : Float : is))
+>       => V is -> V (Double : Float : is)
+> liftX = liftVariant
+>
+> > :t liftX x
+> liftX x :: V [Double, Float, String, Int]
+
+== Removing duplicates (nub)
+
+If the list of types of a variant contains the same type more than once, we can
+decide to only keep one of them with 'nubVariant':
+
+> > z = nubVariant (V "test" :: V [String,Int,Double,Float,Double,String])
+> > :t z
+> z :: V [String, Int, Double, Float]
+
+== Flattening nested variants
+
+If the value types of a variant are themselves variants, you can flatten them
+with 'flattenVariant':
+
+> x :: V [String,Int]
+> x = V "test"
+>
+> nest :: V [ V [String,Int], V [Float,Double]]
+> nest = V x
+>
+> > :t flattenVariant nest
+> flattenVariant nest :: V [String, Int, Float, Double]
+
+== Joining variants of functors\/monads
+
+We can transform a variant of functor values (e.g., @V [m a, m b, m c]@) into
+a single functor value (e.g., @m (V [a,b,c])@) with 'joinVariant':
+
+> fs0,fs1,fs2 :: V [ Maybe Int, Maybe String, Maybe Double]
+> fs0 = V @(Maybe Int) (Just 10)
+> fs1 = V (Just "Test")
+> fs2 = V @(Maybe Double) Nothing
+>
+> > joinVariant @Maybe fs0
+> Just (V @Int 10)
+>
+> > joinVariant @Maybe fs1
+> Just (V @[Char] "Test")
+>
+> > joinVariant @Maybe fs2
+> Nothing
+
+It also works with @IO@ for example:
+
+> ms0,ms1 :: V [ IO Int, IO String, IO Double]
+> ms0 = V @(IO Int) (printRet 10)
+> ms1 = V (printRet "Test")
+>
+> > joinVariant @IO ms0
+> 10
+> V @Int 10
+>
+> > :t joinVariant @IO ms0
+> joinVariant @IO ms0 :: IO (V [Int, String, Double])
+
+Writing generic code requires the use of the 'JoinVariant' constraint and
+the resulting list of value types can be obtained with the 'ExtractM' type
+family.
+
+With @IO@ it is possible to use 'joinVariantUnsafe' which does not require the
+type application and does not use the 'JoinVariant' type-class. However some
+other functor types are not supported (e.g., @Maybe@) and using
+'joinVariantUnsafe' with them makes the program crash at runtime.
+
+== Combining two variants (product)
+
+We can combine two variants into a single variant containing a tuple with
+'productVariant':
+
+> fl :: V [Float,Double]
+> fl = V @Float 5.0
+>
+> d :: V [Int,Word]
+> d = V @Word 10
+>
+> dfl = productVariant d fl
+>
+> > dfl
+> V @(Word,Float) (10,5.0)
+>
+> > :t dfl
+> dfl :: V [(Int, Float), (Int, Double), (Word, Float), (Word, Double)]
+
+== Converting variants to tuples\/HList
+
+We can convert a Variant into a tuple of 'Maybe's with 'variantToTuple':
+
+> w :: V [String,Int,Double,Maybe Int]
+> w = V @Double 1.0
+>
+> > variantToTuple w
+> (Nothing,Nothing,Just 1.0,Nothing)
+
+And similarly into an HList (heterogeneous list) with 'variantToHList':
+
+> > variantToHList w
+> H[Nothing,Nothing,Just 1.0,Nothing]
+
+== Mapping
+
+=== By type
+
+We can easily apply a function @f :: A -> B@ to a variant so that its value
+type @A@ is replaced with @B@. If the value in the variant has type @A@, then
+@f@ is applied to it to get the new value. Example:
+
+> x,y :: V [String,Int]
+> x = V "test"
+> y = V @Int 10
+>
+> > mapVariant ((+5) :: Int -> Int) x
+> V @String "test"
+>
+> > mapVariant ((+5) :: Int -> Int) y
+> V @Int 15
+
+Note that the resulting variant may contain the same type more than once. To
+avoid this, we can either use 'nubVariant' or directly use 'mapNubVariant':
+
+> > :t mapVariant (length :: String -> Int) x
+> mapVariant (length :: String -> Int) x :: V [Int, Int]
+>
+> > :t mapNubVariant (length :: String -> Int) x
+> mapNubVariant (length :: String -> Int) x :: V [Int]
+>
+> > mapNubVariant (length :: String -> Int) x
+> V @Int 4
+
+=== By index
+
+If we know the index of the value type we want to map, we can use
+'mapVariantAt'. Example:
+
+> x,y :: V [String,Int]
+> x = V "test"
+> y = V @Int 10
+>
+> > mapVariantAt @0 length x
+> V @Int 4
+>
+> > mapVariantAt @0 length y
+> V @Int 10
+>
+> > mapVariantAt @1 (+5) x
+> V @[Char] "test"
+>
+> > mapVariantAt @1 (+5) y
+> V @Int 15
+
+Note that the compiler uses the type of the element whose index is given as
+first argument to infer the type of the functions, hence we do not need type
+ascriptions.
+
+We can use 'mapVariantAtM' to perform an applicative (or monadic) update.
+
+=== First matching type
+
+A variant can have the same type more than once in its value type list.
+'mapVariant' updates all the matching types in the list but sometimes that is
+not what we want. We can use 'mapVariantAt' if we know the index of the type we
+want to update. We can also use 'mapVariantFirst' to update only the first
+matching type:
+
+> vv :: V [Int,Int,Int]
+> vv = toVariantAt @1 5
+>
+> > r0 = mapVariant (show :: Int -> String) vv
+> > r1 = mapVariantFirst (show :: Int -> String) vv
+>
+> > :t r0
+> r0 :: V [String,String,String]
+>
+> > :t r1
+> r1 :: V [String, Int, Int]
+>
+> > r0
+> V @[Char] "5"
+>
+> > r1
+> V @Int 5
+
+We can also apply an applicative (or monadic) function with
+'mapVariantFirstM'.
+
+-}
 module Data.Variant
    ( V (..)
    , variantIndex
@@ -216,7 +707,7 @@
 -- | Haskell code corresponding to a Variant
 --
 -- >>> showsVariant 0 (V @Double 5.0 :: V [Int,String,Double]) ""
--- "V @Double 5.0 :: V '[Int, [Char], Double]"
+-- "V @Double 5.0 :: V [Int, [Char], Double]"
 showsVariant ::
    ( Typeable xs
    , ShowTypeList (V xs)
@@ -238,7 +729,7 @@
 
 -- | Show instance
 --
--- >>> show (V @Int 10  :: V '[Int,String,Double])
+-- >>> show (V @Int 10  :: V [Int,String,Double])
 -- "10"
 instance
    ( Show x
@@ -252,7 +743,7 @@
 -- | Show a list of ShowS
 showList__ :: [ShowS] -> ShowS
 showList__ []     s = "'[]" ++ s
-showList__ (x:xs) s = '\'' : '[' : x (showl xs)
+showList__ (x:xs) s = '[' : x (showl xs)
   where
     showl []     = ']' : s
     showl (y:ys) = ',' : ' ' : y (showl ys)
@@ -294,7 +785,7 @@
 
 -- | Get variant size
 --
--- >>> let x = V "Test" :: V '[Int,String,Double]
+-- >>> let x = V "Test" :: V [Int,String,Double]
 -- >>> variantSize x
 -- 3
 -- >>> let y = toVariantAt @0 10 :: V [Int,String,Double,Int]
@@ -545,7 +1036,7 @@
 
 -- | Bimap Variant head and tail 
 --
--- >>> let f = mapVariantHeadTail (+5) (appendVariant @'[Double,Char])
+-- >>> let f = mapVariantHeadTail (+5) (appendVariant @[Double,Char])
 -- >>> f (V @Int 10 :: V [Int,Word,Float])
 -- 15
 --
@@ -701,7 +1192,7 @@
 
 -- | Pick the first matching type of a Variant
 --
--- >>> let x = toVariantAt @2 10 :: V '[Int,String,Int]
+-- >>> let x = toVariantAt @2 10 :: V [Int,String,Int]
 -- >>> fromVariantFirst @Int x
 -- Nothing
 --
@@ -872,7 +1363,7 @@
 --
 -- >>> newtype Odd  = Odd Int  deriving (Show)
 -- >>> newtype Even = Even Int deriving (Show)
--- >>> let f x = if even x then V (Even x) else V (Odd x) :: V '[Odd, Even]
+-- >>> let f x = if even x then V (Even x) else V (Odd x) :: V [Odd, Even]
 -- >>> foldMapVariantAt @1 f (V @Int 10 :: V [Float,Int,Double])
 -- Even 10
 --
diff --git a/src/lib/Data/Variant/ContFlow.hs b/src/lib/Data/Variant/ContFlow.hs
--- a/src/lib/Data/Variant/ContFlow.hs
+++ b/src/lib/Data/Variant/ContFlow.hs
@@ -8,7 +8,48 @@
 {-# LANGUAGE UndecidableInstances #-}
 {-# LANGUAGE BangPatterns #-}
 
--- | Continuation based control-flow
+{- | Continuation-based control-flow
+
+This module provides safe pattern matching on 'Data.Variant.V' values using
+multi-continuations. Instead of pattern matching with the @V@ pattern (which
+the compiler cannot check for completeness), we can provide a function per
+constructor as in a pattern-match.
+
+== Safe pattern matching with ordered continuations ('>:>')
+
+With multi-continuations we can transform a variant @V [A,B,C]@ into a
+function whose type is @(A -> r, B -> r, C -> r) -> r@. Hence the compiler
+will ensure that we provide the correct number of alternatives in the
+continuation tuple.
+
+Applying a multi-continuation to a Variant is done with '>:>':
+
+> import Data.Variant.ContFlow
+>
+> printV :: V [String,Int,Float] -> IO ()
+> printV v = v >:>
+>    ( \s -> putStrLn ("Found string: " ++ s)
+>    , \i -> putStrLn ("Found int: " ++ show i)
+>    , \f -> putStrLn ("Found float: " ++ show f)
+>    )
+
+== Safe pattern matching with unordered continuations ('>%:>')
+
+By using the '>%:>' operator instead of '>:>', we can provide continuations in
+any order as long as an alternative for each constructor is provided.
+
+The types must be unambiguous as the Variant constructor types cannot be used to
+infer the continuation types (as is done with '>:>'). Hence the type
+ascriptions in the following example:
+
+> printU :: V [String,Int,Float] -> IO ()
+> printU v = v >%:>
+>    ( \f -> putStrLn ("Found float: " ++ show (f :: Float))
+>    , \s -> putStrLn ("Found string: " ++ s)
+>    , \i -> putStrLn ("Found int: " ++ show (i :: Int))
+>    )
+
+-}
 module Data.Variant.ContFlow
    ( ContFlow (..)
    , ContTuple
diff --git a/src/lib/Data/Variant/EADT/TH.hs b/src/lib/Data/Variant/EADT/TH.hs
--- a/src/lib/Data/Variant/EADT/TH.hs
+++ b/src/lib/Data/Variant/EADT/TH.hs
@@ -150,7 +150,7 @@
 
             conTyp = getConTyp tys
 
-            -- [* -> *]
+            -- [Type -> Type]
             tyToTyList = AppT ListT (AppT (AppT ArrowT StarT) StarT)
 
             -- retrieve functor var in "e"
diff --git a/src/lib/Data/Variant/Excepts.hs b/src/lib/Data/Variant/Excepts.hs
--- a/src/lib/Data/Variant/Excepts.hs
+++ b/src/lib/Data/Variant/Excepts.hs
@@ -14,6 +14,79 @@
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 
+{- | Multi-exception monad transformer
+
+Just like @ExceptT e m a@ from the @transformers@ package wraps an @Either e a@
+value, 'Excepts' wraps a 'VEither' value (see "Data.Variant.VEither") and
+provides standard do-notation for computations that can fail with multiple
+error types.
+
+== Example
+
+> import Data.Variant.Excepts
+>
+> import Prelude hiding (head,lookup)
+> import qualified Prelude
+> import Text.Read
+>
+> data ParseError = ParseError deriving Show
+>
+> parse :: String -> Excepts [ParseError] IO Integer
+> parse s = case readMaybe s of
+>    Just i  -> pure i
+>    Nothing -> throwE ParseError
+>
+>
+> data HeadError = ListWasEmpty deriving Show
+>
+> head :: [a] -> Excepts [HeadError] IO a
+> head []    = throwE ListWasEmpty
+> head (x:_) = pure x
+>
+> data LookupError k = KeyWasNotPresent k deriving Show
+>
+> lookup :: Eq k => k -> [(k,v)] -> Excepts [LookupError k] IO v
+> lookup k vs = case Prelude.lookup k vs of
+>    Just v  -> pure v
+>    Nothing -> throwE (KeyWasNotPresent k)
+>
+>
+> foo :: String -> Excepts [ParseError, LookupError Char, HeadError] IO Integer
+> foo str = do
+>    c <- liftE $ head str
+>    r <- liftE $ lookup c codeMap
+>    liftE $ parse (r ++ tail str)
+>
+>    where
+>       codeMap :: [(Char, String)]
+>       codeMap = [ ('x', "0x")
+>                 , ('d', "")
+>                 ]
+
+Test:
+
+> > runE (foo "d10")
+> VRight 10
+>
+> > runE (foo "x10")
+> VRight 16
+>
+> > runE (foo "u10")
+> VLeft KeyWasNotPresent 'u'
+>
+> > runE (foo "")
+> VLeft ListWasEmpty
+>
+> > runE (foo "d10X")
+> VLeft ParseError
+
+Exceptions can be caught with 'catchE':
+
+> > runE (foo "" `catchE` (\ListWasEmpty -> successE 42)
+> >    :: Excepts [ParseError,LookupError Char] IO Integer)
+> VRight 42
+
+-}
 module Data.Variant.Excepts
    ( Excepts (..)
    , runE
diff --git a/src/lib/Data/Variant/Syntax.hs b/src/lib/Data/Variant/Syntax.hs
--- a/src/lib/Data/Variant/Syntax.hs
+++ b/src/lib/Data/Variant/Syntax.hs
@@ -3,7 +3,86 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE DataKinds #-}
 
--- | Rebindable syntax for Variant
+{- | Rebindable syntax for Variant
+
+This module provides @RebindableSyntax@-based do-notation for 'V'. We
+recommend using 'Data.Variant.VEither' or "Data.Variant.Excepts" instead, which
+provide proper 'Monad' instances without requiring @RebindableSyntax@.
+
+== Do-notation with Variants
+
+We can use do-notation with 'V' as we would with other sum types such as
+'Maybe' or 'Either'. However, as we cannot have a 'Monad' instance for 'V', we
+rely on the @RebindableSyntax@ extension to mimic it.
+
+The leftmost type is extracted from the Variant with @>>=@ (or @x \<-
+myVariant@ with do-notation syntax). Variant types are concatenated on the
+left.
+
+Function @foo@ in the following example composes functions returning Variants
+by using do-notation:
+
+> {-# LANGUAGE TypeApplications #-}
+> {-# LANGUAGE RebindableSyntax #-}
+>
+> import Data.Variant
+> import Data.Variant.Syntax
+>
+> import Prelude hiding (head,lookup,(>>=),(>>),return)
+> import qualified Prelude
+> import Text.Read
+>
+> foo :: String -> V [Integer, ParseError, LookupError Char, HeadError]
+> foo str = do
+>    c <- head str
+>    r <- lookup c codeMap
+>    parse (r ++ tail str)
+>
+>    where
+>       codeMap :: [(Char, String)]
+>       codeMap = [ ('x', "0x")
+>                 , ('d', "")
+>                 ]
+>
+>
+> data ParseError = ParseError deriving Show
+>
+> parse :: String -> V [Integer,ParseError]
+> parse s = case readMaybe s of
+>    Just i  -> V @Integer i
+>    Nothing -> V ParseError
+>
+> data HeadError = ListWasEmpty deriving Show
+>
+> head :: [a] -> V [a,HeadError]
+> head []    = toVariantAt @1 ListWasEmpty
+> head (x:_) = toVariantAt @0 x
+>
+> data LookupError k = KeyWasNotPresent k deriving Show
+>
+> lookup :: Eq k => k -> [(k,v)] -> V [v,LookupError k]
+> lookup k vs = case Prelude.lookup k vs of
+>    Just v  -> toVariantAt @0 v
+>    Nothing -> toVariantAt @1 (KeyWasNotPresent k)
+
+Test:
+
+> > foo "d10"
+> V @Integer 10
+>
+> > foo "x10"
+> V @Integer 16
+>
+> > foo "u10"
+> V @(LookupError Char) (KeyWasNotPresent 'u')
+>
+> > foo ""
+> V @HeadError ListWasEmpty
+>
+> > foo "d10X"
+> V @ParseError ParseError
+
+-}
 module Data.Variant.Syntax
    ( (>>=)
    , (>>)
diff --git a/src/lib/Data/Variant/VEither.hs b/src/lib/Data/Variant/VEither.hs
--- a/src/lib/Data/Variant/VEither.hs
+++ b/src/lib/Data/Variant/VEither.hs
@@ -20,10 +20,68 @@
 {-# LANGUAGE DerivingStrategies #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 
--- | Variant biased towards one type
---
--- This allows definition of common type classes (Functor, etc.) that can't  be
--- provided for Variant
+{- | Variant biased towards one type
+
+Variants have types like @V [W,X,Y,Z]@. This is great when all the inner types
+play the same role. However in some cases we want one type to be the main one
+and the other ones to be secondaries.
+
+For instance we could have @V [Result,ErrorA,ErrorB,ErrorC]@ to represent the
+result of a function. In this case, the first type is the main one and it would
+be great to be able to define the common type-classes ('Functor', 'Monad',
+etc.) so that we have easy access to it.
+
+'VEither' is a 'V' wrapper that does exactly this:
+
+> newtype VEither es a = VEither (V (a : es))
+
+It is isomorphic to @Either (V es) a@. The difference is in the runtime
+representation: @VEither es a@ has one less indirection than @Either (V es) a@
+(it uses only one tag value).
+
+== Pattern matching (VRight and VLeft)
+
+'VEither' values can be created and matched on with the 'VRight' and 'VLeft'
+patterns (just as if we had the @Either (V es) a@ type).
+
+> >>> VRight True :: VEither [String,Int] Bool
+> VRight True
+>
+> >>> VLeft (V "failed" :: V [String,Int]) :: VEither [String,Int] Bool
+> VLeft "failed"
+
+== Common instances
+
+The main advantage of @VEither es a@ over @V (a ': es)@ is that we can define
+instances for common type-classes such as 'Functor', 'Applicative', 'Monad',
+'Foldable', etc.:
+
+> > let x = VRight True :: VEither [Int,Float] Bool
+> > fmap (\b -> if b then "Success" else "Failure") x
+> VRight "Success"
+>
+> > let x = VRight True  :: VEither [Int,Float] Bool
+> > let y = VRight False :: VEither [Int,Float] Bool
+> > (&&) \<$> x \<*> y
+> VRight False
+>
+> > let x   = VRight True    :: VEither [Int,Float] Bool
+> > let f v = VRight (not v) :: VEither [Int,Float] Bool
+> > x >>= f
+> VRight False
+>
+> > let x = VRight True :: VEither [Int,Float] Bool
+> > let y = VLeft (V "failed" :: V [String,Int]) :: VEither [String,Int] Bool
+> > forM_ x print
+> True
+> > forM_ y print
+
+== See also
+
+* "Data.Variant.Excepts" — multi-exception monad transformer wrapping 'VEither'
+* "Data.Variant" — the underlying 'V' type
+
+-}
 module Data.Variant.VEither
    ( VEither
    , pattern VLeft
@@ -68,7 +126,7 @@
 
 -- | Left value
 --
--- >>> VLeft (V "failed" :: V '[String,Int]) :: VEither '[String,Int] Bool
+-- >>> VLeft (V "failed" :: V [String,Int]) :: VEither [String,Int] Bool
 -- VLeft "failed"
 --
 pattern VLeft :: forall x xs. V xs -> VEither xs x
@@ -78,7 +136,7 @@
 
 -- | Right value
 --
--- >>> VRight True :: VEither '[String,Int] Bool
+-- >>> VRight True :: VEither [String,Int] Bool
 -- VRight True
 pattern VRight :: forall x xs. x -> VEither xs x
 pattern VRight x <- ((popVariantHead . veitherToVariant) -> Right x)
@@ -93,10 +151,10 @@
 
 -- | Check VEithers for equality
 --
--- >>> let a = VRight "Foo" :: VEither '[Int,Double] String
--- >>> let b = VRight "Foo" :: VEither '[Int,Double] String
--- >>> let c = VRight "Bar" :: VEither '[Int,Double] String
--- >>> let d = VLeft (V (1::Int) :: V '[Int, Double]) :: VEither '[Int,Double] String
+-- >>> let a = VRight "Foo" :: VEither [Int,Double] String
+-- >>> let b = VRight "Foo" :: VEither [Int,Double] String
+-- >>> let c = VRight "Bar" :: VEither [Int,Double] String
+-- >>> let d = VLeft (V (1::Int) :: V [Int, Double]) :: VEither [Int,Double] String
 -- >>> a == b
 -- True
 -- >>> a == c
@@ -113,8 +171,8 @@
 
 -- | Compare VEithers
 --
--- >>> let a = VRight "Foo" :: VEither '[Int,Double] String
--- >>> let b = VRight "Bar" :: VEither '[Int,Double] String
+-- >>> let a = VRight "Foo" :: VEither [Int,Double] String
+-- >>> let b = VRight "Bar" :: VEither [Int,Double] String
 -- >>> a < b
 -- False
 -- >>> a > b
@@ -140,7 +198,7 @@
 
 -- | Convert a Variant into a VEither
 --
--- >>> let x = V "Test" :: V '[Int,String,Double]
+-- >>> let x = V "Test" :: V [Int,String,Double]
 -- >>> veitherFromVariant x
 -- VLeft "Test"
 --
@@ -150,7 +208,7 @@
 
 -- | Convert a VEither into a Variant
 --
--- >>> let x = VRight True :: VEither '[Int,Float] Bool
+-- >>> let x = VRight True :: VEither [Int,Float] Bool
 -- >>> veitherToVariant x
 -- True
 --
@@ -160,7 +218,7 @@
 
 -- | Convert a VEither into an Either
 --
--- >>> let x = VRight True :: VEither '[Int,Float] Bool
+-- >>> let x = VRight True :: VEither [Int,Float] Bool
 -- >>> veitherToEither x
 -- Right True
 --
@@ -181,7 +239,7 @@
 
 -- | Bimap for VEither
 --
--- >>> let x = VRight True :: VEither '[Int,Float] Bool
+-- >>> let x = VRight True :: VEither [Int,Float] Bool
 -- >>> veitherBimap id not x
 -- VRight False
 --
@@ -229,7 +287,7 @@
 
 -- | Functor instance for VEither
 --
--- >>> let x = VRight True :: VEither '[Int,Float] Bool
+-- >>> let x = VRight True :: VEither [Int,Float] Bool
 -- >>> fmap (\b -> if b then "Success" else "Failure") x
 -- VRight "Success"
 --
@@ -239,8 +297,8 @@
 
 -- | Applicative instance for VEither
 --
--- >>> let x = VRight True  :: VEither '[Int,Float] Bool
--- >>> let y = VRight False :: VEither '[Int,Float] Bool
+-- >>> let x = VRight True  :: VEither [Int,Float] Bool
+-- >>> let y = VRight False :: VEither [Int,Float] Bool
 -- >>> (&&) <$> x <*> y
 -- VRight False
 -- >>> (||) <$> x <*> y
@@ -255,8 +313,8 @@
 
 -- | Monad instance for VEither
 --
--- >>> let x   = VRight True    :: VEither '[Int,Float] Bool
--- >>> let f v = VRight (not v) :: VEither '[Int,Float] Bool
+-- >>> let x   = VRight True    :: VEither [Int,Float] Bool
+-- >>> let f v = VRight (not v) :: VEither [Int,Float] Bool
 -- >>> x >>= f
 -- VRight False
 --
@@ -266,8 +324,8 @@
 
 -- | Foldable instance for VEither
 --
--- >>> let x   = VRight True    :: VEither '[Int,Float] Bool
--- >>> let y   = VLeft (V "failed" :: V '[String,Int]) :: VEither '[String,Int] Bool
+-- >>> let x   = VRight True    :: VEither [Int,Float] Bool
+-- >>> let y   = VLeft (V "failed" :: V [String,Int]) :: VEither [String,Int] Bool
 -- >>> forM_ x print
 -- True
 -- >>> forM_ y print
diff --git a/src/lib/Data/Variant/VariantF.hs b/src/lib/Data/Variant/VariantF.hs
--- a/src/lib/Data/Variant/VariantF.hs
+++ b/src/lib/Data/Variant/VariantF.hs
@@ -76,7 +76,7 @@
 -- >>>
 -- >>> data ConsF a e = ConsF a e deriving (Eq,Ord,Show,Functor)
 -- >>> data NilF    e = NilF      deriving (Eq,Ord,Show,Functor)
--- >>> type ListF   a = VariantF '[NilF,ConsF a]
+-- >>> type ListF   a = VariantF [NilF,ConsF a]
 -- >>>
 -- >>> instance Eq a => Eq1 (ConsF a) where liftEq cmp (ConsF a e1) (ConsF b e2) = a == b && cmp e1 e2
 -- >>> instance Eq1 NilF where liftEq _ _ _ = True
@@ -102,7 +102,7 @@
 
 -- | Apply its first argument to every element of the 2nd arg list
 --
--- > ApplyAll e '[f,g,h] ==> '[f e, g e, h e]
+-- > ApplyAll e [f,g,h] ==> [f e, g e, h e]
 --
 type family ApplyAll (e :: t) (xs :: [t -> k]) :: [k] where
    ApplyAll e '[]       = '[]
@@ -112,9 +112,9 @@
 
 -- | Eq instance for VariantF
 --
--- >>> let a = FV (ConsF 'a' "Test") :: VariantF '[ConsF Char,NilF] String
--- >>> let a' = FV (ConsF 'a' "XXX") :: VariantF '[ConsF Char,NilF] String
--- >>> let b = FV (ConsF 'b' "Test") :: VariantF '[ConsF Char,NilF] String
+-- >>> let a = FV (ConsF 'a' "Test") :: VariantF [ConsF Char,NilF] String
+-- >>> let a' = FV (ConsF 'a' "XXX") :: VariantF [ConsF Char,NilF] String
+-- >>> let b = FV (ConsF 'b' "Test") :: VariantF [ConsF Char,NilF] String
 -- >>> a == a
 -- True
 -- >>> a == a'
@@ -122,12 +122,12 @@
 -- >>> a == b
 -- False
 --
--- >>> let c = FV (ConsF 'c' b) :: VariantF '[ConsF Char,NilF] (VariantF '[ConsF Char, NilF] String)
+-- >>> let c = FV (ConsF 'c' b) :: VariantF [ConsF Char,NilF] (VariantF [ConsF Char, NilF] String)
 -- >>> c == c
 -- True
 --
--- >>> let n1 = FV (NilF :: NilF ()) :: VariantF '[ConsF Char,NilF] ()
--- >>> let n2 = FV (NilF :: NilF ()) :: VariantF '[ConsF Char,NilF] ()
+-- >>> let n1 = FV (NilF :: NilF ()) :: VariantF [ConsF Char,NilF] ()
+-- >>> let n2 = FV (NilF :: NilF ()) :: VariantF [ConsF Char,NilF] ()
 -- >>> n1 == n2
 -- True
 --
@@ -141,9 +141,9 @@
 
 -- | Ord instance for VariantF
 --
--- >>> let a = FV (ConsF 'a' "Test") :: VariantF '[ConsF Char,NilF] String
--- >>> let a' = FV (ConsF 'a' "XXX") :: VariantF '[ConsF Char,NilF] String
--- >>> let b = FV (ConsF 'b' "Test") :: VariantF '[ConsF Char,NilF] String
+-- >>> let a = FV (ConsF 'a' "Test") :: VariantF [ConsF Char,NilF] String
+-- >>> let a' = FV (ConsF 'a' "XXX") :: VariantF [ConsF Char,NilF] String
+-- >>> let b = FV (ConsF 'b' "Test") :: VariantF [ConsF Char,NilF] String
 -- >>> compare a a
 -- EQ
 -- >>> compare a a'
@@ -203,8 +203,8 @@
 
 -- | Show instance for VariantF
 --
--- >>> let a = FV (ConsF 'a' "Test") :: VariantF '[ConsF Char,NilF] String
--- >>> let b = FV (NilF :: NilF String) :: VariantF '[ConsF Char,NilF] String
+-- >>> let a = FV (ConsF 'a' "Test") :: VariantF [ConsF Char,NilF] String
+-- >>> let b = FV (NilF :: NilF String) :: VariantF [ConsF Char,NilF] String
 -- >>> print a
 -- ConsF 'a' "Test"
 -- >>> print b
@@ -229,7 +229,7 @@
 
 -- | Pattern-match in a VariantF
 --
--- >>> FV (NilF :: NilF String) :: VariantF '[ConsF Char,NilF] String
+-- >>> FV (NilF :: NilF String) :: VariantF [ConsF Char,NilF] String
 -- NilF
 pattern FV :: forall c cs e. c :< (ApplyAll e cs) => c -> VariantF cs e
 pattern FV x = VariantF (V x)
diff --git a/src/tests/EADT.hs b/src/tests/EADT.hs
--- a/src/tests/EADT.hs
+++ b/src/tests/EADT.hs
@@ -35,8 +35,8 @@
 eadtPattern 'NilF  "Nil"
 eadtInfixPattern 'ConsF ":->"
 
-type ListF a = VariantF '[NilF, ConsF a]
-type List  a = EADT     '[NilF, ConsF a]
+type ListF a = VariantF [NilF, ConsF a]
+type List  a = EADT     [NilF, ConsF a]
 
 instance Eq a => Eq1 (ConsF a) where
    liftEq cmp (ConsF a e1) (ConsF b e2) = a == b && cmp e1 e2
diff --git a/src/tests/Variant.hs b/src/tests/Variant.hs
--- a/src/tests/Variant.hs
+++ b/src/tests/Variant.hs
@@ -26,8 +26,8 @@
 data E = E deriving (Show,Eq)
 data F = F deriving (Show,Eq)
 
-type ABC = V '[A,B,C]
-type DEF = V '[D,E,F]
+type ABC = V [A,B,C]
+type DEF = V [D,E,F]
 
 b :: ABC
 b = toVariantAt @1 B
@@ -60,7 +60,7 @@
                                                          V (x :: B) -> x == B
                                                          V (_ :: C) -> False
                                                          _          -> undefined
-   , testProperty "pattern V: type application"       $ (V @Float 1.0 :: V '[Int,Float,String]) == toVariantAt @1 1.0
+   , testProperty "pattern V: type application"       $ (V @Float 1.0 :: V [Int,Float,String]) == toVariantAt @1 1.0
    , testProperty "get by type (match)"               $ fromVariant    (V B :: ABC) == Just B
    , testProperty "get by type (don't match)"         $ fromVariant @C (V B :: ABC) == Nothing
    , testProperty "variant equality (match)"          $ b == b
@@ -74,35 +74,35 @@
    , testProperty "Convert single variant"            $ variantToValue (V A :: V '[A]) == A
    , testProperty "Lift Either: Left"                 $ variantFromEither (Left A :: Either A B) == V A
    , testProperty "Lift Either: Right"                $ variantFromEither (Right B :: Either A B) == V B
-   , testProperty "To Either: Left"                   $ variantToEither (V B :: V '[A,B]) == Left B
-   , testProperty "To Either: Right"                  $ variantToEither (V A :: V '[A,B]) == Right A
+   , testProperty "To Either: Left"                   $ variantToEither (V B :: V [A,B]) == Left B
+   , testProperty "To Either: Right"                  $ variantToEither (V A :: V [A,B]) == Right A
    , testProperty "popVariantHead (match)"            $ popVariantHead (V A :: ABC) == Right A
    , testProperty "popVariantHead (don't match)"      $ isLeft (popVariantHead b)
    , testProperty "popVariantAt (match)"              $ popVariantAt @1 b == Right B
    , testProperty "popVariantAt (don't match)"        $ isLeft (popVariantAt @2 b)
 
-   , testProperty "popVariant (match)"                $ popVariant @D (toVariantAt @4 D :: V '[A,B,C,B,D,E,D]) == Right D
-   , testProperty "popVariant (match)"                $ popVariant @D (toVariantAt @6 D :: V '[A,B,C,B,D,E,D]) == Right D
-   , testProperty "popVariant (don't match)"          $ popVariant @B (toVariantAt @4 D :: V '[A,B,C,B,D,E,D]) == Left (toVariantAt @2 D)
+   , testProperty "popVariant (match)"                $ popVariant @D (toVariantAt @4 D :: V [A,B,C,B,D,E,D]) == Right D
+   , testProperty "popVariant (match)"                $ popVariant @D (toVariantAt @6 D :: V [A,B,C,B,D,E,D]) == Right D
+   , testProperty "popVariant (don't match)"          $ popVariant @B (toVariantAt @4 D :: V [A,B,C,B,D,E,D]) == Left (toVariantAt @2 D)
 
-   , testProperty "prependVariant"                    $ fromVariantAt @4 (prependVariant @'[D,E,F] b) == Just B
-   , testProperty "appendVariant"                     $ fromVariantAt @1 (appendVariant @'[D,E,F] b)  == Just B
+   , testProperty "prependVariant"                    $ fromVariantAt @4 (prependVariant @[D,E,F] b) == Just B
+   , testProperty "appendVariant"                     $ fromVariantAt @1 (appendVariant @[D,E,F] b)  == Just B
 
-   , testProperty "alterVariant"                      $ alterVariant @Num (+1) (V @Float 1.0 :: V '[Int,Float]) == V @Float 2.0
-   , testProperty "alterVariant"                      $ alterVariant @Num (+1) (V @Float 1.0 :: V '[Float,Int]) == V @Float 2.0
+   , testProperty "alterVariant"                      $ alterVariant @Num (+1) (V @Float 1.0 :: V [Int,Float]) == V @Float 2.0
+   , testProperty "alterVariant"                      $ alterVariant @Num (+1) (V @Float 1.0 :: V [Float,Int]) == V @Float 2.0
 
    , testProperty "traverseVariant"                   $ traverseVariant @OrdNum (\x -> if x > 1 then Just x else Nothing)
-                                                            (V @Float 2.0 :: V '[Float,Int]) == Just (V @Float 2.0)
+                                                            (V @Float 2.0 :: V [Float,Int]) == Just (V @Float 2.0)
    , testProperty "traverseVariant"                   $ traverseVariant @OrdNum (\x -> if x > 1 then Just x else Nothing)
-                                                            (V @Float 0.5 :: V '[Float,Int]) == Nothing
-   , testProperty "liftVariant"                       $ fromVariant (liftVariant b :: V '[D,A,E,B,F,C])  == Just B
-   , testProperty "splitVariant"                      $ case splitVariant @'[A,C,D] (V A :: V '[A,B,C,D,E,F]) of
-                                                            Right (x :: V '[A,C,D]) -> x == V A
-                                                            Left  (_ :: V '[B,E,F]) -> True
-   , testProperty "splitVariant2"                     $ case splitVariant @'[A,C,D] (V E :: V '[A,B,C,D,E,F]) of
-                                                            Right (_ :: V '[A,C,D]) -> True
-                                                            Left  (y :: V '[B,E,F]) -> y == V E
-   , testProperty "toCont"                            $ (toCont (V E :: V '[A,B,C,D,E,F]) >::>
+                                                            (V @Float 0.5 :: V [Float,Int]) == Nothing
+   , testProperty "liftVariant"                       $ fromVariant (liftVariant b :: V [D,A,E,B,F,C])  == Just B
+   , testProperty "splitVariant"                      $ case splitVariant @[A,C,D] (V A :: V [A,B,C,D,E,F]) of
+                                                            Right (x :: V [A,C,D]) -> x == V A
+                                                            Left  (_ :: V [B,E,F]) -> True
+   , testProperty "splitVariant2"                     $ case splitVariant @[A,C,D] (V E :: V [A,B,C,D,E,F]) of
+                                                            Right (_ :: V [A,C,D]) -> True
+                                                            Left  (y :: V [B,E,F]) -> y == V E
+   , testProperty "toCont"                            $ (toCont (V E :: V [A,B,C,D,E,F]) >::>
                                                             ( \(_ :: A) -> False
                                                             , \(_ :: B) -> False
                                                             , \(_ :: C) -> False
diff --git a/variant.cabal b/variant.cabal
--- a/variant.cabal
+++ b/variant.cabal
@@ -1,23 +1,26 @@
 cabal-version:       2.4
 name:                variant
-version:             1.0.1
+version:             1.0.2
 synopsis:            Variant and EADT
 license:             BSD-3-Clause
 license-file:        LICENSE
 author:              Sylvain Henry
 maintainer:          sylvain@haskus.fr
 homepage:            https://www.haskus.org
-copyright:           Sylvain Henry 2024
+copyright:           Sylvain Henry 2026
 category:            System
 build-type:          Simple
 
 description:
    Variant (extensible sum type) and EADT (extensible recursive sum type)
    datatypes.
+extra-doc-files:
+  changelog.md
 
+
 source-repository head
   type: git
-  location: git://github.com/haskus/variant.git
+  location: https://github.com/haskus/variant.git
 
 flag unliftio
   Description: Enable MonadUnliftIO instance
@@ -42,7 +45,7 @@
   other-modules:
 
   build-depends:       
-     base                      >= 4.9 && < 5.0
+     base                      >= 4.9 && < 5
    , transformers
    , deepseq
    , exceptions                >= 0.9
