{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Test.StateMachine.Types.Environment
( Environment(..)
, EnvironmentError(..)
, emptyEnvironment
, insertConcrete
, insertConcretes
, reifyDynamic
, reifyEnvironment
, reify
) where
import Data.Dynamic
(Dynamic, Typeable, dynTypeRep, fromDynamic)
import Data.Map
(Map)
import qualified Data.Map as M
import Data.Semigroup
import Data.Typeable
(Proxy(Proxy), TypeRep, typeRep)
import Prelude
import qualified Test.StateMachine.Types.Rank2 as Rank2
import Test.StateMachine.Types.References
newtype Environment = Environment
{ unEnvironment :: Map Var Dynamic
}
deriving stock (Show)
deriving newtype (Semigroup, Monoid)
data EnvironmentError
= EnvironmentValueNotFound !Var
| EnvironmentTypeError !TypeRep !TypeRep
deriving stock (Eq, Ord, Show)
emptyEnvironment :: Environment
emptyEnvironment = Environment M.empty
insertConcrete :: Var -> Dynamic -> Environment -> Environment
insertConcrete var dyn = Environment . M.insert var dyn . unEnvironment
insertConcretes :: [Var] -> [Dynamic] -> Environment -> Environment
insertConcretes [] [] env = env
insertConcretes (var : vars) (dyn : dyns) env =
insertConcretes vars dyns (insertConcrete var dyn env)
insertConcretes _ _ _ =
error "insertConcrets: impossible."
reifyDynamic :: forall a. Typeable a => Dynamic
-> Either EnvironmentError (Concrete a)
reifyDynamic dyn =
case fromDynamic dyn of
Nothing ->
Left (EnvironmentTypeError (typeRep (Proxy :: Proxy a)) (dynTypeRep dyn))
Just x ->
Right (Concrete x)
reifyEnvironment :: Environment
-> (forall a. Symbolic a -> Either EnvironmentError (Concrete a))
reifyEnvironment (Environment vars) (Symbolic n) =
case M.lookup n vars of
Nothing ->
Left (EnvironmentValueNotFound n)
Just dyn ->
reifyDynamic dyn
reify :: Rank2.Traversable t
=> Environment -> t Symbolic -> Either EnvironmentError (t Concrete)
reify vars = Rank2.traverse (reifyEnvironment vars)