{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Dino.Interpretation where
import Dino.Prelude
import qualified Prelude
import Control.Exception (Exception, throw)
import Control.Monad (foldM, unless)
import Control.Monad.Catch (MonadThrow (..))
import Control.Monad.Except (ExceptT, MonadError (..))
import Control.Monad.Identity (Identity (..))
import Control.Monad.Loops (dropWhileM)
import Control.Monad.Reader (MonadReader (..), ReaderT (..))
import Control.Monad.Writer (WriterT)
import Data.Coerce (coerce)
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Text as Text
import Data.Proxy (Proxy (..))
import Data.Ratio (denominator, numerator)
import GHC.TypeLits (symbolVal)
import Dino.AST
import Dino.Types
import Dino.Expression
instance ConstExp DinoTypeRep where
lit _ = dinoTypeRep
instance NumExp DinoTypeRep where
add t _ = t
sub t _ = t
mul t _ = t
absE t = t
signE t = t
fromIntegral _ = dinoTypeRep
floor _ = dinoTypeRep
truncate _ = dinoTypeRep
roundN _ t = t
instance FracExp DinoTypeRep where
fdiv t _ = t
instance LogicExp DinoTypeRep where
not _ = dinoTypeRep
conj _ _ = dinoTypeRep
disj _ _ = dinoTypeRep
xor _ _ = dinoTypeRep
instance CompareExp DinoTypeRep where
eq _ _ = dinoTypeRep
neq _ _ = dinoTypeRep
lt _ _ = dinoTypeRep
gt _ _ = dinoTypeRep
lte _ _ = dinoTypeRep
gte _ _ = dinoTypeRep
min t _ = t
max t _ = t
instance CondExpFO DinoTypeRep where
just t = withType t OtherType
cases _ (_ :-> t) = t
partial_cases = default_partial_cases
instance CondExp DinoTypeRep where
maybe t _ _ = t
instance ListExpFO DinoTypeRep where
range t _ = ListType t
list _ = dinoTypeRep
headE = (\t -> withType t OtherType) . listTypeElem
append t _ = t
instance ListExp DinoTypeRep where
mapE f = ListType . f . listTypeElem
dropWhileE _ = id
foldE _ t _ = t
instance TupleExp DinoTypeRep where
pair = PairType
fstE (PairType t _) = t
sndE (PairType _ t) = t
instance LetExp DinoTypeRep where
letE _ t body = body t
instance FieldExp DinoTypeRep where
getField _ _ = dinoTypeRep
instance AnnExp ann DinoTypeRep
instance AssertExp DinoTypeRep
instance ConstExp Identity
instance NumExp Identity
instance FracExp Identity
instance LogicExp Identity
instance CompareExp Identity
instance CondExpFO Identity
instance CondExp Identity
instance ListExpFO Identity
instance ListExp Identity
instance TupleExp Identity
instance LetExp Identity
instance FieldExp Identity
instance AnnExp ann Identity
instance AssertExp Identity
instance ConstExp Maybe
instance NumExp Maybe
instance FracExp Maybe
instance LogicExp Maybe
instance CompareExp Maybe
instance CondExpFO Maybe
instance CondExp Maybe
instance ListExpFO Maybe
instance ListExp Maybe
instance TupleExp Maybe
instance LetExp Maybe
instance FieldExp Maybe
instance AnnExp ann Maybe
instance AssertExp Maybe
instance ConstExp (Either e)
instance NumExp (Either e)
instance FracExp (Either e)
instance LogicExp (Either e)
instance CompareExp (Either e)
instance CondExpFO (Either e)
instance CondExp (Either e)
instance ListExpFO (Either e)
instance ListExp (Either e)
instance TupleExp (Either e)
instance LetExp (Either e)
instance FieldExp (Either e)
instance AnnExp ann (Either e)
instance AssertExp (Either e)
instance Monad m => ConstExp (ExceptT e m)
instance Monad m => NumExp (ExceptT e m)
instance Monad m => FracExp (ExceptT e m)
instance Monad m => LogicExp (ExceptT e m)
instance Monad m => CompareExp (ExceptT e m)
instance Monad m => CondExpFO (ExceptT e m)
instance Monad m => CondExp (ExceptT e m)
instance Monad m => ListExpFO (ExceptT e m)
instance Monad m => ListExp (ExceptT e m)
instance Monad m => TupleExp (ExceptT e m)
instance Monad m => LetExp (ExceptT e m)
instance Monad m => FieldExp (ExceptT e m)
instance AnnExp ann (ExceptT e m)
instance AssertExp (ExceptT e m)
instance Applicative m => ConstExp (ReaderT env m)
instance Applicative m => NumExp (ReaderT env m)
instance Applicative m => FracExp (ReaderT env m)
instance Applicative m => LogicExp (ReaderT env m)
instance Applicative m => CompareExp (ReaderT env m)
instance Monad m => CondExpFO (ReaderT env m)
instance Monad m => CondExp (ReaderT env m)
instance Monad m => ListExpFO (ReaderT env m)
instance Monad m => ListExp (ReaderT env m)
instance Monad m => TupleExp (ReaderT env m)
instance Monad m => LetExp (ReaderT env m)
instance Monad m => FieldExp (ReaderT env m)
instance AnnExp ann (ReaderT env m)
instance AssertExp (ReaderT env m)
instance (Monoid t, Applicative m) => ConstExp (WriterT t m)
instance (Monoid t, Applicative m) => NumExp (WriterT t m)
instance (Monoid t, Applicative m) => FracExp (WriterT t m)
instance (Monoid t, Applicative m) => LogicExp (WriterT t m)
instance (Monoid t, Applicative m) => CompareExp (WriterT t m)
instance (Monoid t, Monad m) => CondExpFO (WriterT t m)
instance (Monoid t, Monad m) => CondExp (WriterT t m)
instance (Monoid t, Monad m) => ListExpFO (WriterT t m)
instance (Monoid t, Monad m) => ListExp (WriterT t m)
instance (Monoid t, Monad m) => TupleExp (WriterT t m)
instance (Monoid t, Monad m) => LetExp (WriterT t m)
instance (Monoid t, Monad m) => FieldExp (WriterT t m)
instance AnnExp ann (WriterT t m)
instance AssertExp (WriterT t m)
eval :: Exp Identity a -> a
eval = coerce
evalF :: Exp f a -> f a
evalF = coerce
newtype Fold e a = Fold {fold :: e}
deriving (Eq, Show, Functor, Semigroup, Monoid)
instance Monoid e => Applicative (Fold e) where
pure = mempty
f <*> a = Fold (fold f <> fold a)
class FoldN f e | f -> e where
foldN :: e -> (e -> e -> e) -> f
instance FoldN (Fold e a) e where
foldN e0 _ = Fold e0
instance FoldN f e => FoldN (Fold e a -> f) e where
foldN e0 conc = \(Fold e) -> foldN (conc e0 e) conc
foldMonoid :: (FoldN f e, Monoid e) => f
foldMonoid = foldN mempty mappend
instance Monoid e => ConstExp (Fold e)
instance Monoid e => NumExp (Fold e)
instance Monoid e => FracExp (Fold e)
instance Monoid e => LogicExp (Fold e)
instance Monoid e => CompareExp (Fold e)
instance Monoid e => ListExpFO (Fold e)
instance Monoid e => TupleExp (Fold e)
instance Monoid e => FieldExp (Fold e)
instance AnnExp ann (Fold e)
instance Monoid e => CondExpFO (Fold e) where
cases cs (Otherwise :-> d) =
Fold $ mconcat $ concat [[fold c, fold a] | (c :-> a) <- cs] ++ [fold d]
partial_cases cs =
Fold $ mconcat $ concat [[fold c, fold a] | (c :-> a) <- cs]
instance Monoid e => CondExp (Fold e) where
maybe n j m = coerce m <> coerce n <> coerce (j mempty)
instance Monoid e => ListExp (Fold e) where
mapE f as = coerce as <> coerce (f mempty)
dropWhileE p as = coerce as <> coerce (p mempty)
foldE f a as = coerce a <> coerce as <> coerce (f mempty mempty)
instance Monoid e => LetExp (Fold e) where
letE _ a f = coerce a <> f mempty
instance AssertExp (Fold e)
instance Monoid e => VarExp (Fold e) where
varE _ = mempty
instance Semigroup e => CondIntensional (Fold e) where
maybeI _ n j m = coerce m <> coerce n <> coerce j
instance Semigroup e => ListIntensional (Fold e) where
mapI _ b as = coerce as <> coerce b
dropWhileI _ b as = coerce as <> coerce b
foldI _ _ b a as = coerce a <> coerce as <> coerce b
instance Semigroup e => LetIntensional (Fold e) where
letI _ a b = coerce a <> coerce b
data (e1 :×: e2) a = (:×:)
{ prodFst :: e1 a
, prodSnd :: e2 a
}
mkProd ::
(lang e1, lang e2)
=> proxy lang
-> (forall e. lang e => e a)
-> (e1 :×: e2) a
mkProd _ e = e :×: e
liftProd ::
(lang e1, lang e2)
=> proxy lang
-> (forall e. lang e => e a -> e b)
-> (e1 :×: e2) a
-> (e1 :×: e2) b
liftProd _ f (a1 :×: a2) = f a1 :×: f a2
liftProd2 ::
(lang e1, lang e2)
=> proxy lang
-> (forall e. lang e => e a -> e b -> e c)
-> (e1 :×: e2) a
-> (e1 :×: e2) b
-> (e1 :×: e2) c
liftProd2 _ f (a1 :×: a2) (b1 :×: b2) = f a1 b1 :×: f a2 b2
liftProd3 ::
(lang e1, lang e2)
=> proxy lang
-> (forall e. lang e => e a -> e b -> e c -> e d)
-> (e1 :×: e2) a
-> (e1 :×: e2) b
-> (e1 :×: e2) c
-> (e1 :×: e2) d
liftProd3 _ f (a1 :×: a2) (b1 :×: b2) (c1 :×: c2) = f a1 b1 c1 :×: f a2 b2 c2
instance (ConstExp e1, ConstExp e2) => ConstExp (e1 :×: e2) where
lit a = mkProd (Proxy @ConstExp) (lit a)
instance (NumExp e1, NumExp e2) => NumExp (e1 :×: e2) where
add = liftProd2 (Proxy @NumExp) add
sub = liftProd2 (Proxy @NumExp) sub
mul = liftProd2 (Proxy @NumExp) mul
absE = liftProd (Proxy @NumExp) absE
signE = liftProd (Proxy @NumExp) signE
fromIntegral = liftProd (Proxy @NumExp) fromIntegral
floor = liftProd (Proxy @NumExp) floor
truncate = liftProd (Proxy @NumExp) truncate
roundN n = liftProd (Proxy @NumExp) (roundN n)
instance (FracExp e1, FracExp e2) => FracExp (e1 :×: e2) where
fdiv = liftProd2 (Proxy @FracExp) fdiv
instance (LogicExp e1, LogicExp e2) => LogicExp (e1 :×: e2) where
not = liftProd (Proxy @LogicExp) not
conj = liftProd2 (Proxy @LogicExp) conj
disj = liftProd2 (Proxy @LogicExp) disj
xor = liftProd2 (Proxy @LogicExp) xor
instance (CompareExp e1, CompareExp e2) => CompareExp (e1 :×: e2) where
eq = liftProd2 (Proxy @CompareExp) eq
neq = liftProd2 (Proxy @CompareExp) neq
lt = liftProd2 (Proxy @CompareExp) lt
gt = liftProd2 (Proxy @CompareExp) gt
lte = liftProd2 (Proxy @CompareExp) lte
gte = liftProd2 (Proxy @CompareExp) gte
min = liftProd2 (Proxy @CompareExp) min
max = liftProd2 (Proxy @CompareExp) max
instance (CondExpFO e1, CondExpFO e2) => CondExpFO (e1 :×: e2) where
just = liftProd (Proxy @CondExpFO) just
cases cs (Otherwise :-> (d1 :×: d2)) =
cases cs1 (Otherwise :-> d1) :×: cases cs2 (Otherwise :-> d2)
where
(cs1, cs2) =
unzip [(c1 :-> a1, c2 :-> a2) | ((c1 :×: c2) :-> (a1 :×: a2)) <- cs]
partial_cases cs = partial_cases cs1 :×: partial_cases cs2
where
(cs1, cs2) =
unzip [(c1 :-> a1, c2 :-> a2) | ((c1 :×: c2) :-> (a1 :×: a2)) <- cs]
instance (ListExpFO e1, ListExpFO e2) => ListExpFO (e1 :×: e2) where
range = liftProd2 (Proxy @ListExpFO) range
headE = liftProd (Proxy @ListExpFO) headE
append = liftProd2 (Proxy @ListExpFO) append
list as = list as1 :×: list as2
where
(as1, as2) = unzip [(a1, a2) | (a1 :×: a2) <- as]
instance (TupleExp e1, TupleExp e2) => TupleExp (e1 :×: e2) where
pair = liftProd2 (Proxy @TupleExp) pair
fstE = liftProd (Proxy @TupleExp) fstE
sndE = liftProd (Proxy @TupleExp) sndE
instance (FieldExp e1, FieldExp e2) => FieldExp (e1 :×: e2) where
getField f = liftProd (Proxy @FieldExp) (getField f)
instance (AnnExp ann e1, AnnExp ann e2) => AnnExp ann (e1 :×: e2) where
ann a = liftProd (Proxy @(AnnExp ann)) (ann a)
instance (AssertExp e1, AssertExp e2) => AssertExp (e1 :×: e2) where
assert lab = liftProd2 (Proxy @AssertExp) (assert lab)
assertEq lab = liftProd2 (Proxy @AssertExp) (assertEq lab)
instance (VarExp e1, VarExp e2) => VarExp (e1 :×: e2) where
varE v = mkProd (Proxy @VarExp) (varE v)
instance (CondIntensional e1, CondIntensional e2) =>
CondIntensional (e1 :×: e2) where
maybeI v = liftProd3 (Proxy @CondIntensional) (maybeI v)
instance (ListIntensional e1, ListIntensional e2) =>
ListIntensional (e1 :×: e2) where
mapI v = liftProd2 (Proxy @ListIntensional) (mapI v)
dropWhileI v = liftProd2 (Proxy @ListIntensional) (dropWhileI v)
foldI va vb = liftProd3 (Proxy @ListIntensional) (foldI va vb)
instance (LetIntensional e1, LetIntensional e2) =>
LetIntensional (e1 :×: e2) where
letI v = liftProd2 (Proxy @LetIntensional) (letI v)
type BindSet = HashMap Text Int
freshVar :: Text -> BindSet -> (Text, BindSet)
freshVar v bs = case HashMap.lookup v bs of
Nothing -> (v, HashMap.insert v 0 bs)
Just n -> (v <> fromString (show n), HashMap.insert v (n+1) bs)
newtype Intensional e a = Intensional
{ unIntensional :: (Fold BindSet :×: e) a
} deriving ( ConstExp
, NumExp
, FracExp
, LogicExp
, CompareExp
, CondExpFO
, ListExpFO
, TupleExp
, FieldExp
, AnnExp ann
, AssertExp
)
liftIntensional :: (e a -> e b) -> Intensional e a -> Intensional e b
liftIntensional f (Intensional (bsa :×: ea)) = Intensional (coerce bsa :×: f ea)
liftIntensional2 ::
(e a -> e b -> e c)
-> Intensional e a
-> Intensional e b
-> Intensional e c
liftIntensional2 f (Intensional (bsa :×: ea)) (Intensional (bsb :×: eb)) =
Intensional ((coerce bsa <> coerce bsb) :×: f ea eb)
liftIntensional3 ::
(e a -> e b -> e c -> e d)
-> Intensional e a
-> Intensional e b
-> Intensional e c
-> Intensional e d
liftIntensional3 f
(Intensional (bsa :×: ea))
(Intensional (bsb :×: eb))
(Intensional (bsc :×: ec)) =
Intensional (Fold (fold bsa <> fold bsb <> fold bsc) :×: f ea eb ec)
class VarExp e where
varE ::
DinoType a
=> Text
-> e a
instance VarExp e => VarExp (Intensional e) where
varE v = Intensional (mempty :×: varE v)
unbind ::
(VarExp e, DinoType a)
=> Text
-> (Intensional e a -> Intensional e b)
-> (Text, Intensional e b)
unbind base f = (v, Intensional (Fold bsb' :×: eb))
where
Intensional (Fold bsb :×: eb) = f (varE v)
(v, bsb') = freshVar base bsb
unbind2 ::
(VarExp e, DinoType a, DinoType b)
=> Text
-> (Intensional e a -> Intensional e b -> Intensional e c)
-> (Text, Text, Intensional e c)
unbind2 base f = (va, vb, Intensional (Fold bsc'' :×: ec))
where
Intensional (Fold bsc :×: ec) = f (varE va) (varE vb)
(va, bsc') = freshVar base bsc
(vb, bsc'') = freshVar base bsc'
class CondIntensional e where
maybeI ::
DinoType a
=> Text
-> e b
-> e b
-> e (Maybe a)
-> e b
class ListIntensional e where
mapI ::
DinoType a
=> Text
-> e b
-> e [a]
-> e [b]
dropWhileI ::
DinoType a
=> Text
-> e Bool
-> e [a]
-> e [a]
foldI
:: (DinoType a, DinoType b)
=> Text
-> Text
-> e a
-> e a
-> e [b]
-> e a
class LetIntensional e where
letI ::
DinoType a
=> Text
-> e a
-> e b
-> e b
instance (CondExpFO e, VarExp e, CondIntensional e) =>
CondExp (Intensional e) where
maybe n j = liftIntensional3 (maybeI var) n body
where
(var, body) = unbind "elem" j
instance (ListExpFO e, VarExp e, ListIntensional e) =>
ListExp (Intensional e) where
mapE f = liftIntensional2 (mapI var) body
where
(var, body) = unbind "elem" f
dropWhileE f = liftIntensional2 (dropWhileI var) body
where
(var, body) = unbind "elem" f
foldE f = liftIntensional3 (foldI va vb) body
where
(va, vb, body) = unbind2 "elem" f
instance (VarExp e, LetIntensional e) => LetExp (Intensional e) where
letE base a f = liftIntensional2 (letI var) a body
where
(var, body) = unbind base f
newtype NumRep = NumRep {unNumRep :: Rational}
deriving (Eq, Ord, Num, Fractional, Real, Hashable)
instance Show NumRep where
show (NumRep n)
| denominator n Prelude.== 1 = show $ numerator n
| otherwise = show $ fromRational @Double n
newtype Reified a = Reified {unReified :: AST NumRep}
instance Inspectable (Reified a) where
inspect = coerce
appReified :: Constr -> Reified a -> Reified b
appReified con = coerce $ \a -> App @NumRep con [a]
appReified2 :: Constr -> Reified a -> Reified b -> Reified c
appReified2 con = coerce $ \a b -> App @NumRep con [a, b]
appReified3 :: Constr -> Reified a -> Reified b -> Reified c -> Reified d
appReified3 con = coerce $ \a b c -> App @NumRep con [a, b, c]
appReified4 ::
Constr -> Reified a -> Reified b -> Reified c -> Reified d -> Reified e
appReified4 con = coerce $ \a b c d -> App @NumRep con [a, b, c, d]
appReified5 ::
Constr
-> Reified a
-> Reified b
-> Reified c
-> Reified d
-> Reified e
-> Reified f
appReified5 con = coerce $ \a b c d f -> App @NumRep con [a, b, c, d, f]
instance ConstExp Reified where
lit = coerce . inspect
instance NumExp Reified where
add = appReified2 "add"
sub = appReified2 "sub"
mul = appReified2 "mul"
absE = appReified "absE"
signE = appReified "signE"
fromIntegral = appReified "fromIntegral"
floor = appReified "floor"
truncate = appReified "truncate"
roundN n = appReified2 "roundN" (Reified $ Number $ Prelude.fromIntegral n)
instance FracExp Reified where
fdiv = appReified2 "fdiv"
instance LogicExp Reified where
not = appReified "not"
conj = appReified2 "conj"
disj = appReified2 "disj"
xor = appReified2 "xor"
instance CompareExp Reified where
eq = appReified2 "eq"
neq = appReified2 "neq"
lt = appReified2 "lt"
gt = appReified2 "gt"
lte = appReified2 "lte"
gte = appReified2 "gte"
min = appReified2 "min"
max = appReified2 "max"
instance CondExpFO Reified where
just = appReified "just"
cases cs (Otherwise :-> d) =
partial_cases (cs ++ [Reified (App "Otherwise" []) :-> d])
partial_cases cs = Reified $ App "cases" $ pure $ App List
[App ":->" [unReified c, unReified a] | c :-> a <- cs]
instance ListExpFO Reified where
range = appReified2 "range"
list = coerce $ App @NumRep List
headE = appReified "headE"
append = appReified2 "append"
instance TupleExp Reified where
pair = appReified2 "pair"
fstE = appReified "fstE"
sndE = appReified "sndE"
instance FieldExp Reified where
getField f = appReified (fromString $ "#" ++ symbolVal f)
instance AnnExp Text Reified where
ann = appReified . Named Annotation
instance AssertExp Reified
instance VarExp Reified where
varE v = Reified $ App (Named LocalVar v) []
instance CondIntensional Reified where
maybeI v = appReified3 $ Named Constructor $ "maybe *" <> v
instance ListIntensional Reified where
mapI v = appReified2 $ Named Constructor $ "mapE *" <> v
dropWhileI v = appReified2 $ Named Constructor $ "dropWhileE *" <> v
foldI va vb = appReified3 $
Named Constructor $ Text.unwords ["foldE", "*" <> va, "*" <> vb]
instance LetIntensional Reified where
letI v a b = (coerce $ Let @NumRep v) a b
newtype EvalEnv e a = EvalEnv
{ unEvalEnv :: ReaderT (HashMap Text Dinamic) e a
} deriving ( Functor
, Applicative
, Monad
, MonadReader (HashMap Text Dinamic)
, ConstExp
, NumExp
, FracExp
, LogicExp
, CompareExp
, CondExpFO
, ListExpFO
, TupleExp
, FieldExp
, AnnExp ann
)
extendEnv ::
(Monad e, DinoType a)
=> Text
-> a
-> EvalEnv e b
-> EvalEnv e b
extendEnv var = local . HashMap.insert var . Dinamic
data EvalEnvError
= NotInScope Text
| TypeError Text
deriving (Show)
instance Exception EvalEnvError
instance Monad e => VarExp (EvalEnv e) where
varE var = do
env <- ask
return $
Prelude.maybe (throw $ TypeError var) id $
fromDinamic $
Prelude.maybe (throw $ NotInScope var) id $ HashMap.lookup var env
instance Monad e => CondIntensional (EvalEnv e) where
maybeI var n j m = do
m' <- m
case m' of
Nothing -> n
Just a -> extendEnv var a j
instance Monad e => ListIntensional (EvalEnv e) where
mapI var body as = Prelude.mapM (flip (extendEnv var) body) =<< as
dropWhileI var body as = dropWhileM (flip (extendEnv var) body) =<< as
foldI va vb body a bs = do
a' <- a
bs' <- bs
foldM (\aa bb -> extendEnv va aa $ extendEnv vb bb body) a' bs'
instance Monad e => LetIntensional (EvalEnv e) where
letI var a b = flip (extendEnv var) b =<< a
data InvalidAssertion
= InvalidCondition
{ assertionLabel :: Text
}
| NotEqual
{ assertionLabel :: Text
, reference :: Text
, actual :: Text
}
deriving (Show)
instance Exception InvalidAssertion
newtype AssertViaMonadError e a = AssertViaMonadError
{ unAssertViaMonadError :: e a
} deriving ( Functor
, Applicative
, Monad
, MonadError exc
, ConstExp
, NumExp
, FracExp
, LogicExp
, CompareExp
, CondExpFO
, CondExp
, ListExpFO
, ListExp
, TupleExp
, LetExp
, FieldExp
, AnnExp ann
)
instance MonadError InvalidAssertion e =>
AssertExp (AssertViaMonadError e) where
assert lab cond a = do
c <- cond
unless c $ throwError $ InvalidCondition lab
a
assertEq lab ref act = do
r <- ref
a <- act
unless (r Prelude.== a) $
throwError $ NotEqual lab (Text.pack $ show r) (Text.pack $ show a)
return a
newtype AssertViaMonadThrow e a = AssertViaMonadThrow
{ unAssertViaMonadThrow :: e a
} deriving ( Functor
, Applicative
, Monad
, MonadThrow
, ConstExp
, NumExp
, FracExp
, LogicExp
, CompareExp
, CondExpFO
, CondExp
, ListExpFO
, ListExp
, TupleExp
, LetExp
, FieldExp
, AnnExp ann
)
instance MonadThrow e => AssertExp (AssertViaMonadThrow e) where
assert lab cond a = do
c <- cond
unless c $ throwM $ InvalidCondition lab
a
assertEq lab ref act = do
r <- ref
a <- act
unless (r Prelude.== a) $
throwM $ NotEqual lab (Text.pack $ show r) (Text.pack $ show a)
return a
data Assertion e where
Assert :: e Bool -> Assertion e
AssertEq :: (Eq a, Show a) => e a -> e a -> Assertion e
newtype CollectAssertions e a = CollectAssertions
{ unCollectAssertions :: (Intensional (e :×: Fold [(Text, Assertion e)])) a
} deriving ( ConstExp
, NumExp
, FracExp
, LogicExp
, CompareExp
, CondExpFO
, CondExp
, ListExpFO
, ListExp
, TupleExp
, LetExp
, FieldExp
, AnnExp ann
, VarExp
)
instance AssertExp (CollectAssertions e) where
assert lab =
coerce $
liftIntensional2 @(e :×: Fold [(Text, Assertion e)]) $
\(c :×: _) (a :×: as) -> (a :×: (as <> Fold [(lab, Assert c)]))
assertEq lab =
coerce $
liftIntensional2 @(e :×: Fold [(Text, Assertion e)]) $
\(ref :×: _) (act :×: as) ->
(act :×: (as <> Fold [(lab, AssertEq ref act)]))