linnet-0.1.0.0: src/Linnet/Internal/HList.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Linnet.Internal.HList
( HList(..)
, AdjoinHList(..)
, FnToProduct(..)
) where
infixr 6 :::
data HList xs where
HNil :: HList '[]
(:::) :: a -> HList as -> HList (a ': as)
class Prepend a b ab | a b -> ab where
prependHList :: HList a -> HList b -> HList ab
instance (Prepend' (PrependT a b) a b ab) => Prepend a b ab where
prependHList = prependHList' @(PrependT a b)
class Prepend' (lists :: PrependHList) a b ab | a b -> ab where
prependHList' :: HList a -> HList b -> HList ab
data PrependHList
= LeftHNil
| RightHNil
| BothHNil
| NoneHNil
type family PrependT a b where
PrependT '[] '[] = 'BothHNil
PrependT _ '[] = 'RightHNil
PrependT '[] _ = 'LeftHNil
PrependT _ _ = 'NoneHNil
instance Prepend' 'BothHNil '[] '[] '[] where
prependHList' _ _ = HNil
instance Prepend' 'LeftHNil '[] b b where
prependHList' _ b = b
instance Prepend' 'RightHNil a '[] a where
prependHList' a _ = a
instance (Prepend as (b ': bs) cs) => Prepend' 'NoneHNil (a ': as) (b ': bs) (a ': cs) where
prependHList' (a ::: as) (b ::: bs) = a ::: prependHList as (b ::: bs)
class AdjoinHList ls l | ls -> l where
adjoin :: HList ls -> HList l
instance AdjoinHList' (NeedAdjoin ls) ls l => AdjoinHList ls l where
adjoin = adjoin' @(NeedAdjoin ls)
class AdjoinHList' (needAdjoin :: Bool) ls l | ls -> l where
adjoin' :: HList ls -> HList l
instance AdjoinHList' 'False '[] '[] where
adjoin' _ = HNil
instance (AdjoinHList t out) => AdjoinHList' 'False (h ': t) (h ': out) where
adjoin' (h ::: t) = h ::: adjoin t
instance (AdjoinHList t ao, Prepend a ao po) => AdjoinHList' 'True (HList a ': t) po where
adjoin' (l ::: t) = prependHList l (adjoin t)
type family NeedAdjoin (l :: [*]) :: Bool where
NeedAdjoin (HList a ': t) = 'True
NeedAdjoin '[] = 'False
NeedAdjoin (a ': t) = 'False
instance Show (HList '[]) where
show HNil = "HNil"
instance (Show a, Show (HList as)) => Show (HList (a ': as)) where
show (a ::: as) = show a ++ " ::: " ++ show as
instance Eq (HList '[]) where
(==) HNil HNil = True
instance (Eq a, Eq (HList as)) => Eq (HList (a ': as)) where
(==) (a ::: as) (a' ::: as') = a == a' && as == as'
class FnToProduct fn ls out | fn ls -> out, ls out -> fn where
fromFunction :: fn -> HList ls -> out
instance (v ~ fn) => FnToProduct fn '[] v where
fromFunction v HNil = v
instance (FnToProduct fnOut tail out) => FnToProduct (input -> fnOut) (input ': tail) out where
fromFunction fn (input ::: t) = fromFunction (fn input) t