{-# 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)