{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Fin.Enum (
Enum (..),
gfrom, GFrom,
gto, GTo,
GEnumSize,
) where
import Prelude hiding (Enum (..))
import Data.Bifunctor (bimap)
import Data.Fin (Fin (..))
import Data.Nat (Nat (..))
import Data.Proxy (Proxy (..))
import GHC.Generics ((:+:) (..), M1 (..), U1 (..), V1)
import qualified Data.Fin as F
import qualified Data.Type.Nat as N
import qualified Data.Void as V
import qualified GHC.Generics as G
class Enum a where
type EnumSize a :: Nat
type EnumSize a = GEnumSize a
from :: a -> Fin (EnumSize a)
default from :: (G.Generic a, GFrom a, EnumSize a ~ GEnumSize a) => a -> Fin (EnumSize a)
from = gfrom
to :: Fin (EnumSize a) -> a
default to :: (G.Generic a, GTo a, EnumSize a ~ GEnumSize a) => Fin (EnumSize a) -> a
to = gto
instance Enum V.Void where
type EnumSize V.Void = N.Nat0
from = V.absurd
to = F.absurd
instance Enum ()
instance Enum Bool
instance Enum Ordering
instance (Enum a, Enum b, N.InlineInduction (EnumSize a)) => Enum (Either a b) where
type EnumSize (Either a b) = N.Plus (EnumSize a) (EnumSize b)
to = bimap to to . F.split
from = F.append . bimap from from
type GEnumSize a = EnumSizeRep (G.Rep a) N.Nat0
type family EnumSizeRep (a :: * -> *) (n :: Nat) :: Nat where
EnumSizeRep (a :+: b ) n = EnumSizeRep a (EnumSizeRep b n)
EnumSizeRep V1 n = n
EnumSizeRep (M1 _d _c a) n = EnumSizeRep a n
EnumSizeRep U1 n = 'S n
gfrom :: (G.Generic a, GFrom a) => a -> Fin (GEnumSize a)
gfrom = \x -> gfromRep (G.from x) (error "gfrom: internal error" :: Fin N.Nat0)
type GFrom a = GFromRep (G.Rep a)
class GFromRep (a :: * -> *) where
gfromRep :: a x -> Fin n -> Fin (EnumSizeRep a n)
gfromSkip :: Proxy a -> Fin n -> Fin (EnumSizeRep a n)
instance (GFromRep a, GFromRep b) => GFromRep (a :+: b) where
gfromRep (L1 a) n = gfromRep a (gfromSkip (Proxy :: Proxy b) n)
gfromRep (R1 b) n = gfromSkip (Proxy :: Proxy a) (gfromRep b n)
gfromSkip _ n = gfromSkip (Proxy :: Proxy a) (gfromSkip (Proxy :: Proxy b) n)
instance GFromRep a => GFromRep (M1 d c a) where
gfromRep (M1 a) n = gfromRep a n
gfromSkip _ n = gfromSkip (Proxy :: Proxy a) n
instance GFromRep V1 where
gfromRep _ n = n
gfromSkip _ n = n
instance GFromRep U1 where
gfromRep U1 _ = FZ
gfromSkip _ n = FS n
gto :: (G.Generic a, GTo a) => Fin (GEnumSize a) -> a
gto = \x -> G.to $ gtoRep x id $ F.absurd
type GTo a = GToRep (G.Rep a)
class GToRep (a :: * -> *) where
gtoRep :: Fin (EnumSizeRep a n) -> (a x -> r) -> (Fin n -> r) -> r
instance (GToRep a, GToRep b) => GToRep (a :+: b) where
gtoRep n s k = gtoRep n (s . L1) $ \r -> gtoRep r (s . R1) k
instance GToRep a => GToRep (M1 d c a) where
gtoRep n s = gtoRep n (s . M1)
instance GToRep V1 where
gtoRep n _ k = k n
instance GToRep U1 where
gtoRep FZ s _ = s U1
gtoRep (FS n) _ k = k n