{-# OPTIONS_GHC -fglasgow-exts -fallow-overlapping-instances #-} ---------------------------------------------------------------- -- Code for the article -- "Modularity and Implementation of -- Mathematical Operational Semantics" -- -- Mauro Jaskelioff Neil Ghani Graham Hutton ---------------------------------------------------------------- module MOS where import Prelude hiding (seq) import Control.Monad infixr 5 :+: data L1 = Put1 A | Seq1 L1 L1 | Alt1 L1 L1 type A = Char ---------------------------------------------------------------- -- Terms as Free Monads ---------------------------------------------------------------- data L2 a = Put2 A | Seq2 a a | Alt2 a a instance Functor L2 where fmap _ (Put2 c) = Put2 c fmap f (Seq2 p q) = Seq2 (f p) (f q) fmap f (Alt2 p q) = Alt2 (f p) (f q) data Term f x = Var x | Con (f (Term f x)) instance Functor f => Functor (Term f) where fmap f (Var x) = Var (f x) fmap f (Con t) = Con (fmap (fmap f) t) instance Functor f => Monad (Term f) where return = Var (Var x) >>= f = f x (Con t) >>= f = Con (fmap (>>= f) t) foldTerm :: Functor f => (a -> b) -> (f b -> b) -> Term f a -> b foldTerm var _ (Var a) = var a foldTerm var con (Con fta) = con (fmap (foldTerm var con) fta) type Program f = Term f Zero data Zero empty :: Zero -> a empty _ = undefined ---------------------------------------------------------------- -- Coproducts of Free Monads ---------------------------------------------------------------- data (f :+: g) a = Inl (f a) | Inr (g a) instance (Functor f, Functor g) => Functor (f :+: g) where fmap h (Inl fx) = Inl (fmap h fx) fmap h (Inr gx) = Inr (fmap h gx) copair :: (f a -> b) -> (g a -> b) -> (f :+: g) a -> b copair f _ (Inl fa) = f fa copair _ g (Inr ga) = g ga type P = Put :+: Seq :+: Alt data Put a = Put Char data Seq a = Seq a a data Alt a = Alt a a instance Functor Put where fmap _ (Put c) = Put c instance Functor Alt where fmap f (Alt p q) = Alt (f p) (f q) instance Functor Seq where fmap f (Seq p q) = Seq (f p) (f q) prog :: Program P prog = alt1 (put1 'a' `seq1` put1 'b') (put1 'c') put1 :: Char -> Term P a put1 c = Con (Inl (Put c)) seq1, alt1 :: Term P a -> Term P a -> Term P a seq1 p q = Con (Inr (Inl (Seq p q))) alt1 p q = Con (Inr (Inr (Alt p q))) data N a = N Int data Add a = Add a a data Ifz a = Ifz a a a data Thr a = Thr data Cat a = Cat a a type Z = N :+: Add :+: Ifz type E = Thr :+: Cat instance Functor N where fmap f (N n) = N n instance Functor Add where fmap f (Add p q) = Add (f p) (f q) instance Functor Ifz where fmap f (Ifz c t e) = Ifz (f c) (f t) (f e) instance Functor Thr where fmap f Thr = Thr instance Functor Cat where fmap f (Cat t u) = Cat (f t) (f u) type EP = Thr :+: Cat :+: Put :+: Seq :+: Alt type EZ = Thr :+: Cat :+: N :+: Add :+: Ifz type NDZ = N :+: Add :+: Alt put' :: Char -> Term EP a put' c = Con (Inr (Inr (Inl (Put c)))) ----------------------------------------------------------------------------- -- SubType relation ----------------------------------------------------------------------------- infixl 4 >>>= class (Functor sub, Functor sup) => SubType sub sup where inj :: sub a -> sup a prj :: sup a -> Maybe (sub a) instance (Functor f) => SubType f f where inj = id prj = Just instance (Functor f, Functor g) => SubType f (f :+: g) where inj = Inl prj (Inl f) = Just f prj _ = Nothing instance (Functor h, SubType f g) => SubType f (h :+: g) where inj = Inr . inj prj (Inr a) = prj a prj _ = Nothing (>>>=) :: (SubType sub sup) => (sup x -> a) -> (sub x -> a) -> sup x -> a (f >>>= g) x = case prj x of Nothing -> f x Just y -> g y con :: (SubType s t) => s (Term t x) -> Term t x con = Con . inj put :: (SubType Put s) => Char -> Term s x put c = con (Put c) seq :: (SubType Seq s) => Term s x -> Term s x -> Term s x seq p q = con (Seq p q) alt :: (SubType Alt s) => Term s x -> Term s x -> Term s x alt p q = con (Alt p q) n :: (SubType N s) => Int -> Term s x n m = con (N m) add :: (SubType Add s) => Term s x -> Term s x -> Term s x add p q = con (Add p q) ifz :: (SubType Ifz s) => Term s x -> (Term s x, Term s x) -> Term s x ifz c (t,e) = con (Ifz c t e) thr :: (SubType Thr s) => Term s x thr = con Thr cat :: (SubType Cat s) => Term s x -> Term s x -> Term s x cat p q = con (Cat p q) ------------------------------------------------------------------------- -- Composition of functors ------------------------------------------------------------------------- infixr 7 :.: data (h :.: g) x = Comp {deComp :: (h (g x)) } instance (Functor h, Functor g) => Functor (h :.: g) where fmap f (Comp c) = Comp (fmap (fmap f) c) data Pr a = Pr A a data KA a = KA A instance Functor Pr where fmap f (Pr c a) = Pr c (f a) instance Functor KA where fmap _ (KA c) = KA c data KI a = KI Int instance Functor KI where fmap _ (KI c) = KI c data KE a = KE instance Functor KE where fmap _ KE = KE ---------------------------------------------------------------- -- Execution of a Transition System ---------------------------------------------------------------- data Nu f = Nu (f (Nu f)) unfold :: Functor b => (x -> b x) -> x -> Nu b unfold g = Nu. fmap (unfold g) . g ---------------------------------------------------------------- -- Mathematical Operational Semantics ---------------------------------------------------------------- type OR s b = forall x y. (x -> y) -> -- Term environment (x -> b y) -> -- Behaviour environment s x -> -- Source of the conclusion b (Term s y) -- Target of the conclusion type (:..:) f g x = f (g x) orP :: OR L2 ([] :..: (KA :+: Pr)) orP _ _ (Put2 c) = [Inl (KA c)] orP te be (Seq2 p q) = be' p >>= copair (\(KA c) -> [Inr (Pr c (te' q))]) (\(Pr c p') -> [Inr (Pr c (Con (Seq2 p' (te' q))))]) where te' = Var . te be' = map (fmap Var). be orP te be (Alt2 p q) = map (fmap Var) (be p ++ be q) ---------------------------------------------------------------- -- Obtaining a transition relation ---------------------------------------------------------------- opMonad :: (Functor s, Functor b) => OR s b -> (x -> b x) -> Term s x -> b (Term s x) opMonad op k = snd. foldTerm (pair Var (fmap Var. k)) (pair (Con. fmap fst) (fmap join. op fst snd)) where pair f g a = (f a, g a) run :: (Functor s, Functor b) => OR s b -> Program s -> Nu b run op = unfold (opMonad op empty) ---------------------------------------------------------------- -- Modular Operational Semantics ---------------------------------------------------------------- type MOR s t m b = forall x y. (x -> y) -> (x -> m (b y)) -> s x -> m (b (Term t y)) ossify :: MOR s s m b -> OR s (m :.: b) ossify mor te be = Comp . mor te (deComp . be) ---------------------------------------------------------------- -- Joining MOR ---------------------------------------------------------------- infixr 5 <+> (<+>) :: MOR s t m b -> MOR s' t m b -> MOR (s :+: s') t m b (op1 <+> op2) te be = copair (op1 te be) (op2 te be) ---------------------------------------------------------------- -- Defining Modular Operational Rules ---------------------------------------------------------------- morPut :: (SubType Put s, SubType KA b, Monad m) => MOR Put s m b morPut _ _ (Put c) = return (inj (KA c)) morSeq :: (SubType Seq s, SubType KA b, SubType Pr b, Monad m) => MOR Seq s m b morSeq te be (Seq p q) = be' p >>= return . fmap (`seq` te' q) >>>= \(KA c) -> return (inj (Pr c (te' q))) where te' = Var . te be' = liftM (fmap Var). be morAlt :: (SubType Alt s, Functor b, MonadPlus m) => MOR Alt s m b morAlt te be (Alt p q) = liftM (fmap Var) (be p `mplus` be q) addl, addr :: (SubType Add s) => Term s x -> Term s x -> Term s x addl = add addr = add ifzr :: (SubType Ifz s) => Term s x -> (Term s x, Term s x) -> Term s x ifzr = ifz catr :: (SubType Cat s) => Term s x -> Term s x -> Term s x catr = cat morN ::(SubType N s, SubType KI b, Monad m) => MOR N s m b morN _ _ (N n) = return (inj (KI n)) morAdd :: (SubType Add s, SubType KI b, Monad m) => MOR Add s m b morAdd te be (Add p q) = be' p >>= return . fmap (`addr` te' q) >>>= \(KI n) -> be' q >>= return . fmap (te' p `addl`) >>>= \(KI m) -> return (inj (KI (n+m))) where te' = Var . te be' = liftM (fmap Var). be morIfz :: (SubType Ifz s, SubType KI b, Monad m) => MOR Ifz s m b morIfz te be (Ifz c t e) = be' c >>= return . fmap (`ifzr` (te' t,te' e)) >>>= \(KI n) -> if n==0 then be' t else be' e where te' = Var . te be' = liftM (fmap Var). be morThr :: (SubType Thr s, SubType KE b, Monad m) => MOR Thr s m b morThr _ _ Thr = return (inj KE) morCat :: (SubType Cat s, SubType KE b, Monad m) => MOR Cat s m b morCat te be (Cat p q) = be' p >>= return . fmap (`catr` te' q) >>>= \KE -> be' q where te' = return . te be' = liftM (fmap return). be ------------------------------------------------------------------------- -- Putting it all together ------------------------------------------------------------------------- morP :: (SubType Put s, SubType Seq s, SubType Alt s, SubType KA b, SubType Pr b, MonadPlus m) => MOR P s m b morP = morPut <+> morSeq <+> morAlt orP' :: OR P ([] :.: (Pr :+: KA)) orP' = ossify morP morZ :: (SubType N s, SubType Ifz s, SubType Add s, Monad m, SubType KI b ) => MOR Z s m b morZ = morN <+> morAdd <+> morIfz z :: OR Z (Id :.: KI) z = ossify morZ ep :: OR EP ([] :.: (KE :+: Pr :+: KA)) ep = ossify (morThr <+> morCat <+> morP) ez :: OR EZ (Id :.: (KE :+: KI)) ez = ossify (morThr <+> morCat <+> morZ) zp :: OR (Put :+: Seq :+: Z) (Id :.: (KA :+: Pr :+: KI)) zp = ossify (morPut <+> morSeq <+> morZ) zP :: OR (Put :+: Seq :+: Alt :+: Z) ([] :.: (KA :+: Pr :+: KI)) zP = ossify (morPut <+> morSeq <+> morAlt <+> morZ) addND a b = alt (a `add` b) (b `add` a) type AOR s b = forall a. s (a , b a) -> b (Term s a) fromOR :: OR s b -> AOR s b fromOR os = os fst snd toOR :: (Functor s) => AOR s b -> OR s b toOR aor te be = aor. fmap (pair te be) where pair f g a = (f a, g a) ------------------------------------------------------------------------- -- Identity Monad ------------------------------------------------------------------------- data Id a = Id a instance Functor Id where fmap f (Id a) = Id (f a) instance Monad Id where return = Id (Id a) >>= f = f a ------------------------------------------------------------------------- -- Showing fixpoints ------------------------------------------------------------------------- class Functor f => PreservesString f where preservesString :: f String -> String instance (Show a, PreservesString f) => Show (Term f a) where show (Var a) = show a show (Con x) = preservesString (fmap show x) instance (PreservesString f, PreservesString g) => PreservesString (f :+: g) where preservesString (Inl x) = preservesString x preservesString (Inr x) = preservesString x instance Show Zero where ------------------------------------------------------------------------- -- Showing Behaviour and Syntax ------------------------------------------------------------------------- instance PreservesString [] where preservesString = ("["++). h where h [] = "]" h [x] = x++"]" h (x:y:xs) = x++", "++h (y:xs) instance PreservesString KA where preservesString (KA c) = "-"++show c++"->." instance PreservesString KI where preservesString (KI i) = show i instance PreservesString KE where preservesString KE = "Exception!" instance PreservesString Pr where preservesString (Pr c a) = "-"++[c]++"-> "++a instance PreservesString N where preservesString (N n) = show n instance PreservesString Thr where preservesString Thr = "Throw" instance PreservesString Cat where preservesString (Cat p q) = "(cat "++p++" : "++q++")" instance PreservesString Add where preservesString (Add p q) = "("++p++" + "++q++")" instance PreservesString Ifz where preservesString (Ifz c p q) = "(if "++c++" then " ++p++" else "++q++")" instance PreservesString Put where preservesString (Put c) = [c] instance PreservesString Seq where preservesString (Seq p q) = "("++p++" ; "++q++")" instance PreservesString Alt where preservesString (Alt p q) = "("++p++" or "++q++")" ----------------------------------------------------------- -- Example Programs ----------------------------------------------------------- -- P p0,p1,p2 :: Program P p0 = seq (put 'b') (put 'c') p1 = alt (put 'a') (alt p0 (put 'd')) p2 = seq (alt (put 'a') (put 'd')) p0 -- EP p3,p4,p5 :: Program EP p3 = (put 'a' `seq` thr) `alt` (put 'b' `seq` put 'c') p4 = cat p3 (put 'X') p5 = (cat (put 'a' `seq` thr) (put 'Y')) `alt` (put 'b' `seq` put 'c') -- EZ p10,p11,p12,p13 :: Program EZ p10 = n 1 `add` (n 2 `add` n 4) p11 = cat (n 1 `add` thr) (n 4 `add` n 6) p12 = add p10 p11 p13 = ifz (n 0) (n 1, thr) -- printZ p20 :: Program (Put :+: Seq :+: Z) p20 = (put 'o' `seq` put 'w') `add` (n 2 `add` (put 'f' `seq` n 4))