symantic-base-0.5.0.20221211: src/Symantic/Syntaxes/Data.hs
{-# LANGUAGE ConstraintKinds #-}
-- For Semantic
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ViewPatterns #-}
module Symantic.Syntaxes.Data where
import Data.Bool (Bool)
import Data.Either (Either)
import Data.Eq qualified as Eq
import Data.Function qualified as Fun
import Data.Maybe (Maybe)
import Data.Maybe qualified as Maybe
import Data.Semigroup (Semigroup)
import Type.Reflection (Typeable, eqTypeRep, typeRep, (:~~:) (..))
import Symantic.Syntaxes.Classes
import Symantic.Syntaxes.Derive
-- * Type 'SomeData'
data SomeData sem a
= forall able.
( Derivable (Data able sem)
, Typeable able
) =>
SomeData (Data able sem a)
type instance Derived (SomeData sem) = sem
instance Derivable (SomeData sem) where
derive (SomeData x) = derive x
-- ** Type 'Data'
-- TODO: neither data families nor data instances
-- can have phantom roles with GHC-9's RoleAnnotations,
-- hence 'Data.Coerce.coerce' cannot be used on them for now.
-- https://gitlab.haskell.org/ghc/ghc/-/issues/8177
-- https://gitlab.haskell.org/ghc/ghc/-/wikis/roles#proposal-roles-for-type-families
data family Data (able :: Syntax) :: Semantic -> Semantic
type instance Derived (Data able sem) = sem
-- | Convenient utility to pattern-match a 'SomeData'.
pattern Data :: Typeable able => Data able sem a -> SomeData sem a
pattern Data x <- (unSomeData -> Maybe.Just x)
-- | @(unSomeData c :: 'Maybe' ('Data' able sem a))@
-- extract the data-constructor from the given 'SomeData'
-- iif. it belongs to the @('Data' able sem a)@ data-instance.
unSomeData ::
forall able sem a.
Typeable able =>
SomeData sem a ->
Maybe (Data able sem a)
unSomeData (SomeData (c :: Data c sem a)) =
case typeRep @able `eqTypeRep` typeRep @c of
Maybe.Just HRefl -> Maybe.Just c
Maybe.Nothing -> Maybe.Nothing
-- Abstractable
data instance Data Abstractable sem a where
Lam :: (SomeData sem a -> SomeData sem b) -> Data Abstractable sem (a -> b)
Lam1 :: (SomeData sem a -> SomeData sem b) -> Data Abstractable sem (a -> b)
Var :: sem a -> Data Abstractable sem a
instance Abstractable sem => Derivable (Data Abstractable sem) where
derive = \case
Lam f -> lam (\x -> derive (f (SomeData (Var x))))
Lam1 f -> lam1 (\x -> derive (f (SomeData (Var x))))
Var x -> var x
instance Abstractable sem => Abstractable (SomeData sem) where
lam f = SomeData (Lam f)
lam1 f = SomeData (Lam1 f)
var = Fun.id
-- | Beta-reduce the left-most outer-most lambda abstraction (aka. normal-order reduction),
-- but to avoid duplication of work, only those manually marked
-- as using their variable at most once.
--
-- DOC: Demonstrating Lambda Calculus Reduction, Peter Sestoft, 2001,
-- https://www.itu.dk/people/sestoft/papers/sestoft-lamreduce.pdf
normalOrderReduction ::
forall sem a.
Abstractable sem =>
IfThenElseable sem =>
SomeData sem a ->
SomeData sem a
normalOrderReduction = nor
where
nor :: SomeData sem b -> SomeData sem b
nor = \case
Data (Lam f) -> lam (nor Fun.. f)
Data (Lam1 f) -> lam1 (nor Fun.. f)
Data (x :@ y) -> case whnf x of
Data (Lam1 f) -> nor (f y)
x' -> nor x' .@ nor y
Data (IfThenElse test ok ko) ->
case nor test of
Data (Constant b :: Data (Constantable Bool) sem Bool) ->
if b then nor ok else nor ko
t -> ifThenElse (nor t) (nor ok) (nor ko)
x -> x
whnf :: SomeData sem b -> SomeData sem b
whnf = \case
Data (x :@ y) -> case whnf x of
Data (Lam1 f) -> whnf (f y)
x' -> x' .@ y
x -> x
-- Unabstractable
data instance Data Unabstractable sem a where
(:@) :: SomeData sem (a -> b) -> SomeData sem a -> Data Unabstractable sem b
instance Unabstractable sem => Derivable (Data Unabstractable sem) where
derive = \case
f :@ x -> derive f .@ derive x
instance Unabstractable sem => Unabstractable (SomeData sem) where
f .@ x = SomeData (f :@ x)
-- Functionable
instance
( Abstractable sem
) =>
Functionable (SomeData sem)
where
($) = lam1 (\f -> lam1 (\x -> f .@ x))
(.) = lam1 (\f -> lam1 (\g -> lam1 (\x -> f .@ (g .@ x))))
const = lam1 (\x -> lam1 (\_y -> x))
flip = lam1 (\f -> lam1 (\x -> lam1 (\y -> f .@ y .@ x)))
id = lam1 (\x -> x)
-- Anythingable
data instance Data Anythingable sem a where
Anything :: sem a -> Data Anythingable sem a
instance
( Anythingable sem
) =>
Derivable (Data Anythingable sem)
where
derive = \case
Anything x -> anything x
instance Anythingable (SomeData sem)
instance Anythingable (Data Anythingable sem)
-- Bottomable
data instance Data Bottomable sem a where
Bottom :: Data Bottomable sem a
instance Bottomable sem => Derivable (Data Bottomable sem) where
derive Bottom{} = bottom
-- Constantable
data instance Data (Constantable c) sem a where
Constant {-Typeable c =>-} :: c -> Data (Constantable c) sem c
instance Constantable c sem => Derivable (Data (Constantable c) sem) where
derive = \case
Constant x -> constant x
instance
( Constantable c sem
, Typeable c
) =>
Constantable c (SomeData sem)
where
constant c = SomeData (Constant c)
instance{-Typeable c =>-} Constantable c (Data (Constantable c) sem) where
constant = Constant
-- Eitherable
data instance Data Eitherable sem a where
Either :: Data Eitherable sem ((l -> a) -> (r -> a) -> Either l r -> a)
Left :: Data Eitherable sem (l -> Either l r)
Right :: Data Eitherable sem (r -> Either l r)
instance Eitherable sem => Derivable (Data Eitherable sem) where
derive = \case
Either -> either
Left -> left
Right -> right
instance Eitherable sem => Eitherable (SomeData sem) where
either = SomeData Either
left = SomeData Left
right = SomeData Right
instance Eitherable (Data Eitherable sem) where
either = Either
left = Left
right = Right
-- Equalable
data instance Data Equalable sem a where
Equal :: Eq.Eq a => Data Equalable sem (a -> a -> Bool)
instance Equalable sem => Derivable (Data Equalable sem) where
derive = \case
Equal -> equal
instance
( Equalable sem
) =>
Equalable (SomeData sem)
where
equal = SomeData Equal
instance Equalable (Data Equalable sem) where
equal = Equal
-- Emptyable
data instance Data Emptyable sem a where
Empty :: Data Emptyable sem a
instance Emptyable sem => Derivable (Data Emptyable sem) where
derive = \case
Empty -> empty
instance
( Emptyable sem
) =>
Emptyable (SomeData sem)
where
empty = SomeData Empty
instance Emptyable (Data Emptyable sem) where
empty = Empty
-- Semigroupable
data instance Data Semigroupable sem a where
Concat :: Semigroup a => Data Semigroupable sem (a -> a -> a)
infixr 4 `Concat`
instance Semigroupable sem => Derivable (Data Semigroupable sem) where
derive = \case
Concat -> concat
instance
( Semigroupable sem
) =>
Semigroupable (SomeData sem)
where
concat = SomeData Concat
instance Semigroupable (Data Semigroupable sem) where
concat = Concat
-- IfThenElseable
data instance Data IfThenElseable sem a where
IfThenElse ::
SomeData sem Bool ->
SomeData sem a ->
SomeData sem a ->
Data IfThenElseable sem a
instance IfThenElseable sem => Derivable (Data IfThenElseable sem) where
derive = \case
IfThenElse test ok ko -> ifThenElse (derive test) (derive ok) (derive ko)
instance
( IfThenElseable sem
) =>
IfThenElseable (SomeData sem)
where
ifThenElse test ok ko = SomeData (IfThenElse test ok ko)
instance IfThenElseable sem => IfThenElseable (Data IfThenElseable sem) where
ifThenElse test ok ko = IfThenElse (SomeData test) (SomeData ok) (SomeData ko)
-- Listable
data instance Data Listable sem a where
Cons :: Data Listable sem (a -> [a] -> [a])
Nil :: Data Listable sem [a]
infixr 4 `Cons`
instance Listable sem => Derivable (Data Listable sem) where
derive = \case
Cons -> cons
Nil -> nil
instance
( Listable sem
) =>
Listable (SomeData sem)
where
cons = SomeData Cons
nil = SomeData Nil
instance Listable (Data Listable sem) where
cons = Cons
nil = Nil
-- Maybeable
data instance Data Maybeable sem a where
Nothing :: Data Maybeable sem (Maybe a)
Just :: Data Maybeable sem (a -> Maybe a)
instance Maybeable sem => Derivable (Data Maybeable sem) where
derive = \case
Nothing -> nothing
Just -> just
instance
( Maybeable sem
) =>
Maybeable (SomeData sem)
where
nothing = SomeData Nothing
just = SomeData Just
instance Maybeable (Data Maybeable sem) where
nothing = Nothing
just = Just