{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module TotalMap
( TotalMap()
, generateAllConstructors
, allTags
, getTotalMap
, setTotalMap
, ixTotal
, IsEnumType
) where
import Control.Lens (FoldableWithIndex (..),
FunctorWithIndex (..), Lens',
TraversableWithIndex (..), itoList, lens)
import Data.Distributive (Distributive (..))
import Data.Functor.Classes (Eq1 (..), Show1 (..))
import Data.Functor.Rep (Representable (..))
import Data.List (intercalate)
import Generics.SOP
generateAllConstructors :: IsEnumType tag => NP (K tag) (Code tag)
generateAllConstructors = hliftA2 aux (hcpure (Proxy :: Proxy SListI) $ hpure undefined) injections
where
aux np (Fn inj) = K (to (SOP $ unK $ inj np))
data TotalMap (tag :: *) (a :: *) where
TotalMap :: (IsEnumType tag) => NP (K a) (Code tag) -> TotalMap tag a
allTags :: IsEnumType tag => TotalMap tag tag
allTags = TotalMap generateAllConstructors
instance Functor (TotalMap tag) where
fmap f (TotalMap np) = TotalMap $ hliftA (mapKK f) np
instance IsEnumType tag => FunctorWithIndex tag (TotalMap tag) where
imap f tm = f <$> allTags <*> tm
instance (IsEnumType tag) => Applicative (TotalMap tag) where
pure a = TotalMap $ hpure (K a)
TotalMap a <*> TotalMap b = TotalMap $ hliftA2 (mapKKK ($)) a b
instance IsEnumType tag => Monad (TotalMap tag) where
tm >>= f = imap (\tag a -> getTotalMap (f a) tag) tm
instance Foldable (TotalMap tag) where
foldMap f (TotalMap np) = foldMap f $ hcollapse np
instance IsEnumType tag => FoldableWithIndex tag (TotalMap tag) where
ifoldMap f tm = foldMap (uncurry f) ((,) <$> allTags <*> tm)
instance Traversable (TotalMap tag) where
sequenceA (TotalMap np) = TotalMap <$> hsequenceK np
instance IsEnumType tag => TraversableWithIndex tag (TotalMap tag) where
itraverse func tm = traverse (uncurry func) ( (,) <$> allTags <*> tm)
instance IsEnumType tag => Distributive (TotalMap tag) where
distribute = imap (\tag -> fmap (\tm -> getTotalMap tm tag)) . pure
instance IsEnumType tag => Representable (TotalMap tag) where
type Rep (TotalMap tag) = tag
index = getTotalMap
tabulate func = TotalMap $ hmap (mapKK func) generateAllConstructors
instance (IsEnumType tag) => Eq1 (TotalMap tag) where
liftEq f a b = foldr (&&) True (f <$> a <*> b)
instance (IsEnumType tag, Eq a) => Eq (TotalMap tag a) where
(==) = liftEq (==)
instance (IsEnumType tag, Show tag) => Show1 (TotalMap tag) where
liftShowsPrec f _ n tm ss =
"TotalMap [" ++
intercalate
", "
(map (\(t, a) -> "(" ++ show t ++ "," ++ f n a "" ++ ")") $
itoList tm) ++
"]" ++ ss
instance (IsEnumType tag, Show a, Show tag) => Show (TotalMap tag a) where
showsPrec n = liftShowsPrec showsPrec undefined n
getTotalMap :: TotalMap tag a -> tag -> a
getTotalMap (TotalMap tm) a = hcollapse (hapInjs tm !! (hindex $ from a))
setTotalMap ::
forall tag a. IsEnumType tag
=> TotalMap tag a
-> tag
-> a
-> TotalMap tag a
setTotalMap (TotalMap tm) tag a =
let helper :: NP (K a) xss -> NS (NP f) xss -> NP (K a) xss
helper (k :* as) (S z) = k :* helper as z
helper (_ :* as) (Z _) = (K a) :* as
helper Nil _ = error "Unreachable"
in TotalMap (helper tm (unSOP $ from tag))
ixTotal :: IsEnumType tag => tag -> Lens' (TotalMap tag a) a
ixTotal tag = lens (\tm -> getTotalMap tm tag) (\tm -> setTotalMap tm tag)