diff --git a/bench/tuple.hs b/bench/tuple.hs
new file mode 100644
--- /dev/null
+++ b/bench/tuple.hs
@@ -0,0 +1,57 @@
+module Main (main) where
+
+import Data.Time.Clock
+import Numeric.Semigroup
+import Numeric.Tuple
+
+
+main :: IO ()
+main = do
+    let n = 10000000 :: Int
+        one = 1 :: Int
+    bench "MaxTup0Fold" $
+      fromTuple $ foldMap' (const T0) [1..n]
+    bench "MaxFold" $
+      foldMap' Max [1..n]
+    bench "MaxTupFold" $
+      foldMap' (T1 <$> Max) [1..n]
+    bench "MaxSumFold" $
+      fromTuple $ foldMap' (T2 <$> Max <*> Sum) [1..n]
+    bench "MaxSumMinFold" $
+      fromTuple $ foldMap' (T3 <$> Max <*> Sum <*> Min) [1..n]
+    bench "MaxSumMinMinMaxFold" $
+      fromTuple $ foldMap' (T4 <$> Max <*> Sum <*> Min <*> minMax) [1..n]
+    bench "MaxSumMinMinMaxAllFold" $
+      fromTuple $ foldMap' (T5 <$> Max
+                               <*> Sum . (`mod` 19)
+                               <*> Min . (`mod` 23)
+                               <*> minMax
+                               <*> (All . (>0))
+                            ) [1..n]
+    bench "MaxSumMinMinMaxAllCountFold" $
+      fromTuple $ foldMap' (T6 <$> Max
+                               <*> Sum . (`mod` 19)
+                               <*> Min . (`mod` 23)
+                               <*> minMax
+                               <*> (All . (>0))
+                               <*> (Sum . const one)
+                            ) [1..n]
+    bench "MaxSumMinMinMaxAllCountProdFold" $
+      fromTuple $ foldMap' (T7 <$> Max
+                               <*> Sum . (`mod` 19)
+                               <*> Min . (`mod` 23)
+                               <*> minMax
+                               <*> (All . (>0))
+                               <*> (Sum . const one)
+                               <*> Product . (+1) . (*(-2)) . (`mod` 2)
+                            ) [1..n]
+
+
+
+
+bench :: Show a => String -> a -> IO ()
+bench bname v = do
+    t0 <- getCurrentTime
+    t1 <- t0 `seq` v `seq` putStrLn ("Evaluated " <> bname <> ":") >> print v >> getCurrentTime
+    seq t1 putStrLn $ "Execution time: " <> show (diffUTCTime t1 t0)
+    putStrLn ""
diff --git a/easytensor.cabal b/easytensor.cabal
--- a/easytensor.cabal
+++ b/easytensor.cabal
@@ -1,5 +1,5 @@
 name: easytensor
-version: 0.3.2.0
+version: 0.4.0.0
 cabal-version: >=1.20
 build-type: Simple
 license: BSD3
@@ -44,7 +44,7 @@
         Numeric.Scalar
         Numeric.Quaternion
         Numeric.Semigroup
-        Numeric.Semigroup.StrictTuple
+        Numeric.Tuple
     build-depends:
         base >=4.9 && <5,
         ghc-prim >=0.5,
@@ -142,6 +142,19 @@
 
     type: exitcode-stdio-1.0
     main-is: subspacefolds.hs
+    build-depends:
+        base -any,
+        easytensor -any,
+        dimensions -any,
+        time -any
+    default-language: Haskell2010
+    hs-source-dirs: bench
+    ghc-options: -Wall -fwarn-tabs -O2
+
+benchmark et-tuple
+
+    type: exitcode-stdio-1.0
+    main-is: tuple.hs
     build-depends:
         base -any,
         easytensor -any,
diff --git a/src/Numeric/Semigroup/StrictTuple.hs b/src/Numeric/Semigroup/StrictTuple.hs
deleted file mode 100644
--- a/src/Numeric/Semigroup/StrictTuple.hs
+++ /dev/null
@@ -1,578 +0,0 @@
-{-# LANGUAGE BangPatterns           #-}
-{-# LANGUAGE DeriveDataTypeable     #-}
-{-# LANGUAGE DeriveGeneric          #-}
-{-# LANGUAGE FunctionalDependencies #-}
-{-# LANGUAGE MultiParamTypeClasses  #-}
-{-# LANGUAGE ScopedTypeVariables    #-}
------------------------------------------------------------------------------
--- |
--- Module      :  Numeric.Semigroup.StrictTuple
--- Copyright   :  (c) Artem Chirkin
--- License     :  BSD3
---
--- Maintainer  :  chirkin@arch.ethz.ch
---
--- This module defines a set of tuple data types to substitute normal lazy tuples.
--- The reason is that @Monoid@ instances of normal tuples are lazy,
--- which makes folds with arithmetic operations leak memory.
--- @Semigroup@ and @Monoid@ instances of these tuples are strict in all their arguments.
---
--- Using tuple types defined here together with @Numeric.Semigroup.foldMap'@,
--- one can combine multiple monoidal fold structures in a single pass over foldable container:
---
--- >> foldMap' (T3 <$> Max <*> Sum <*> minMax) $ take 100000000 ([1..] :: [Int])
---
--- The example above runs in constant space, which would not happen with normal
---  GHC tuples due to strictness properties of their `mappend` implementations
---  (tuple arguments are not enforced).
------------------------------------------------------------------------------
-module Numeric.Semigroup.StrictTuple
-    ( T0 (..), T1 (..), T2 (..), T3 (..), T4 (..)
-    , T5 (..), T6 (..), T7 (..), T8 (..), T9 (..)
-    , StrictTuple (..)
-    , strictT0, strictT1, strictT2, strictT3, strictT4
-    , strictT5, strictT6, strictT7, strictT8, strictT9
-    ) where
-
-import           Data.Bifunctor
-import           Data.Coerce       (coerce)
-import           Data.Data
-import           GHC.Generics
-import           Numeric.Semigroup
-
-data T0 = T0
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic)
-newtype T1 a = T1 a
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
-data T2 a b = T2 a b
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
-data T3 a b c = T3 a b c
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
-data T4 a b c d = T4 a b c d
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
-data T5 a b c d e = T5 a b c d e
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
-data T6 a b c d e f = T6 a b c d e f
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
-data T7 a b c d e f g = T7 a b c d e f g
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
-data T8 a b c d e f g h = T8 a b c d e f g h
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
-data T9 a b c d e f g h i = T9 a b c d e f g h i
-  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
-
-strictT0 :: T0
-strictT0 = T0
-strictT1 :: a -> T1 a
-strictT1 !a = T1 a
-strictT2 :: a -> b -> T2 a b
-strictT2 !a !b = T2 a b
-strictT3 :: a -> b -> c -> T3 a b c
-strictT3 !a !b !c = T3 a b c
-strictT4 :: a -> b -> c -> d -> T4 a b c d
-strictT4 !a !b !c !d = T4 a b c d
-strictT5 :: a -> b -> c -> d -> e -> T5 a b c d e
-strictT5 !a !b !c !d !e = T5 a b c d e
-strictT6 :: a -> b -> c -> d -> e -> f -> T6 a b c d e f
-strictT6 !a !b !c !d !e !f = T6 a b c d e f
-strictT7 :: a -> b -> c -> d -> e -> f -> g -> T7 a b c d e f g
-strictT7 !a !b !c !d !e !f !g = T7 a b c d e f g
-strictT8 :: a -> b -> c -> d -> e -> f -> g -> h -> T8 a b c d e f g h
-strictT8 !a !b !c !d !e !f !g !h = T8 a b c d e f g h
-strictT9 :: a -> b -> c -> d -> e -> f -> g -> h -> i -> T9 a b c d e f g h i
-strictT9 !a !b !c !d !e !f !g !h !i = T9 a b c d e f g h i
-
-instance Semigroup T0 where
-    _ <> _ = T0
-instance Semigroup a => Semigroup (T1 a) where
-    (<>) = coerce ((<>) :: a -> a -> a)
-instance ( Semigroup a
-         , Semigroup b
-         ) => Semigroup (T2 a b) where
-    (T2 !ax !bx) <> (T2 !ay !by) = strictT2 (ax <> ay) (bx <> by)
-instance ( Semigroup a
-         , Semigroup b
-         , Semigroup c
-         ) => Semigroup (T3 a b c) where
-    (T3 !ax !bx !cx) <> (T3 !ay !by !cy)
-      = strictT3 (ax <> ay) (bx <> by) (cx <> cy)
-instance ( Semigroup a
-         , Semigroup b
-         , Semigroup c
-         , Semigroup d
-         ) => Semigroup (T4 a b c d) where
-    (T4 !ax !bx !cx !dx) <> (T4 !ay !by !cy !dy)
-      = strictT4 (ax <> ay) (bx <> by) (cx <> cy) (dx <> dy)
-instance ( Semigroup a
-         , Semigroup b
-         , Semigroup c
-         , Semigroup d
-         , Semigroup e
-         ) => Semigroup (T5 a b c d e) where
-    (T5 !ax !bx !cx !dx !ex) <> (T5 !ay !by !cy !dy !ey)
-      = strictT5 (ax <> ay) (bx <> by) (cx <> cy) (dx <> dy) (ex <> ey)
-instance ( Semigroup a
-         , Semigroup b
-         , Semigroup c
-         , Semigroup d
-         , Semigroup e
-         , Semigroup f
-         ) => Semigroup (T6 a b c d e f) where
-    (T6 !ax !bx !cx !dx !ex !fx) <> (T6 !ay !by !cy !dy !ey !fy)
-      = strictT6 (ax <> ay) (bx <> by) (cx <> cy) (dx <> dy) (ex <> ey) (fx <> fy)
-instance ( Semigroup a
-         , Semigroup b
-         , Semigroup c
-         , Semigroup d
-         , Semigroup e
-         , Semigroup f
-         , Semigroup g
-         ) => Semigroup (T7 a b c d e f g) where
-    (T7 !ax !bx !cx !dx !ex !fx !gx) <> (T7 !ay !by !cy !dy !ey !fy !gy)
-      = strictT7 (ax <> ay) (bx <> by) (cx <> cy) (dx <> dy) (ex <> ey)
-           (fx <> fy) (gx <> gy)
-instance ( Semigroup a
-         , Semigroup b
-         , Semigroup c
-         , Semigroup d
-         , Semigroup e
-         , Semigroup f
-         , Semigroup g
-         , Semigroup h
-         ) => Semigroup (T8 a b c d e f g h) where
-    (T8 !ax !bx !cx !dx !ex !fx !gx !hx) <> (T8 !ay !by !cy !dy !ey !fy !gy !hy)
-      = strictT8 (ax <> ay) (bx <> by) (cx <> cy) (dx <> dy) (ex <> ey)
-           (fx <> fy) (gx <> gy) (hx <> hy)
-instance ( Semigroup a
-         , Semigroup b
-         , Semigroup c
-         , Semigroup d
-         , Semigroup e
-         , Semigroup f
-         , Semigroup g
-         , Semigroup h
-         , Semigroup i
-         ) => Semigroup (T9 a b c d e f g h i) where
-    (T9 !ax !bx !cx !dx !ex !fx !gx !hx !ix) <> (T9 !ay !by !cy !dy !ey !fy !gy !hy !iy)
-      = strictT9 (ax <> ay) (bx <> by) (cx <> cy) (dx <> dy) (ex <> ey)
-           (fx <> fy) (gx <> gy) (hx <> hy) (ix <> iy)
-
-
-
-instance Monoid T0 where
-    mempty = strictT0
-    mappend T0 T0 = strictT0
-instance Monoid a => Monoid (T1 a) where
-    mempty = strictT1 mempty
-    mappend = coerce (mappend :: a -> a -> a)
-instance ( Monoid a
-         , Monoid b
-         ) => Monoid (T2 a b) where
-    mempty = strictT2 mempty mempty
-    mappend (T2 !ax !bx) (T2 !ay !by) = strictT2 (mappend ax ay) (mappend bx by)
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         ) => Monoid (T3 a b c) where
-    mempty = strictT3 mempty mempty mempty
-    mappend (T3 !ax !bx !cx) (T3 !ay !by !cy)
-      = strictT3 (mappend ax ay) (mappend bx by) (mappend cx cy)
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         ) => Monoid (T4 a b c d) where
-    mempty = strictT4 mempty mempty mempty mempty
-    mappend (T4 !ax !bx !cx !dx) (T4 !ay !by !cy !dy)
-      = strictT4 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy)
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         , Monoid e
-         ) => Monoid (T5 a b c d e) where
-    mempty = strictT5 mempty mempty mempty mempty mempty
-    mappend (T5 !ax !bx !cx !dx !ex) (T5 !ay !by !cy !dy !ey)
-      = strictT5 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         , Monoid e
-         , Monoid f
-         ) => Monoid (T6 a b c d e f) where
-    mempty = strictT6 mempty mempty mempty mempty mempty mempty
-    mappend (T6 !ax !bx !cx !dx !ex !fx) (T6 !ay !by !cy !dy !ey !fy)
-      = strictT6 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey) (mappend fx fy)
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         , Monoid e
-         , Monoid f
-         , Monoid g
-         ) => Monoid (T7 a b c d e f g) where
-    mempty = strictT7 mempty mempty mempty mempty mempty mempty mempty
-    mappend (T7 !ax !bx !cx !dx !ex !fx !gx) (T7 !ay !by !cy !dy !ey !fy !gy)
-      = strictT7 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-           (mappend fx fy) (mappend gx gy)
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         , Monoid e
-         , Monoid f
-         , Monoid g
-         , Monoid h
-         ) => Monoid (T8 a b c d e f g h) where
-    mempty = strictT8 mempty mempty mempty mempty mempty mempty mempty mempty
-    mappend (T8 !ax !bx !cx !dx !ex !fx !gx !hx) (T8 !ay !by !cy !dy !ey !fy !gy !hy)
-      = strictT8 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-           (mappend fx fy) (mappend gx gy) (mappend hx hy)
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         , Monoid e
-         , Monoid f
-         , Monoid g
-         , Monoid h
-         , Monoid i
-         ) => Monoid (T9 a b c d e f g h i) where
-    mempty = strictT9 mempty mempty mempty mempty mempty mempty mempty mempty mempty
-    mappend (T9 !ax !bx !cx !dx !ex !fx !gx !hx !ix) (T9 !ay !by !cy !dy !ey !fy !gy !hy !iy)
-      = strictT9 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-           (mappend fx fy) (mappend gx gy) (mappend hx hy) (mappend ix iy)
-
-
-instance Functor T1 where
-    fmap = coerce
-instance Functor (T2 a) where
-    fmap fun ~(T2 a b) = T2 a (fun b)
-instance Functor (T3 a b) where
-    fmap fun ~(T3 a b c) = T3 a b (fun c)
-instance Functor (T4 a b c) where
-    fmap fun ~(T4 a b c d) = T4 a b c (fun d)
-instance Functor (T5 a b c d) where
-    fmap fun ~(T5 a b c d e) = T5 a b c d (fun e)
-instance Functor (T6 a b c d e) where
-    fmap fun ~(T6 a b c d e f) = T6 a b c d e (fun f)
-instance Functor (T7 a b c d e f) where
-    fmap fun ~(T7 a b c d e f g) = T7 a b c d e f (fun g)
-instance Functor (T8 a b c d e f g) where
-    fmap fun ~(T8 a b c d e f g h) = T8 a b c d e f g (fun h)
-instance Functor (T9 a b c d e f g h) where
-    fmap fun ~(T9 a b c d e f g h i) = T9 a b c d e f g h (fun i)
-
-instance Applicative T1 where
-    pure = T1
-    (<*>) = coerce
-instance ( Monoid a
-         ) => Applicative (T2 a) where
-    pure = T2 mempty
-    (T2 !ax fun) <*> (T2 !ay val)
-      = ($ fun val)
-     <$> strictT2 (mappend ax ay) id
-instance ( Monoid a
-         , Monoid b
-         ) => Applicative (T3 a b) where
-    pure = T3 mempty mempty
-    (T3 !ax !bx fun) <*> (T3 !ay !by val)
-      = ($ fun val)
-     <$> strictT3 (mappend ax ay) (mappend bx by) id
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         ) => Applicative (T4 a b c) where
-    pure = T4 mempty mempty mempty
-    (T4 !ax !bx !cx fun) <*> (T4 !ay !by !cy val)
-      = ($ fun val)
-     <$> strictT4 (mappend ax ay) (mappend bx by) (mappend cx cy) id
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         ) => Applicative (T5 a b c d) where
-    pure = T5 mempty mempty mempty mempty
-    (T5 !ax !bx !cx !dx fun) <*> (T5 !ay !by !cy !dy val)
-      = ($ fun val)
-     <$> strictT5 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) id
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         , Monoid e
-         ) => Applicative (T6 a b c d e) where
-    pure = T6 mempty mempty mempty mempty mempty
-    (T6 !ax !bx !cx !dx !ex fun) <*> (T6 !ay !by !cy !dy !ey val)
-      = ($ fun val)
-     <$> strictT6 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey) id
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         , Monoid e
-         , Monoid f
-         ) => Applicative (T7 a b c d e f) where
-    pure = T7 mempty mempty mempty mempty mempty mempty
-    (T7 !ax !bx !cx !dx !ex !fx fun) <*> (T7 !ay !by !cy !dy !ey !fy val)
-      = ($ fun val)
-     <$> strictT7 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-                  (mappend fx fy) id
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         , Monoid e
-         , Monoid f
-         , Monoid g
-         ) => Applicative (T8 a b c d e f g) where
-    pure = T8 mempty mempty mempty mempty mempty mempty mempty
-    (T8 !ax !bx !cx !dx !ex !fx !gx fun) <*> (T8 !ay !by !cy !dy !ey !fy !gy val)
-      = ($ fun val)
-     <$> strictT8 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-                  (mappend fx fy) (mappend gx gy) id
-instance ( Monoid a
-         , Monoid b
-         , Monoid c
-         , Monoid d
-         , Monoid e
-         , Monoid f
-         , Monoid g
-         , Monoid h
-         ) => Applicative (T9 a b c d e f g h) where
-    pure = T9 mempty mempty mempty mempty mempty mempty mempty mempty
-    (T9 !ax !bx !cx !dx !ex !fx !gx !hx fun) <*> (T9 !ay !by !cy !dy !ey !fy !gy !hy val)
-      = ($ fun val)
-     <$> strictT9 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-                  (mappend fx fy) (mappend gx gy) (mappend hx hy) id
-
-
-
-instance Monad T1 where
-    m >>= k = k (coerce m)
-instance ( Monoid a
-         ) => Monad (T2 a) where
-    (T2 !ax x) >>= k = case k x of
-      (T2 !ay val) -> ($ val) <$>
-         strictT2 (mappend ax ay) id
-instance ( Monoid a, Monoid b
-         ) => Monad (T3 a b) where
-    (T3 !ax !bx x) >>= k = case k x of
-      (T3 !ay !by val) -> ($ val) <$>
-         strictT3 (mappend ax ay) (mappend bx by) id
-instance ( Monoid a, Monoid b, Monoid c
-         ) => Monad (T4 a b c) where
-    (T4 !ax !bx !cx x) >>= k = case k x of
-      (T4 !ay !by !cy val) -> ($ val) <$>
-         strictT4 (mappend ax ay) (mappend bx by) (mappend cx cy) id
-instance ( Monoid a, Monoid b, Monoid c, Monoid d
-         ) => Monad (T5 a b c d) where
-    (T5 !ax !bx !cx !dx x) >>= k = case k x of
-      (T5 !ay !by !cy !dy val) -> ($ val) <$>
-         strictT5 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) id
-instance ( Monoid a, Monoid b, Monoid c, Monoid d
-         , Monoid e
-         ) => Monad (T6 a b c d e) where
-    (T6 !ax !bx !cx !dx !ex x) >>= k = case k x of
-      (T6 !ay !by !cy !dy !ey val) -> ($ val) <$>
-         strictT6 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey) id
-instance ( Monoid a, Monoid b, Monoid c, Monoid d
-         , Monoid e, Monoid f
-         ) => Monad (T7 a b c d e f) where
-    (T7 !ax !bx !cx !dx !ex !fx x) >>= k = case k x of
-      (T7 !ay !by !cy !dy !ey !fy val) -> ($ val) <$>
-         strictT7 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-                  (mappend fx fy) id
-instance ( Monoid a, Monoid b, Monoid c, Monoid d
-         , Monoid e, Monoid f, Monoid g
-         ) => Monad (T8 a b c d e f g) where
-    (T8 !ax !bx !cx !dx !ex !fx !gx x) >>= k = case k x of
-      (T8 !ay !by !cy !dy !ey !fy !gy val) -> ($ val) <$>
-         strictT8 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-                  (mappend fx fy) (mappend gx gy) id
-instance ( Monoid a, Monoid b, Monoid c, Monoid d
-         , Monoid e, Monoid f, Monoid g, Monoid h
-         ) => Monad (T9 a b c d e f g h) where
-    (T9 !ax !bx !cx !dx !ex !fx !gx !hx x) >>= k = case k x of
-      (T9 !ay !by !cy !dy !ey !fy !gy !hy val) -> ($ val) <$>
-         strictT9 (mappend ax ay) (mappend bx by) (mappend cx cy) (mappend dx dy) (mappend ex ey)
-                  (mappend fx fy) (mappend gx gy) (mappend hx hy) id
-
-instance Bounded T0 where
-    minBound = T0
-    maxBound = T0
-instance ( Bounded a
-         ) => Bounded (T1 a) where
-    minBound = strictT1 minBound
-    maxBound = strictT1 maxBound
-instance ( Bounded a, Bounded b
-         ) => Bounded (T2 a b) where
-    minBound = strictT2 minBound minBound
-    maxBound = strictT2 maxBound maxBound
-instance ( Bounded a, Bounded b, Bounded c
-         ) => Bounded (T3 a b c) where
-    minBound = strictT3 minBound minBound minBound
-    maxBound = strictT3 maxBound maxBound maxBound
-instance ( Bounded a, Bounded b, Bounded c, Bounded d
-         ) => Bounded (T4 a b c d) where
-    minBound = strictT4 minBound minBound minBound minBound
-    maxBound = strictT4 maxBound maxBound maxBound maxBound
-instance ( Bounded a, Bounded b, Bounded c, Bounded d
-         , Bounded e
-         ) => Bounded (T5 a b c d e) where
-    minBound = strictT5 minBound minBound minBound minBound minBound
-    maxBound = strictT5 maxBound maxBound maxBound maxBound maxBound
-instance ( Bounded a, Bounded b, Bounded c, Bounded d
-         , Bounded e, Bounded f
-         ) => Bounded (T6 a b c d e f) where
-    minBound = strictT6 minBound minBound minBound minBound minBound minBound
-    maxBound = strictT6 maxBound maxBound maxBound maxBound maxBound maxBound
-instance ( Bounded a, Bounded b, Bounded c, Bounded d
-         , Bounded e, Bounded f, Bounded g
-         ) => Bounded (T7 a b c d e f g) where
-    minBound = strictT7 minBound minBound minBound minBound minBound minBound minBound
-    maxBound = strictT7 maxBound maxBound maxBound maxBound maxBound maxBound maxBound
-instance ( Bounded a, Bounded b, Bounded c, Bounded d
-         , Bounded e, Bounded f, Bounded g, Bounded h
-         ) => Bounded (T8 a b c d e f g h) where
-    minBound = strictT8 minBound minBound minBound minBound minBound minBound minBound minBound
-    maxBound = strictT8 maxBound maxBound maxBound maxBound maxBound maxBound maxBound maxBound
-instance ( Bounded a, Bounded b, Bounded c, Bounded d
-         , Bounded e, Bounded f, Bounded g, Bounded h, Bounded i
-         ) => Bounded (T9 a b c d e f g h i) where
-    minBound = strictT9 minBound minBound minBound minBound minBound minBound minBound minBound minBound
-    maxBound = strictT9 maxBound maxBound maxBound maxBound maxBound maxBound maxBound maxBound maxBound
-
-instance Foldable T1 where
-    foldMap = coerce
-    foldr f z y = f (coerce y) z
-instance Foldable (T2 a) where
-    foldMap f ~(T2 _ x) = f x
-    foldr f z ~(T2 _ x) = f x z
-    length _ = 1
-    null _ = False
-instance Foldable (T3 a b) where
-    foldMap f ~(T3 _ _ x) = f x
-    foldr f z ~(T3 _ _ x) = f x z
-    length _ = 1
-    null _ = False
-instance Foldable (T4 a b c) where
-    foldMap f ~(T4 _ _ _ x) = f x
-    foldr f z ~(T4 _ _ _ x) = f x z
-    length _ = 1
-    null _ = False
-instance Foldable (T5 a b c e) where
-    foldMap f ~(T5 _ _ _ _ x) = f x
-    foldr f z ~(T5 _ _ _ _ x) = f x z
-    length _ = 1
-    null _ = False
-instance Foldable (T6 a b c d e) where
-    foldMap f ~(T6 _ _ _ _ _ x) = f x
-    foldr f z ~(T6 _ _ _ _ _ x) = f x z
-    length _ = 1
-    null _ = False
-instance Foldable (T7 a b c d e f) where
-    foldMap f ~(T7 _ _ _ _ _ _ x) = f x
-    foldr f z ~(T7 _ _ _ _ _ _ x) = f x z
-    length _ = 1
-    null _ = False
-instance Foldable (T8 a b c d e f g) where
-    foldMap f ~(T8 _ _ _ _ _ _ _ x) = f x
-    foldr f z ~(T8 _ _ _ _ _ _ _ x) = f x z
-    length _ = 1
-    null _ = False
-instance Foldable (T9 a b c d e f g h) where
-    foldMap f ~(T9 _ _ _ _ _ _ _ _ x) = f x
-    foldr f z ~(T9 _ _ _ _ _ _ _ _ x) = f x z
-    length _ = 1
-    null _ = False
-
-instance Traversable T1 where
-    traverse f = fmap T1 . coerce f
-instance Traversable (T2 a) where
-    traverse fun ~(T2 a b) = T2 a <$> fun b
-instance Traversable (T3 a b) where
-    traverse fun ~(T3 a b c) = T3 a b <$> fun c
-instance Traversable (T4 a b c) where
-    traverse fun ~(T4 a b c d) = T4 a b c <$> fun d
-instance Traversable (T5 a b c d) where
-    traverse fun ~(T5 a b c d e) = T5 a b c d <$> fun e
-instance Traversable (T6 a b c d e) where
-    traverse fun ~(T6 a b c d e f) = T6 a b c d e <$> fun f
-instance Traversable (T7 a b c d e f) where
-    traverse fun ~(T7 a b c d e f g) = T7 a b c d e f <$> fun g
-instance Traversable (T8 a b c d e f g) where
-    traverse fun ~(T8 a b c d e f g h) = T8 a b c d e f g <$> fun h
-instance Traversable (T9 a b c d e f g h) where
-    traverse fun ~(T9 a b c d e f g h i) = T9 a b c d e f g h <$> fun i
-
-
-instance Bifunctor T2 where
-    first  funA ~(T2 a b) = T2 (funA a) b
-    second funB ~(T2 a b) = T2 a (funB b)
-    bimap  funA funB ~(T2 a b) = T2 (funA a) (funB b)
-instance Bifunctor (T3 a) where
-    first  funA ~(T3 a b c) = T3 a (funA b) c
-    second funB ~(T3 a b c) = T3 a b (funB c)
-    bimap  funA funB ~(T3 a b c) = T3 a (funA b) (funB c)
-instance Bifunctor (T4 a b) where
-    first  funA ~(T4 a b c d) = T4 a b (funA c) d
-    second funB ~(T4 a b c d) = T4 a b c (funB d)
-    bimap  funA funB ~(T4 a b c d) = T4 a b (funA c) (funB d)
-instance Bifunctor (T5 a b c) where
-    first  funA ~(T5 a b c d e) = T5 a b c (funA d) e
-    second funB ~(T5 a b c d e) = T5 a b c d (funB e)
-    bimap  funA funB ~(T5 a b c d e) = T5 a b c (funA d) (funB e)
-instance Bifunctor (T6 a b c d) where
-    first  funA ~(T6 a b c d e f) = T6 a b c d (funA e) f
-    second funB ~(T6 a b c d e f) = T6 a b c d e (funB f)
-    bimap  funA funB ~(T6 a b c d e f) = T6 a b c d (funA e) (funB f)
-instance Bifunctor (T7 a b c d e) where
-    first  funA ~(T7 a b c d e f g) = T7 a b c d e (funA f) g
-    second funB ~(T7 a b c d e f g) = T7 a b c d e f (funB g)
-    bimap  funA funB ~(T7 a b c d e f g) = T7 a b c d e (funA f) (funB g)
-instance Bifunctor (T8 a b c d e f) where
-    first  funA ~(T8 a b c d e f g h) = T8 a b c d e f (funA g) h
-    second funB ~(T8 a b c d e f g h) = T8 a b c d e f g (funB h)
-    bimap  funA funB ~(T8 a b c d e f g h) = T8 a b c d e f (funA g) (funB h)
-instance Bifunctor (T9 a b c d e f g) where
-    first  funA ~(T9 a b c d e f g h i) = T9 a b c d e f g (funA h) i
-    second funB ~(T9 a b c d e f g h i) = T9 a b c d e f g h (funB i)
-    bimap  funA funB ~(T9 a b c d e f g h i) = T9 a b c d e f g (funA h) (funB i)
-
-
-class StrictTuple a b | a -> b, b -> a where
-    toStrictTuple :: a -> b
-    fromStrictTuple :: b -> a
-
-instance StrictTuple () T0 where
-    toStrictTuple () = T0
-    fromStrictTuple T0 = ()
--- instance StrictTuple a (T1 a) where
---     toStrictTuple !a = T1 a
---     fromStrictTuple (T1 !a) = a
-instance StrictTuple (a,b) (T2 a b) where
-    toStrictTuple (!a,!b) = T2 a b
-    fromStrictTuple (T2 !a !b) = (a,b)
-instance StrictTuple (a,b,c) (T3 a b c) where
-    toStrictTuple (!a,!b,!c) = T3 a b c
-    fromStrictTuple (T3 !a !b !c) = (a,b,c)
-instance StrictTuple (a,b,c,d) (T4 a b c d) where
-    toStrictTuple (!a,!b,!c,!d) = T4 a b c d
-    fromStrictTuple (T4 !a !b !c !d) = (a,b,c,d)
-instance StrictTuple (a,b,c,d,e) (T5 a b c d e) where
-    toStrictTuple (!a,!b,!c,!d,!e) = T5 a b c d e
-    fromStrictTuple (T5 !a !b !c !d !e) = (a,b,c,d,e)
-instance StrictTuple (a,b,c,d,e,f) (T6 a b c d e f) where
-    toStrictTuple (!a,!b,!c,!d,!e,!f) = T6 a b c d e f
-    fromStrictTuple (T6 !a !b !c !d !e !f) = (a,b,c,d,e,f)
-instance StrictTuple (a,b,c,d,e,f,g) (T7 a b c d e f g) where
-    toStrictTuple (!a,!b,!c,!d,!e,!f,!g) = T7 a b c d e f g
-    fromStrictTuple (T7 !a !b !c !d !e !f !g) = (a,b,c,d,e,f,g)
-instance StrictTuple (a,b,c,d,e,f,g,h) (T8 a b c d e f g h) where
-    toStrictTuple (!a,!b,!c,!d,!e,!f,!g,!h) = T8 a b c d e f g h
-    fromStrictTuple (T8 !a !b !c !d !e !f !g !h) = (a,b,c,d,e,f,g,h)
-instance StrictTuple (a,b,c,d,e,f,g,h,i) (T9 a b c d e f g h i) where
-    toStrictTuple (!a,!b,!c,!d,!e,!f,!g,!h,!i) = T9 a b c d e f g h i
-    fromStrictTuple (T9 !a !b !c !d !e !f !g !h !i) = (a,b,c,d,e,f,g,h,i)
diff --git a/src/Numeric/Tuple.hs b/src/Numeric/Tuple.hs
new file mode 100644
--- /dev/null
+++ b/src/Numeric/Tuple.hs
@@ -0,0 +1,566 @@
+{-# LANGUAGE BangPatterns           #-}
+{-# LANGUAGE DeriveDataTypeable     #-}
+{-# LANGUAGE DeriveGeneric          #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE MultiParamTypeClasses  #-}
+{-# LANGUAGE ScopedTypeVariables    #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Numeric.Tuple
+-- Copyright   :  (c) Artem Chirkin
+-- License     :  BSD3
+--
+-- Maintainer  :  chirkin@arch.ethz.ch
+--
+-- This module defines a set of tuple data types to substitute normal Haskell tuples.
+-- The reason is that @Monoid@ instances of normal tuples are lazy,
+-- which makes folds with arithmetic operations leak memory.
+-- @Semigroup@ and @Monoid@ instances of tuples in this module are strict in all their arguments.
+--
+-- Using tuple types defined here together with @Numeric.Semigroup.foldMap'@,
+-- one can combine multiple monoidal fold structures in a single pass over a foldable container:
+--
+-- >> foldMap' (T3 <$> Max <*> Sum <*> Min) $ take 100000000 ([1..] :: [Int])
+--
+-- The example above runs in constant space, which would not happen with normal
+--  GHC tuples due to strictness properties of their `mappend` implementations
+--  (tuple arguments are not enforced).
+--
+--
+-----------------------------------------------------------------------------
+module Numeric.Tuple
+    ( T0 (..), T1 (..), T2 (..), T3 (..), T4 (..)
+    , T5 (..), T6 (..), T7 (..), T8 (..), T9 (..)
+    , AsTuple (..)
+    , foldMap'
+    ) where
+
+import           Data.Bifunctor
+import           Data.Coerce       (coerce)
+import           Data.Data
+import           GHC.Generics
+import           Numeric.Semigroup
+
+data T0 = T0
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic)
+newtype T1 a = T1 a
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
+data T2 a b = T2 a b
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
+data T3 a b c = T3 a b c
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
+data T4 a b c d = T4 a b c d
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
+data T5 a b c d e = T5 a b c d e
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
+data T6 a b c d e f = T6 a b c d e f
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
+data T7 a b c d e f g = T7 a b c d e f g
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
+data T8 a b c d e f g h = T8 a b c d e f g h
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
+data T9 a b c d e f g h i = T9 a b c d e f g h i
+  deriving (Eq, Show, Read, Ord, Data, Typeable, Generic, Generic1)
+
+
+-- | This function is exactly the same as @($!)@ defined in GHC.Base,
+--   but it is left-associative, which makes it possible to apply
+--   several arguments to one function strictly.
+(!$)   :: (a -> b) -> a -> b
+f !$ x  = let !vx = x in f vx
+infixl 1 !$
+{-# INLINE (!$) #-}
+
+instance Semigroup T0 where
+    _ <> _ = T0
+instance Semigroup a => Semigroup (T1 a) where
+    (<>) = coerce ((<>) :: a -> a -> a)
+instance ( Semigroup a
+         , Semigroup b
+         ) => Semigroup (T2 a b) where
+    (T2 !ax !bx) <> (T2 !ay !by) = T2 !$ ax <> ay !$ bx <> by
+instance ( Semigroup a
+         , Semigroup b
+         , Semigroup c
+         ) => Semigroup (T3 a b c) where
+    (T3 !ax !bx !cx) <> (T3 !ay !by !cy)
+      = T3 !$ ax <> ay !$ bx <> by !$ cx <> cy
+instance ( Semigroup a
+         , Semigroup b
+         , Semigroup c
+         , Semigroup d
+         ) => Semigroup (T4 a b c d) where
+    (T4 !ax !bx !cx !dx) <> (T4 !ay !by !cy !dy)
+      = T4 !$ ax <> ay !$ bx <> by !$ cx <> cy !$ dx <> dy
+instance ( Semigroup a
+         , Semigroup b
+         , Semigroup c
+         , Semigroup d
+         , Semigroup e
+         ) => Semigroup (T5 a b c d e) where
+    (T5 !ax !bx !cx !dx !ex) <> (T5 !ay !by !cy !dy !ey)
+      = T5 !$ ax <> ay !$ bx <> by !$ cx <> cy !$ dx <> dy !$ ex <> ey
+instance ( Semigroup a
+         , Semigroup b
+         , Semigroup c
+         , Semigroup d
+         , Semigroup e
+         , Semigroup f
+         ) => Semigroup (T6 a b c d e f) where
+    (T6 !ax !bx !cx !dx !ex !fx) <> (T6 !ay !by !cy !dy !ey !fy)
+      = T6 !$ ax <> ay !$ bx <> by !$ cx <> cy !$ dx <> dy !$ ex <> ey !$ fx <> fy
+instance ( Semigroup a
+         , Semigroup b
+         , Semigroup c
+         , Semigroup d
+         , Semigroup e
+         , Semigroup f
+         , Semigroup g
+         ) => Semigroup (T7 a b c d e f g) where
+    (T7 !ax !bx !cx !dx !ex !fx !gx) <> (T7 !ay !by !cy !dy !ey !fy !gy)
+      = T7 !$ ax <> ay !$ bx <> by !$ cx <> cy !$ dx <> dy !$ ex <> ey !$ fx <> fy !$ gx <> gy
+instance ( Semigroup a
+         , Semigroup b
+         , Semigroup c
+         , Semigroup d
+         , Semigroup e
+         , Semigroup f
+         , Semigroup g
+         , Semigroup h
+         ) => Semigroup (T8 a b c d e f g h) where
+    (T8 !ax !bx !cx !dx !ex !fx !gx !hx) <> (T8 !ay !by !cy !dy !ey !fy !gy !hy)
+      = T8 !$ ax <> ay !$ bx <> by !$ cx <> cy !$ dx <> dy !$ ex <> ey !$ fx <> fy !$ gx <> gy !$ hx <> hy
+instance ( Semigroup a
+         , Semigroup b
+         , Semigroup c
+         , Semigroup d
+         , Semigroup e
+         , Semigroup f
+         , Semigroup g
+         , Semigroup h
+         , Semigroup i
+         ) => Semigroup (T9 a b c d e f g h i) where
+    (T9 !ax !bx !cx !dx !ex !fx !gx !hx !ix) <> (T9 !ay !by !cy !dy !ey !fy !gy !hy !iy)
+      = T9 !$ ax <> ay !$ bx <> by !$ cx <> cy !$ dx <> dy !$ ex <> ey !$ fx <> fy !$ gx <> gy !$ hx <> hy !$ ix <> iy
+
+
+
+instance Monoid T0 where
+    mempty = T0
+    mappend _ _ = T0
+instance Monoid a => Monoid (T1 a) where
+    mempty = T1 !$ mempty
+    mappend = coerce (mappend :: a -> a -> a)
+instance ( Monoid a
+         , Monoid b
+         ) => Monoid (T2 a b) where
+    mempty = T2 !$ mempty !$ mempty
+    mappend (T2 !ax !bx) (T2 !ay !by) = T2 !$ mappend ax ay !$ mappend bx by
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         ) => Monoid (T3 a b c) where
+    mempty = T3 !$ mempty !$ mempty !$ mempty
+    mappend (T3 !ax !bx !cx) (T3 !ay !by !cy)
+      = T3 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         ) => Monoid (T4 a b c d) where
+    mempty = T4 !$ mempty !$ mempty !$ mempty !$ mempty
+    mappend (T4 !ax !bx !cx !dx) (T4 !ay !by !cy !dy)
+      = T4 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         , Monoid e
+         ) => Monoid (T5 a b c d e) where
+    mempty = T5 !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty
+    mappend (T5 !ax !bx !cx !dx !ex) (T5 !ay !by !cy !dy !ey)
+      = T5 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy !$ mappend ex ey
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         , Monoid e
+         , Monoid f
+         ) => Monoid (T6 a b c d e f) where
+    mempty = T6 !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty
+    mappend (T6 !ax !bx !cx !dx !ex !fx) (T6 !ay !by !cy !dy !ey !fy)
+      = T6 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy !$ mappend ex ey !$ mappend fx fy
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         , Monoid e
+         , Monoid f
+         , Monoid g
+         ) => Monoid (T7 a b c d e f g) where
+    mempty = T7 !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty
+    mappend (T7 !ax !bx !cx !dx !ex !fx !gx) (T7 !ay !by !cy !dy !ey !fy !gy)
+      = T7 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy !$ mappend ex ey
+           !$ mappend fx fy !$ mappend gx gy
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         , Monoid e
+         , Monoid f
+         , Monoid g
+         , Monoid h
+         ) => Monoid (T8 a b c d e f g h) where
+    mempty = T8 !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty
+    mappend (T8 !ax !bx !cx !dx !ex !fx !gx !hx) (T8 !ay !by !cy !dy !ey !fy !gy !hy)
+      = T8 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy !$ mappend ex ey
+           !$ mappend fx fy !$ mappend gx gy !$ mappend hx hy
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         , Monoid e
+         , Monoid f
+         , Monoid g
+         , Monoid h
+         , Monoid i
+         ) => Monoid (T9 a b c d e f g h i) where
+    mempty = T9 !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty
+    mappend (T9 !ax !bx !cx !dx !ex !fx !gx !hx !ix) (T9 !ay !by !cy !dy !ey !fy !gy !hy !iy)
+      = T9 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy !$ mappend ex ey
+           !$ mappend fx fy !$ mappend gx gy !$ mappend hx hy !$ mappend ix iy
+
+
+instance Functor T1 where
+    fmap = coerce
+instance Functor (T2 a) where
+    fmap fun ~(T2 a b) = T2 a (fun b)
+instance Functor (T3 a b) where
+    fmap fun ~(T3 a b c) = T3 a b (fun c)
+instance Functor (T4 a b c) where
+    fmap fun ~(T4 a b c d) = T4 a b c (fun d)
+instance Functor (T5 a b c d) where
+    fmap fun ~(T5 a b c d e) = T5 a b c d (fun e)
+instance Functor (T6 a b c d e) where
+    fmap fun ~(T6 a b c d e f) = T6 a b c d e (fun f)
+instance Functor (T7 a b c d e f) where
+    fmap fun ~(T7 a b c d e f g) = T7 a b c d e f (fun g)
+instance Functor (T8 a b c d e f g) where
+    fmap fun ~(T8 a b c d e f g h) = T8 a b c d e f g (fun h)
+instance Functor (T9 a b c d e f g h) where
+    fmap fun ~(T9 a b c d e f g h i) = T9 a b c d e f g h (fun i)
+
+instance Applicative T1 where
+    pure = T1
+    (<*>) = coerce
+instance ( Monoid a
+         ) => Applicative (T2 a) where
+    pure = T2 !$ mempty
+    ~(T2 !ax fun) <*> ~(T2 !ay val)
+      = T2 !$ mappend ax ay $ fun val
+instance ( Monoid a
+         , Monoid b
+         ) => Applicative (T3 a b) where
+    pure = T3 !$ mempty !$ mempty
+    ~(T3 !ax !bx fun) <*> ~(T3 !ay !by val)
+      = T3 !$ mappend ax ay !$ mappend bx by $ fun val
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         ) => Applicative (T4 a b c) where
+    pure = T4 !$ mempty !$ mempty !$ mempty
+    ~(T4 !ax !bx !cx fun) <*> ~(T4 !ay !by !cy val)
+      = T4 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy $ fun val
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         ) => Applicative (T5 a b c d) where
+    pure = T5 !$ mempty !$ mempty !$ mempty !$ mempty
+    ~(T5 !ax !bx !cx !dx fun) <*> ~(T5 !ay !by !cy !dy val)
+      = T5 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy $ fun val
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         , Monoid e
+         ) => Applicative (T6 a b c d e) where
+    pure = T6 !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty
+    ~(T6 !ax !bx !cx !dx !ex fun) <*> ~(T6 !ay !by !cy !dy !ey val)
+      = T6 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy
+           !$ mappend ex ey $ fun val
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         , Monoid e
+         , Monoid f
+         ) => Applicative (T7 a b c d e f) where
+    pure = T7 !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty
+    ~(T7 !ax !bx !cx !dx !ex !fx fun) <*> ~(T7 !ay !by !cy !dy !ey !fy val)
+      = T7 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy
+           !$ mappend ex ey !$ mappend fx fy $ fun val
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         , Monoid e
+         , Monoid f
+         , Monoid g
+         ) => Applicative (T8 a b c d e f g) where
+    pure = T8 !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty
+    ~(T8 !ax !bx !cx !dx !ex !fx !gx fun) <*> ~(T8 !ay !by !cy !dy !ey !fy !gy val)
+      = T8 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy
+           !$ mappend ex ey !$ mappend fx fy !$ mappend gx gy $ fun val
+instance ( Monoid a
+         , Monoid b
+         , Monoid c
+         , Monoid d
+         , Monoid e
+         , Monoid f
+         , Monoid g
+         , Monoid h
+         ) => Applicative (T9 a b c d e f g h) where
+    pure = T9 !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty !$ mempty
+    ~(T9 !ax !bx !cx !dx !ex !fx !gx !hx fun) <*> ~(T9 !ay !by !cy !dy !ey !fy !gy !hy val)
+      = T9 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy
+           !$ mappend ex ey !$ mappend fx fy !$ mappend gx gy !$ mappend hx hy $ fun val
+
+
+
+instance Monad T1 where
+    m >>= k = k (coerce m)
+instance ( Monoid a
+         ) => Monad (T2 a) where
+    ~(T2 !ax x) >>= k =
+        T2 !$ mappend ax ay $ val
+      where
+        ~(T2 !ay val) = k x
+instance ( Monoid a, Monoid b
+         ) => Monad (T3 a b) where
+    ~(T3 !ax !bx x) >>= k =
+        T3 !$ mappend ax ay !$ mappend bx by $ val
+      where
+        ~(T3 !ay !by val) = k x
+instance ( Monoid a, Monoid b, Monoid c
+         ) => Monad (T4 a b c) where
+    ~(T4 !ax !bx !cx x) >>= k =
+        T4 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy $ val
+      where
+        ~(T4 !ay !by !cy val) = k x
+instance ( Monoid a, Monoid b, Monoid c, Monoid d
+         ) => Monad (T5 a b c d) where
+    ~(T5 !ax !bx !cx !dx x) >>= k =
+        T5 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy $ val
+      where
+        ~(T5 !ay !by !cy !dy val) = k x
+instance ( Monoid a, Monoid b, Monoid c, Monoid d
+         , Monoid e
+         ) => Monad (T6 a b c d e) where
+    ~(T6 !ax !bx !cx !dx !ex x) >>= k =
+        T6 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy
+           !$ mappend ex ey $ val
+      where
+        ~(T6 !ay !by !cy !dy !ey val) = k x
+instance ( Monoid a, Monoid b, Monoid c, Monoid d
+         , Monoid e, Monoid f
+         ) => Monad (T7 a b c d e f) where
+    ~(T7 !ax !bx !cx !dx !ex !fx x) >>= k =
+        T7 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy
+           !$ mappend ex ey !$ mappend fx fy $ val
+      where
+        ~(T7 !ay !by !cy !dy !ey !fy val) = k x
+instance ( Monoid a, Monoid b, Monoid c, Monoid d
+         , Monoid e, Monoid f, Monoid g
+         ) => Monad (T8 a b c d e f g) where
+    ~(T8 !ax !bx !cx !dx !ex !fx !gx x) >>= k =
+        T8 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy
+           !$ mappend ex ey !$ mappend fx fy !$ mappend gx gy $ val
+      where
+        ~(T8 !ay !by !cy !dy !ey !fy !gy val) = k x
+instance ( Monoid a, Monoid b, Monoid c, Monoid d
+         , Monoid e, Monoid f, Monoid g, Monoid h
+         ) => Monad (T9 a b c d e f g h) where
+    ~(T9 !ax !bx !cx !dx !ex !fx !gx !hx x) >>= k =
+        T9 !$ mappend ax ay !$ mappend bx by !$ mappend cx cy !$ mappend dx dy
+           !$ mappend ex ey !$ mappend fx fy !$ mappend gx gy !$ mappend hx hy $ val
+      where
+        ~(T9 !ay !by !cy !dy !ey !fy !gy !hy val) = k x
+
+instance Bounded T0 where
+    minBound = T0
+    maxBound = T0
+instance ( Bounded a
+         ) => Bounded (T1 a) where
+    minBound = T1 !$ minBound
+    maxBound = T1 !$ maxBound
+instance ( Bounded a, Bounded b
+         ) => Bounded (T2 a b) where
+    minBound = T2 !$ minBound !$ minBound
+    maxBound = T2 !$ maxBound !$ maxBound
+instance ( Bounded a, Bounded b, Bounded c
+         ) => Bounded (T3 a b c) where
+    minBound = T3 !$ minBound !$ minBound !$ minBound
+    maxBound = T3 !$ maxBound !$ maxBound !$ maxBound
+instance ( Bounded a, Bounded b, Bounded c, Bounded d
+         ) => Bounded (T4 a b c d) where
+    minBound = T4 !$ minBound !$ minBound !$ minBound !$ minBound
+    maxBound = T4 !$ maxBound !$ maxBound !$ maxBound !$ maxBound
+instance ( Bounded a, Bounded b, Bounded c, Bounded d
+         , Bounded e
+         ) => Bounded (T5 a b c d e) where
+    minBound = T5 !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound
+    maxBound = T5 !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound
+instance ( Bounded a, Bounded b, Bounded c, Bounded d
+         , Bounded e, Bounded f
+         ) => Bounded (T6 a b c d e f) where
+    minBound = T6 !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound
+    maxBound = T6 !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound
+instance ( Bounded a, Bounded b, Bounded c, Bounded d
+         , Bounded e, Bounded f, Bounded g
+         ) => Bounded (T7 a b c d e f g) where
+    minBound = T7 !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound
+    maxBound = T7 !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound
+instance ( Bounded a, Bounded b, Bounded c, Bounded d
+         , Bounded e, Bounded f, Bounded g, Bounded h
+         ) => Bounded (T8 a b c d e f g h) where
+    minBound = T8 !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound
+    maxBound = T8 !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound
+instance ( Bounded a, Bounded b, Bounded c, Bounded d
+         , Bounded e, Bounded f, Bounded g, Bounded h, Bounded i
+         ) => Bounded (T9 a b c d e f g h i) where
+    minBound = T9 !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound !$ minBound
+    maxBound = T9 !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound !$ maxBound
+
+instance Foldable T1 where
+    foldMap = coerce
+    foldr f z y = f (coerce y) z
+instance Foldable (T2 a) where
+    foldMap f ~(T2 _ x) = f x
+    foldr f z ~(T2 _ x) = f x z
+    length _ = 1
+    null _ = False
+instance Foldable (T3 a b) where
+    foldMap f ~(T3 _ _ x) = f x
+    foldr f z ~(T3 _ _ x) = f x z
+    length _ = 1
+    null _ = False
+instance Foldable (T4 a b c) where
+    foldMap f ~(T4 _ _ _ x) = f x
+    foldr f z ~(T4 _ _ _ x) = f x z
+    length _ = 1
+    null _ = False
+instance Foldable (T5 a b c e) where
+    foldMap f ~(T5 _ _ _ _ x) = f x
+    foldr f z ~(T5 _ _ _ _ x) = f x z
+    length _ = 1
+    null _ = False
+instance Foldable (T6 a b c d e) where
+    foldMap f ~(T6 _ _ _ _ _ x) = f x
+    foldr f z ~(T6 _ _ _ _ _ x) = f x z
+    length _ = 1
+    null _ = False
+instance Foldable (T7 a b c d e f) where
+    foldMap f ~(T7 _ _ _ _ _ _ x) = f x
+    foldr f z ~(T7 _ _ _ _ _ _ x) = f x z
+    length _ = 1
+    null _ = False
+instance Foldable (T8 a b c d e f g) where
+    foldMap f ~(T8 _ _ _ _ _ _ _ x) = f x
+    foldr f z ~(T8 _ _ _ _ _ _ _ x) = f x z
+    length _ = 1
+    null _ = False
+instance Foldable (T9 a b c d e f g h) where
+    foldMap f ~(T9 _ _ _ _ _ _ _ _ x) = f x
+    foldr f z ~(T9 _ _ _ _ _ _ _ _ x) = f x z
+    length _ = 1
+    null _ = False
+
+instance Traversable T1 where
+    traverse f = fmap T1 . coerce f
+instance Traversable (T2 a) where
+    traverse fun ~(T2 a b) = T2 a <$> fun b
+instance Traversable (T3 a b) where
+    traverse fun ~(T3 a b c) = T3 a b <$> fun c
+instance Traversable (T4 a b c) where
+    traverse fun ~(T4 a b c d) = T4 a b c <$> fun d
+instance Traversable (T5 a b c d) where
+    traverse fun ~(T5 a b c d e) = T5 a b c d <$> fun e
+instance Traversable (T6 a b c d e) where
+    traverse fun ~(T6 a b c d e f) = T6 a b c d e <$> fun f
+instance Traversable (T7 a b c d e f) where
+    traverse fun ~(T7 a b c d e f g) = T7 a b c d e f <$> fun g
+instance Traversable (T8 a b c d e f g) where
+    traverse fun ~(T8 a b c d e f g h) = T8 a b c d e f g <$> fun h
+instance Traversable (T9 a b c d e f g h) where
+    traverse fun ~(T9 a b c d e f g h i) = T9 a b c d e f g h <$> fun i
+
+
+instance Bifunctor T2 where
+    first  funA ~(T2 a b) = T2 (funA a) b
+    second funB ~(T2 a b) = T2 a (funB b)
+    bimap  funA funB ~(T2 a b) = T2 (funA a) (funB b)
+instance Bifunctor (T3 a) where
+    first  funA ~(T3 a b c) = T3 a (funA b) c
+    second funB ~(T3 a b c) = T3 a b (funB c)
+    bimap  funA funB ~(T3 a b c) = T3 a (funA b) (funB c)
+instance Bifunctor (T4 a b) where
+    first  funA ~(T4 a b c d) = T4 a b (funA c) d
+    second funB ~(T4 a b c d) = T4 a b c (funB d)
+    bimap  funA funB ~(T4 a b c d) = T4 a b (funA c) (funB d)
+instance Bifunctor (T5 a b c) where
+    first  funA ~(T5 a b c d e) = T5 a b c (funA d) e
+    second funB ~(T5 a b c d e) = T5 a b c d (funB e)
+    bimap  funA funB ~(T5 a b c d e) = T5 a b c (funA d) (funB e)
+instance Bifunctor (T6 a b c d) where
+    first  funA ~(T6 a b c d e f) = T6 a b c d (funA e) f
+    second funB ~(T6 a b c d e f) = T6 a b c d e (funB f)
+    bimap  funA funB ~(T6 a b c d e f) = T6 a b c d (funA e) (funB f)
+instance Bifunctor (T7 a b c d e) where
+    first  funA ~(T7 a b c d e f g) = T7 a b c d e (funA f) g
+    second funB ~(T7 a b c d e f g) = T7 a b c d e f (funB g)
+    bimap  funA funB ~(T7 a b c d e f g) = T7 a b c d e (funA f) (funB g)
+instance Bifunctor (T8 a b c d e f) where
+    first  funA ~(T8 a b c d e f g h) = T8 a b c d e f (funA g) h
+    second funB ~(T8 a b c d e f g h) = T8 a b c d e f g (funB h)
+    bimap  funA funB ~(T8 a b c d e f g h) = T8 a b c d e f (funA g) (funB h)
+instance Bifunctor (T9 a b c d e f g) where
+    first  funA ~(T9 a b c d e f g h i) = T9 a b c d e f g (funA h) i
+    second funB ~(T9 a b c d e f g h i) = T9 a b c d e f g h (funB i)
+    bimap  funA funB ~(T9 a b c d e f g h i) = T9 a b c d e f g (funA h) (funB i)
+
+
+class AsTuple a b | a -> b, b -> a where
+    toTuple :: a -> b
+    fromTuple :: b -> a
+
+instance AsTuple () T0 where
+    toTuple () = T0
+    fromTuple T0 = ()
+-- instance StrictTuple a (T1 a) where
+--     toTuple a = T1 a
+--     fromTuple (T1 a) = a
+instance AsTuple (a,b) (T2 a b) where
+    toTuple (a,b) = T2 a b
+    fromTuple (T2 a b) = (a,b)
+instance AsTuple (a,b,c) (T3 a b c) where
+    toTuple (a,b,c) = T3 a b c
+    fromTuple (T3 a b c)= (a,b,c)
+instance AsTuple (a,b,c,d) (T4 a b c d) where
+    toTuple (a,b,c,d) = T4 a b c d
+    fromTuple (T4 a b c d) = (a,b,c,d)
+instance AsTuple (a,b,c,d,e) (T5 a b c d e) where
+    toTuple (a,b,c,d,e) = T5 a b c d e
+    fromTuple (T5 a b c d e) = (a,b,c,d,e)
+instance AsTuple (a,b,c,d,e,f) (T6 a b c d e f) where
+    toTuple (a,b,c,d,e,f) = T6 a b c d e f
+    fromTuple (T6 a b c d e f) = (a,b,c,d,e,f)
+instance AsTuple (a,b,c,d,e,f,g) (T7 a b c d e f g) where
+    toTuple (a,b,c,d,e,f,g) = T7 a b c d e f g
+    fromTuple (T7 a b c d e f g) = (a,b,c,d,e,f,g)
+instance AsTuple (a,b,c,d,e,f,g,h) (T8 a b c d e f g h) where
+    toTuple (a,b,c,d,e,f,g,h) = T8 a b c d e f g h
+    fromTuple (T8 a b c d e f g h) = (a,b,c,d,e,f,g,h)
+instance AsTuple (a,b,c,d,e,f,g,h,i) (T9 a b c d e f g h i) where
+    toTuple (a,b,c,d,e,f,g,h,i) = T9 a b c d e f g h i
+    fromTuple (T9 a b c d e f g h i) = (a,b,c,d,e,f,g,h,i)
