{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE RankNTypes #-}
-- | Extensible ADT
module Haskus.Utils.EADT
( EADT
, (:<:)
, (:<<:)
, pattern VF
, appendEADT
, liftEADT
, popEADT
, AlterEADT
, alterEADT
, AlgEADT
, algEADT
, eadtToCont
, eadtToContM
, contToEADT
, contToEADTM
-- * Reexport
, module Haskus.Utils.Functor
, module Haskus.Utils.VariantF
)
where
import Haskus.Utils.VariantF
import Haskus.Utils.Variant
import Haskus.Utils.Functor
import Haskus.Utils.Types.List
import Haskus.Utils.Types
import Haskus.Utils.ContFlow
import GHC.Exts (Constraint)
-- | An extensible ADT
type EADT xs = Fix (VariantF xs)
-- | Constructor `f` is in `xs`
type family f :<: xs where
f :<: xs = EADTF' f (EADT xs) xs
-- | Forall `x` in `xs`, `x :<: ys`
type family (:<<:) xs ys :: Constraint where
'[] :<<: ys = ()
(x ': xs) :<<: ys = (x :<: ys, xs :<<: ys)
type EADTF' f e cs =
( Member f cs
, Index (IndexOf (f e) (ApplyAll e cs)) (ApplyAll e cs) ~ f e
, PopVariant (f e) (ApplyAll e cs)
, KnownNat (IndexOf (f e) (ApplyAll e cs))
, Remove (f e) (ApplyAll e cs) ~ ApplyAll e (Remove f cs)
)
-- | Pattern-match in an extensible ADT
pattern VF :: forall e f cs.
( e ~ EADT cs -- allow easy use of TypeApplication to set the EADT type
, f :<: cs -- constraint synonym ensuring `f` is in `cs`
) => f (EADT cs) -> EADT cs
pattern VF x = Fix (VariantF (VSilent x))
-- `VSilent` matches a variant value without checking the membership: we
-- already do it with :<:
-- | Append new "constructors" to the EADT
appendEADT :: forall ys xs zs.
( zs ~ Concat xs ys
, ApplyAll (EADT zs) zs ~ Concat (ApplyAll (EADT zs) xs) (ApplyAll (EADT zs) ys)
, Functor (VariantF xs)
) => EADT xs -> EADT zs
appendEADT (Fix v) = Fix (appendVariantF @ys (fmap (appendEADT @ys) v))
-- | Lift an EADT into another
liftEADT :: forall e as bs.
( e ~ Fix (VariantF bs)
, LiftVariantF as bs e
, Functor (VariantF as)
) => EADT as -> EADT bs
liftEADT = cata (Fix . liftVariantF)
-- | Pop an EADT value
popEADT :: forall f xs e.
( f :<: xs
, e ~ EADT xs
, f e :< ApplyAll e xs
) => EADT xs -> Either (VariantF (Remove f xs) (EADT xs)) (f (EADT xs))
popEADT (Fix v) = popVariantF v
type AlterEADT c xs = AlterVariantF c (EADT xs) xs
-- | Alter an EADT value
alterEADT :: forall c xs.
( AlterEADT c xs
) => (forall f. c f => f (EADT xs) -> f (EADT xs)) -> EADT xs -> EADT xs
alterEADT f (Fix v) = Fix (alterVariantF @c @(EADT xs) f v)
type AlgEADT c xs = AlgVariantF c (EADT xs) xs
-- | Apply an algebra to an EADT value
algEADT :: forall c xs.
( AlgEADT c xs
) => (forall f. c f => f (EADT xs) -> EADT xs) -> EADT xs -> EADT xs
algEADT f (Fix v) = algVariantF @c @(EADT xs) f v
-- | Convert an EADT into a multi-continuation
eadtToCont ::
( ContVariant (ApplyAll (Fix (VariantF xs)) xs)
) => Fix (VariantF xs) -> ContFlow (ApplyAll (Fix (VariantF xs)) xs) r
eadtToCont (Fix v) = variantFToCont v
-- | Convert an EADT into a multi-continuation
eadtToContM ::
( ContVariant (ApplyAll (Fix (VariantF xs)) xs)
, Monad m
) => m (Fix (VariantF xs))
-> ContFlow (ApplyAll (Fix (VariantF xs)) xs) (m r)
eadtToContM f = variantFToContM (unfix <$> f)
-- Orphan instance...
-- instance ContVariant (ApplyAll (EADT xs) xs) => MultiCont (EADT xs) where
-- type MultiContTypes (EADT xs) = ApplyAll (EADT xs) xs
-- toCont = eadtToCont
-- toContM = eadtToContM
-- | Convert a multi-continuation into an EADT
contToEADT ::
( ContVariant (ApplyAll (Fix (VariantF xs)) xs)
) => ContFlow (ApplyAll (Fix (VariantF xs)) xs)
(V (ApplyAll (Fix (VariantF xs)) xs))
-> Fix (VariantF xs)
contToEADT c = Fix (contToVariantF c)
-- | Convert a multi-continuation into an EADT
contToEADTM ::
( ContVariant (ApplyAll (Fix (VariantF xs)) xs)
, Monad f
) => ContFlow (ApplyAll (Fix (VariantF xs)) xs)
(f (V (ApplyAll (Fix (VariantF xs)) xs)))
-> f (Fix (VariantF xs))
contToEADTM f = Fix <$> contToVariantFM f