imsos-monad 0.1.0.0 → 0.2.4.0
raw patch · 15 files changed
+1723/−93 lines, 15 filesdep +compdatadep +containersdep +random
Dependencies added: compdata, containers, random, template-haskell
Files
- imsos-monad.cabal +24/−3
- src/Control/Monad/IMSOS.hs +2/−90
- src/Control/Monad/IMSOS/Algebras.hs +41/−0
- src/Control/Monad/IMSOS/Fixed.hs +49/−0
- src/Control/Monad/IMSOS/Layered.hs +56/−0
- src/Control/Monad/IMSOS/LayeredAlgebras.hs +49/−0
- src/Control/Monad/IMSOS/LayeredDeriving.hs +212/−0
- src/Control/Monad/IMSOS/LayeredTerms.hs +53/−0
- src/Control/Monad/IMSOS/Monad.hs +163/−0
- src/Control/Monad/IMSOS/Rules/LayeredStep.hs +200/−0
- src/Control/Monad/IMSOS/Rules/Relations.hs +68/−0
- src/Control/Monad/IMSOS/Rules/Step.hs +199/−0
- src/Control/Monad/IMSOS/Signatures.hs +65/−0
- src/Data/Comp/ProjectionExt.hs +374/−0
- src/Data/Comp/SubsumeCommon.hs +168/−0
imsos-monad.cabal view
@@ -20,7 +20,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.1.0.0+version: 0.2.4.0 -- A short (one-line) description of the package. synopsis:@@ -64,17 +64,38 @@ import: warnings -- Modules exported by the library.- exposed-modules: Control.Monad.IMSOS + exposed-modules: Control.Monad.IMSOS+ ,Control.Monad.IMSOS.Fixed+ ,Control.Monad.IMSOS.Layered -- Modules included in this library but not exported.- -- other-modules:+ other-modules: + ,Control.Monad.IMSOS.Monad+ ,Control.Monad.IMSOS.Signatures+ ,Control.Monad.IMSOS.Rules.Relations+ ,Control.Monad.IMSOS.Algebras+ ,Control.Monad.IMSOS.LayeredAlgebras+ ,Control.Monad.IMSOS.Rules.Step+ ,Control.Monad.IMSOS.Rules.LayeredStep+ ,Control.Monad.IMSOS.LayeredTerms + ,Data.Comp.SubsumeCommon+ ,Data.Comp.ProjectionExt+ ,Control.Monad.IMSOS.LayeredDeriving++-- ,Control.Monad.IMSOS.Cases.WhileFixed+-- ,Control.Monad.IMSOS.Cases.WhileLayered+ -- LANGUAGE extensions used by modules in this package. -- other-extensions: -- Other library packages from which modules are imported. build-depends: base ^>=4.18.3.0+ ,containers ^>= 0.6 ,mtl ^>= 2.3.1+ ,random ^>= 1.3+ ,template-haskell ^>= 2.20+ ,compdata ^>= 0.13 -- Directories containing source files. hs-source-dirs: src
src/Control/Monad/IMSOS.hs view
@@ -1,92 +1,4 @@-{-# LANGUAGE UndecidableInstances- , FlexibleInstances- , FlexibleContexts- , MultiParamTypeClasses-#-}- module Control.Monad.IMSOS - (MonadIMSOS(..), runIMSOS, yieldIMSOS- ,tell- ,local, reader- ,get, put, state- ,fail, throwError, catchError- ) where--import Control.Applicative (Alternative(..))-import Control.Monad.Error.Class-import Control.Monad.Reader (MonadReader(..))-import Control.Monad.State (MonadState(..))-import Control.Monad.Writer (MonadWriter(..))-import Control.Monad (ap)--newtype MonadIMSOS r s w me e a = MonadIMSOS (r -> s -> me e (a, s, w))--instance Functor (me e) => Functor (MonadIMSOS r s w me e) where- fmap f (MonadIMSOS m) = MonadIMSOS m'- where m' r s = modify <$> m r s- where modify (a, s', w) = (f a, s', w)--instance (MonadError e (me e), Monad (me e), Monoid w)- => Monad (MonadIMSOS r s w me e) where- (MonadIMSOS p) >>= m = MonadIMSOS $ \r1 s1 -> do- (a, s2, w2) <- p r1 s1- let MonadIMSOS q = m a- (b, s3, w3) <- q r1 s2- return (b, s3, w2 <> w3)--instance (MonadError e (me e), Monad (me e), Monoid w)- => Applicative (MonadIMSOS r s w me e) where- pure a = MonadIMSOS m- where m _ s = pure (a, s, mempty)- (<*>) = ap--instance (MonadError e (me e), Monad (me e), Alternative (me e), Monoid w)- => Alternative (MonadIMSOS r s w me e) where- empty = MonadIMSOS (\_ _ -> empty)- (MonadIMSOS p) <|> (MonadIMSOS q) = MonadIMSOS m- where m r s = p r s `catchError` \_ -> q r s- ---instance (MonadError e (me e), Monad (me e), Monoid w)--- => MonadPlus (MonadIMSOS r s w me e)--instance (MonadError String (me String), Monad (me String), Monoid w)- => MonadFail (MonadIMSOS r s w me String) where- fail = throwError--instance (MonadError e (me e), Monad (me e), Monoid w)- => MonadReader r (MonadIMSOS r s w me e) where- ask = MonadIMSOS m - where m r s = pure (r, s, mempty)- local f (MonadIMSOS p) = MonadIMSOS m- where m r s = p (f r) s--instance (MonadError e (me e), Monad (me e), Monoid w)- => MonadState s (MonadIMSOS r s w me e) where- state act = MonadIMSOS m- where m _ s = pure (a, s', mempty)- where (a, s') = act s--instance (MonadError e (me e), Monad (me e), Monoid w)- => MonadWriter w (MonadIMSOS r s w me e) where- tell w = MonadIMSOS m- where m _ s = pure ((), s, w)- listen (MonadIMSOS p) = MonadIMSOS m- where m r s = p r s >>= \(a, s', w) -> pure ((a,w), s', w)- pass (MonadIMSOS p) = MonadIMSOS m- where m r s = p r s >>= \((a,f), s', w) -> pure (a, s', f w)--instance (MonadError e (me e), Monad (me e), Monoid w)- => MonadError e (MonadIMSOS r s w me e) where- throwError e = MonadIMSOS m- where m _ _ = throwError e- catchError (MonadIMSOS p) h = MonadIMSOS m- where m r s = p r s `catchError` \e -> - let MonadIMSOS q = h e- in q r s -- continues with a state as if p not executed--runIMSOS :: r -> s -> MonadIMSOS r s w me e a -> me e (a, s, w)-runIMSOS r s (MonadIMSOS f) = f r s+ (module Control.Monad.IMSOS.Layered) where -yieldIMSOS :: Functor (me e) => - r -> s -> ((a, s, w) -> y) -> MonadIMSOS r s w me e a -> me e y-yieldIMSOS r s toyield m = toyield <$> runIMSOS r s m +import Control.Monad.IMSOS.Layered
+ src/Control/Monad/IMSOS/Algebras.hs view
@@ -0,0 +1,41 @@++{-# LANGUAGE GADTs #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+++module Control.Monad.IMSOS.Algebras where++import Data.Comp.Multi.Ops ( (:+:)(..))+import Data.Comp.Multi ( Term, Alg, HFunctor (..), (:->) )+import Data.Comp.Multi.Algebra (cata)+import Data.Kind (Type)+import Control.Monad.IMSOS.Signatures (Sort, HasSubSig (SubSig))++class HasAlg f e where+ sem :: Alg f (GetCarrier e)++-- TODO can I get rid of this?+newtype GetCarrier e i = GetCarrier { getCarrier :: CarrierOf e i i }++class HasCarrier e s where+ type CarrierOf e s :: Sort -> Type++instance (HasAlg f e, HasAlg g e)+ => HasAlg (f :+: g) e where+ sem (Inl f) = sem @f f+ sem (Inr g) = sem @g g++eval :: forall eval sig sort.+ (HFunctor sig, HasAlg sig eval)+ => Term sig sort -> GetCarrier eval sort+eval = cata (sem @sig @eval)
+ src/Control/Monad/IMSOS/Fixed.hs view
@@ -0,0 +1,49 @@+module Control.Monad.IMSOS.Fixed+ (+ -- from IMSOS.Monad+ MonadIMSOS(..), SemMonad, runIMSOS, yieldIMSOS+ ,Control.Monad.IMSOS.Monad.tell, Control.Monad.IMSOS.Monad.listen, Control.Monad.IMSOS.Monad.censor+ ,Control.Monad.IMSOS.Monad.local, Control.Monad.IMSOS.Monad.reader, Control.Monad.IMSOS.Monad.ask+ ,Control.Monad.IMSOS.Monad.get, Control.Monad.IMSOS.Monad.put, Control.Monad.IMSOS.Monad.modify+ ,fail, MonadError(..)+ ,guard, Alternative(..)+ -- from IMSOS.Rules.Relations+ ,RelID(..), HasRulesErrors(..), SemEntities(..), HasDefaults(..), runSem, Rel+ , GivesSemanticsTo, SharedSemEntities+ -- from IMSOS.Signature + ,Signature, HasSubSig(..)+ --,IsSubSortWitness(..), Share, IsSubSort, HasSubSort, liftTerm, lowerTerm+ -- from IMSOS.Rules.Step + ,HasTaggedRel(..)+ ,isValue, toValue, HasValOps(..), IsLangOp, IsValOp, ValOp, TermL, Language(..), Lang, Sort+ ,StepDefined(..), StepRes(..), steps, stepsOrHalt, stepsOrHaltWhen, StepAvailable, StepsAvailable, StepsTo+ -- to simplify writing rules+ ,premise, premiseE, trans, notrans+ -- from IMSOS.Rules.Trans+ --,TransAvailable, TransDefined(..), (:~>:)(..), EvaluatesTo(..), EvalTag, Direct, Via+ -- from ProjectionExt + ,(:*:)(..), (:<~), (:<|), pr, uncons, recons+ -- from Control.Monad.IMSOS.Algebras + ,HasAlg(..), GetCarrier(..), HasCarrier(..), eval+ ,Term, inject, project, unTerm+ ,(:+:), (:<:), caseH+ -- deriving hfunctors+ ,HFunctor, makeHFunctor, smartConstructors, derive, K(..)+ -- related to Show, Ord and Eq+ ,ShowHF(), makeShowHF+ ) where++import Control.Monad.IMSOS.Signatures+import Control.Monad.IMSOS.Monad+import Control.Monad.IMSOS.Rules.Relations+import Control.Monad.IMSOS.Algebras+import Control.Monad.IMSOS.Rules.Step+import Control.Monad.Error.Class (MonadError(..))+import Control.Applicative (Alternative(..))+import Data.Comp.ProjectionExt+import Data.Comp.Multi.Ops ((:+:), (:<:), caseH)+import Data.Comp.Multi.Derive (makeHFunctor, HFunctor, smartConstructors, derive, makeShowHF, ShowHF)+import Data.Comp.Multi.Sum (inject)+import Data.Comp.Multi ( project, unTerm )+import Data.Comp.Multi.Term ( Term )+import Data.Comp.Multi.HFunctor (K(..))
+ src/Control/Monad/IMSOS/Layered.hs view
@@ -0,0 +1,56 @@+module Control.Monad.IMSOS.Layered+ (+ -- alternative definitions related to 'sort-polymorphic, layered terms'+ Term(..), inject, project, unTerm, fixL+ ,Alg, HasAlg(..), cata, eval+ ,StepDefined(..), steps, stepsOrHalt, stepsOrHaltWhen, premise, premiseE, trans, notrans+ ,isValue, toValue, HasValOps(..), StepsAvailable, StepsTo, SharedSemEntities, IsValOp, ValOp, IsLangOp+ ,HasRulesErrors(..)+ -- deriving + ,smartConstructors++ -- as in IMSOS.Fixed ++ -- from IMSOS.Monad+ ,MonadIMSOS(..), SemMonad, runIMSOS, yieldIMSOS+ ,Control.Monad.IMSOS.Monad.tell, Control.Monad.IMSOS.Monad.listen, Control.Monad.IMSOS.Monad.censor+ ,Control.Monad.IMSOS.Monad.local, Control.Monad.IMSOS.Monad.reader, Control.Monad.IMSOS.Monad.ask+ ,Control.Monad.IMSOS.Monad.get, Control.Monad.IMSOS.Monad.put, Control.Monad.IMSOS.Monad.modify+ ,fail, MonadError(..)+ ,guard, Alternative(..)+ -- from IMSOS.Rules.Relations+ ,RelID(..), SemEntities(..), HasDefaults(..), runSem, Rel+ , GivesSemanticsTo+ -- from IMSOS.Signature + ,Signature, HasSubSig(..)+ --,IsSubSortWitness(..), Share, IsSubSort, HasSubSort, liftTerm, lowerTerm+ -- from IMSOS.Rules.Step + ,HasTaggedRel(..)+ ,TermL, Language(..), Lang, Sort+ -- to simplify writing rules+ -- from IMSOS.Rules.Trans+ --,TransAvailable, TransDefined(..), (:~>:)(..), EvaluatesTo(..), EvalTag, Direct, Via+ -- from ProjectionExt + ,(:*:)(..), (:<~), (:<|), pr, uncons, recons+ -- from Control.Monad.IMSOS.Algebras + ,(:+:), (:<:), caseH+ -- deriving hfunctors+ ,HFunctor, makeHFunctor, derive, K(..)+ -- related to Show, Ord and Eq+ ,ShowHF(), makeShowHF++ ) where++import Control.Monad.IMSOS.Monad+import Control.Monad.IMSOS.LayeredAlgebras+import Control.Monad.IMSOS.LayeredTerms+import Control.Monad.IMSOS.LayeredDeriving (smartConstructors)+import Control.Monad.IMSOS.Rules.LayeredStep+import Control.Monad.IMSOS.Rules.Relations+import Control.Monad.Except+import Control.Applicative+import Control.Monad.IMSOS.Signatures+import Data.Comp.ProjectionExt+import Data.Comp.Multi hiding (Alg, cata, unTerm, project, inject, Term)+import Data.Comp.Multi.Derive hiding (smartConstructors)+
+ src/Control/Monad/IMSOS/LayeredAlgebras.hs view
@@ -0,0 +1,49 @@++{-# LANGUAGE GADTs #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+++module Control.Monad.IMSOS.LayeredAlgebras where++import Data.Comp.Multi ( HFunctor (..), (:->) )+import Data.Kind (Type)+import Control.Monad.IMSOS.Signatures (Sort, Sig)+import Control.Monad.IMSOS.LayeredTerms (Term (..))+import qualified Data.Comp.Multi as Multi+import Data.Comp.Multi.Ops++type Alg l c = Multi.Alg (Sig l) c++class HasAlg f e where+ sem :: Multi.Alg f (GetCarrier e)++-- TODO can I get rid of this?+newtype GetCarrier e i = GetCarrier { getCarrier :: CarrierOf e i i }++class HasCarrier e s where+ type CarrierOf e s :: Sort -> Type++instance (HasAlg f e, HasAlg g e)+ => HasAlg (f :+: g) e where+ sem (Inl f) = sem @f f+ sem (Inr g) = sem @g g++cata :: forall l c. Alg l c -> Term l :-> c+cata f = go + where go :: forall j. Term l j -> c j+ go (Term layer) = f (inj (hfmap go layer))++eval :: forall eval l sort. + (HasAlg (Sig l) eval)+ => Term l sort -> GetCarrier eval sort +eval = cata (sem @(Sig l) @eval)
+ src/Control/Monad/IMSOS/LayeredDeriving.hs view
@@ -0,0 +1,212 @@+{-# LANGUAGE TemplateHaskell #-}+--------------------------------------------------------------------------------+-- |+-- Module : Control.Monad.IMSOS.Derive.SmartConstructors+-- Description : Derive smart constructors for the indexed LTerm representation.+--+-- This is adapted from Data.Comp.Multi.Derive.SmartConstructors in+-- compdata-0.13.1. Unlike compdata's version, the generated constructors+-- build an LTerm layer:+--+-- LTerm (inj (Constructor ...))+--+-- rather than a compdata Cxt layer:+--+-- inject (Constructor ...)+--+-- Usage, assuming the corresponding names are in scope:+--+-- $(smartConstructorsLTerm ''LTerm ''HasSubSig ''SubSig ''Add)+--+-- For+--+-- data Add a i where+-- Add :: a Expr -> a Expr -> Add a Expr+--+-- this generates, up to alpha-renaming:+--+-- iAdd+-- :: ( HasSubSig l Expr+-- , Add :<: SubSig l Expr+-- )+-- => LTerm l Expr -> LTerm l Expr -> LTerm l Expr+-- iAdd x y = LTerm (inj (Add x y))+--+-- For non-nullary constructors the signature is deliberately inferred, as in+-- compdata's original implementation. This preserves the argument sorts of+-- GADT constructors (for example, Expr -> Bool operators).+--------------------------------------------------------------------------------+module Control.Monad.IMSOS.LayeredDeriving+ ( smartConstructorsLTerm+ , smartConstructors+ ) where++import Control.Arrow ((&&&))+import Control.Monad (liftM)+import Data.Comp.Derive.Utils+ ( DataInfo (..)+ , abstractConType+ , abstractNewtypeQ+ , isEqualP+ , newNames+ , tyVarBndrName+ )+import Data.Comp.Multi.Ops ((:<:), inj)+import Language.Haskell.TH hiding (Cxt)+import Control.Monad.IMSOS.LayeredTerms (Term(..))+import Control.Monad.IMSOS.Signatures (HasSubSig(..))++smartConstructors = smartConstructorsLTerm ''Term 'Term ''HasSubSig ''SubSig ++-- | Generate smart constructors for one signature functor, targeting a custom+-- sort-dependent layered term type.+--+-- The first three arguments identify the custom API:+--+-- * the GADT constructor/type name @LTerm@;+-- * the class name @HasSubSig@;+-- * the associated type-family name @SubSig@.+--+-- The last argument is the signature functor to inspect, for example @''Add@.+--+-- Passing names rather than importing the IMSOS modules makes this derivation+-- module independent of a particular LTerm implementation.++-- | Generate smart constructors for one signature functor, targeting a custom+-- sort-dependent LTerm.+--+-- Example:+--+-- $(smartConstructorsLTerm ''LTerm ''HasSubSig ''SubSig ''Add)+smartConstructorsLTerm+ :: Name -- ^ LTerm type+ -> Name -- ^ LTerm constructor+ -> Name -- ^ HasSubSig class+ -> Name -- ^ SubSig associated type family+ -> Name -- ^ signature functor, for example Add+ -> Q [Dec]+smartConstructorsLTerm ltermName ltermCons hasSubSigName subSigName fname = do+ -- `abstractNewtypeQ` already takes a `Q Info`; do not fmap it over+ -- `reify fname`.+ Just (DataInfo _cxt tname targs constrs _deriving) <-+ abstractNewtypeQ (reify fname)++ let iVar = tyVarBndrName (last targs)+ cons = map (abstractConType &&& resultSort iVar) constrs++ liftM concat $+ mapM (genSmartConstr (map tyVarBndrName targs) tname) cons+ where+ -- GHC reifies a GADT result such as+ --+ -- Done :: Done r Commands+ --+ -- as a `GadtC` result type. Older reification styles can instead expose+ -- the refinement as an equality predicate, so support both forms.+ resultSort :: Name -> Con -> Maybe Type+ resultSort iVar (ForallC _ cxt con) =+ case [ y | Just (x, y) <- map isEqualP cxt, x == VarT iVar ] of+ tp : _ -> Just tp+ [] -> resultSort iVar con+ resultSort _ (GadtC _ _ resultType) = finalArgument resultType+ resultSort _ (RecGadtC _ _ resultType) = finalArgument resultType+ resultSort _ _ = Nothing++ finalArgument :: Type -> Maybe Type+ finalArgument (AppT _ x) = Just x+ finalArgument (SigT t _) = finalArgument t+ finalArgument (ParensT t) = finalArgument t+ finalArgument _ = Nothing++ genSmartConstr+ :: [Name]+ -> Name+ -> ((Name, Int), Maybe Type)+ -> Q [Dec]+ genSmartConstr targs' tname ((conName, arity), resultIndex) =+ genSmartConstr'+ targs'+ tname+ (mkName ('i' : nameBase conName))+ conName+ arity+ resultIndex++ genSmartConstr'+ :: [Name]+ -> Name+ -> Name+ -> Name+ -> Int+ -> Maybe Type+ -> Q [Dec]+ genSmartConstr' targs' tname smartName conName arity resultIndex = do+ varNs <- newNames arity "x"++ let pats = map varP varNs+ vars = map varE varNs+ layer = foldl appE (conE conName) vars+ body =+ appE (conE ltermCons)+ (appE (varE 'inj) layer)+ function =+ [ funD smartName+ [ clause pats (normalB body) []+ ]+ ]+ sig+ | arity == 0 =+ genNullarySig targs' tname smartName resultIndex+ | otherwise =+ []++ sequence (sig ++ function)++ -- For constructors with fields, leave the signature inferred. This+ -- preserves their actual GADT argument sorts. A nullary constructor+ -- needs an explicit signature to avoid monomorphism-restriction issues.+ genNullarySig+ :: [Name]+ -> Name+ -> Name+ -> Maybe Type+ -> [Q Dec]+ genNullarySig _ _ _ Nothing = []+ genNullarySig targs' tname smartName (Just indexType) =+ [ do+ lVar <- newName "l"++ let signatureParameters = init (init targs')++ atomicSignature =+ foldl appT (conT tname) (map varT signatureParameters)++ targetSubSig =+ conT subSigName+ `appT` varT lVar+ `appT` pure indexType++ output =+ conT ltermName+ `appT` varT lVar+ `appT` pure indexType++ hasSubSigConstraint =+ conT hasSubSigName+ `appT` varT lVar+ `appT` pure indexType++ embedsInSubSig =+ conT ''(:<:)+ `appT` atomicSignature+ `appT` targetSubSig++ quantified =+ PlainTV lVar SpecifiedSpec+ : map (`PlainTV` SpecifiedSpec) signatureParameters++ sigD smartName $+ forallT quantified+ (sequence [hasSubSigConstraint, embedsInSubSig])+ output+ ]
+ src/Control/Monad/IMSOS/LayeredTerms.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE UndecidableInstances #-}++-- | Module defining 'layered terms'.+-- At each layer, an operator from `SubSig l i` is applied+module Control.Monad.IMSOS.LayeredTerms where+import Control.Monad.IMSOS.Signatures (HasSubSig(..), TermL, Sig)+import Data.Comp.Multi ((:<:), inj, proj, HFunctor (hfmap), inject)+import qualified Data.Comp.Multi as Multi++data Term l i where + Term ::+ ( HasSubSig l i ) => + SubSig l i (Term l) i -> Term l i++unTerm :: Term l i -> SubSig l i (Term l) i+unTerm (Term l) = l++inject :: (f :<: SubSig l i, HasSubSig l i) + => f (Term l) i -> Term l i+inject = Term . inj++project :: (f :<: SubSig l i) => Term l i -> Maybe (f (Term l) i)+project = proj . unTerm++fixL :: forall l i. Term l i -> TermL l i+fixL = go+ where+ go :: forall j. Term l j -> TermL l j+ go (Term layer) =+ Data.Comp.Multi.inject (hfmap go layer)++-- TODO: instances below might cause significant overhead due to fixing+instance + (Show (Multi.Term (Sig l) i)) + => Show (Term l i) where + show = show . fixL++instance + (Ord (Multi.Term (Sig l) i))+ => Ord (Term l i) where + compare l r = compare (fixL l) (fixL r)++instance + (Eq (Multi.Term (Sig l) i))+ => Eq (Term l i) where + l == r = fixL l == fixL r
+ src/Control/Monad/IMSOS/Monad.hs view
@@ -0,0 +1,163 @@+{-# LANGUAGE UndecidableInstances+ , FlexibleInstances+ , FlexibleContexts+ , MultiParamTypeClasses+ , TypeOperators+ , LambdaCase+ , ScopedTypeVariables+ , TypeApplications+ , TupleSections+ , AllowAmbiguousTypes+#-}++module Control.Monad.IMSOS.Monad+ (MonadIMSOS(..), runIMSOS, yieldIMSOS+ ,Control.Monad.IMSOS.Monad.tell, Control.Monad.IMSOS.Monad.listen, Control.Monad.IMSOS.Monad.censor+ ,Control.Monad.IMSOS.Monad.local, Control.Monad.IMSOS.Monad.reader, Control.Monad.IMSOS.Monad.ask+ ,Control.Monad.IMSOS.Monad.get, Control.Monad.IMSOS.Monad.put, Control.Monad.IMSOS.Monad.modify+ ,fail, throwError, catchError+ ,guard, (<|>)+ ) where++import Control.Applicative (Alternative(..))+import Control.Monad.Error.Class+import Control.Monad.Reader (MonadReader(..))+import Control.Monad.State (MonadState(..), modify)+import Control.Monad.Writer (MonadWriter(..), censor)+import Control.Monad (ap, join, guard)++import Data.Foldable (toList)+import Data.Comp.ProjectionExt ((:*:)(..), (:<~), (:<|), pr, uncons, recons, modify)++newtype MonadIMSOS r s w me e fl a = MonadIMSOS {+ unwrap :: r -> s -> me e (fl (a, s, w))+ }++instance (Functor (me e), Functor fl) => Functor (MonadIMSOS r s w me e fl) where+ fmap f (MonadIMSOS m) = MonadIMSOS m'+ where m' r s = fmap modify' <$> m r s+ where modify' (a, s', w) = (f a, s', w)++instance (Monad (me e), Monoid w, Traversable fl, Monad fl)+ => Monad (MonadIMSOS r s w me e fl) where+ (MonadIMSOS p) >>= m = MonadIMSOS $ \r1 s1 -> do+ p_res <- p r1 s1+ let modify' (a, s2, w2) = fmap (,w2) <$> q r1 s2+ where MonadIMSOS q = m a+ q_ress <- mapM modify' p_res+ let mod2 :: ((b, s, w), w) -> (b, s, w)+ mod2 ((b, s3, w3), w2) = (b, s3, w2 <> w3)+ return (fmap mod2 (join q_ress))++instance (Monad (me e), Monoid w, Monad fl, Traversable fl)+ => Applicative (MonadIMSOS r s w me e fl) where+ pure a = MonadIMSOS m+ where m _ s = pure (pure (a, s, mempty))+ (<*>) = ap++instance (Monad (me e), MonadError e (me e), Monoid w+ ,Alternative fl, Monad fl, Traversable fl)+ => Alternative (MonadIMSOS r s w me e fl) where+ empty = MonadIMSOS (\_ _ -> return empty)+ (MonadIMSOS p) <|> (MonadIMSOS q) = MonadIMSOS m+ where m r s = tryError (p r s) >>= \case+ Left _ -> q r s+ Right p_res -> tryError (q r s) >>= \case+ Left _ -> return empty+ Right q_res -> return (p_res <|> q_res)++instance {-# INCOHERENT #-}+ (MonadError String (me String), Monad (me String), Monoid w, Traversable fl, Monad fl)+ => MonadFail (MonadIMSOS r s w me String fl) where+ fail = throwError++instance (MonadError e (me e), Monad (me e), Monoid w, Monad fl, Traversable fl)+ => MonadReader r (MonadIMSOS r s w me e fl) where+ ask = MonadIMSOS m+ where m r s = pure $ pure (r, s, mempty)+ local f (MonadIMSOS p) = MonadIMSOS m+ where m r s = p (f r) s++instance (MonadError e (me e), Monad (me e), Monoid w, Monad fl, Traversable fl)+ => MonadState s (MonadIMSOS r s w me e fl) where+ state act = MonadIMSOS m+ where m _ s = pure $ pure (a, s', mempty)+ where (a, s') = act s++instance (MonadError e (me e), Monad (me e), Monoid w, Monad fl, Traversable fl)+ => MonadWriter w (MonadIMSOS r s w me e fl) where+ tell w = MonadIMSOS m+ where m _ s = pure $ pure ((), s, w)+ listen (MonadIMSOS p) = MonadIMSOS m+ where m r s = p r s >>= \as -> pure (fmap modify' as)+ where modify' (a, s', w) = ((a, w), s', w)+ pass (MonadIMSOS p) = MonadIMSOS m+ where m r s = p r s >>= \as -> pure (fmap modify' as)+ where modify' ((a,f), s', w) = (a, s', f w)++instance (MonadError e (me e), Monad (me e), Monoid w, Traversable fl, Monad fl)+ => MonadError e (MonadIMSOS r s w me e fl) where+ throwError e = MonadIMSOS m+ where m _ _ = throwError e+ catchError (MonadIMSOS p) h = MonadIMSOS m+ where m r s = p r s `catchError` \e ->+ let MonadIMSOS q = h e+ in q r s -- continues with a state as if p not executed++class Unifies a where+ unify :: [a] -> a++search :: (Unifies s, Unifies w, Monad (me e), Applicative fl, Foldable fl)+ => MonadIMSOS r s w me e fl a -> MonadIMSOS r s w me e fl [a]+search (MonadIMSOS p) = MonadIMSOS $ \r s -> do+ (as, ss, ws) <- unzip3 . toList <$> p r s+ return $ pure (as, unify ss, unify ws)++runIMSOS :: (MonadError e (me e))+ => r -> s -> MonadIMSOS r s w me e fl a -> me e (fl (a, s, w))+runIMSOS r s (MonadIMSOS f) = f r s++yieldIMSOS :: (Functor (me e), Functor fl, MonadFail (me e), MonadError e (me e)) =>+ r -> s -> ((a, s, w) -> y) -> MonadIMSOS r s w me e fl a -> me e (fl y)+yieldIMSOS r s toyield m = fmap toyield <$> runIMSOS r s m++-- ------------------------------+-- --- Auxiliary entities +-- ------------------------------++instance (Semigroup f, Semigroup g)+ => Semigroup (f :*: g) where+ (l1 :*: r1) <> (l2 :*: r2) = (l1 <> l2) :*: (r1 <> r2)++instance (Monoid f, Monoid g)+ => Monoid (f :*: g) where+ mempty = mempty :*: mempty++tell :: forall f w m. (f :<| w, MonadWriter w m) => f -> m ()+tell f = Control.Monad.Writer.tell (recons rem_ f)+ where (_, rem_) = uncons @f (mempty @w)++listen :: forall f w m a. (f :<~ w, MonadWriter w m) => m a -> m (a, f)+listen m = fmap (pr @f) <$> Control.Monad.Writer.listen m++censor :: forall f w m a. (f :<| w, MonadWriter w m) => (f -> f) -> m a -> m a+censor tr = Control.Monad.Writer.censor (Data.Comp.ProjectionExt.modify @f tr)++get :: forall f s m. (f :<~ s, MonadState s m) => m f+get = pr @f <$> Control.Monad.State.get++put :: forall f s m. (f :<| s, MonadState s m) => f -> m ()+put f = Control.Monad.IMSOS.Monad.modify (const f)++modify :: forall f s m. (f :<| s, MonadState s m) => (f -> f) -> m ()+modify tr = Control.Monad.State.modify (Data.Comp.ProjectionExt.modify @f tr)++ask :: forall f r m. (f :<~ r, MonadReader r m) => m f+ask = pr @f <$> Control.Monad.Reader.ask++reader :: forall f r m a. (f :<| r, MonadReader r m) => (f -> a) -> m a+reader tr = Control.Monad.Reader.reader tr'+ where tr' r = tr (pr @f r)++local :: forall f r m a. (f :<| r, MonadReader r m) => (f -> f) -> m a -> m a+local tr = Control.Monad.Reader.local (Data.Comp.ProjectionExt.modify tr)
+ src/Control/Monad/IMSOS/Rules/LayeredStep.hs view
@@ -0,0 +1,200 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}++module Control.Monad.IMSOS.Rules.LayeredStep where++import Data.Comp.Multi.Ops ((:+:)(..), (:<:))++import Control.Monad.IMSOS.Rules.Relations+import Data.Comp.ProjectionExt ((:<~), pr)+import Control.Monad.IMSOS.Monad (listen, get, MonadIMSOS)+import Control.Monad.IMSOS.Signatures+import Data.Kind (Type)+import Control.Monad.Error.Class (MonadError(throwError))+import Control.Monad.IMSOS.LayeredTerms (Term, unTerm, inject, project)+import Data.Maybe (isJust)+import Control.Applicative (Alternative(empty))++-- ───────────────────────────────────────────────+-- Rules+-- ───────────────────────────────────────────────++data StepRes (l :: Lang) (e :: Rel) (i :: Type) where+ -- | the constraint on NoStep ensures only value operators are used+ -- forcing the use of NoStep and ValOps mechanisms to be consistent+ NoStep :: (IsValOp e l i f) => f (Term l) i -> StepRes l e i+ Step :: (IsLangOp l i f) => f (Term l) i -> StepRes l e i++-- |+-- StepDefined is the main class to define by users to express I-MSOS rules+-- instances are identified by `l` (language), `e` (relation) and `f` (operator)+-- the sort `i` of the operator is implicit+-- note that in StepDefined the order of the `e` and `l` is purposefully different+-- compared to the helper functions defined below+--+class+ StepDefined (l :: Lang) (e :: Rel) (f :: Signature) where+ step :: forall i. f (Term l) i -> SemMonad l e (StepRes l e i)++premiseE :: forall e l i.+ ( e `GivesSemanticsTo` l+ , HasRulesErrors (SemError l e)+ , StepsAvailable e l i+ )+ => Term l i -> SemMonad l e (Term l i)+premiseE t = step @l @e t' >>= \case+ NoStep _ -> throwError (stepOnValueError (getRelID @e) t')+ Step t'' -> return (inject t'')+ where t' = unTerm t++premise :: forall e l i.+ ( e `GivesSemanticsTo` l+ , StepsAvailable e l i+ )+ => Term l i -> SemMonad l e (Term l i)+premise t = step @l @e t' >>= \case+ NoStep _ -> empty + Step t'' -> return (inject t'')+ where t' = unTerm t++notrans :: forall e l i f. (e `GivesSemanticsTo` l, IsValOp e l i f)+ => f (Term l) i -> SemMonad l e (StepRes l e i)+notrans = return . NoStep++trans :: forall e l i f. (e `GivesSemanticsTo` l, IsLangOp l i f)+ => f (Term l) i -> SemMonad l e (StepRes l e i)+trans = return . Step++steps :: forall e l f i.+ ( e `GivesSemanticsTo` l+ , StepsAvailable e l i+ , IsValOp e l i f )+ => Term l i -> SemMonad l e (f (Term l) i)+steps t | Just v <- toValue @f @e @l t = return v+ | otherwise = premise @e @l t >>= steps @e @l++stepsOrHalt :: forall e h l f i.+ ( e `GivesSemanticsTo` l+ , StepsAvailable e l i+ , IsValOp e l i f+ , h :<~ SemWriter l e, Monoid h, Eq h+ )+ => Term l i -> SemMonad l e (Either (Term l i) (f (Term l) i))+stepsOrHalt = stepsOrHaltWhen @e @l @f predicate+ where predicate _ _ w = pr @h w /= mempty++stepsOrHaltWhen :: forall e l f i.+ ( e `GivesSemanticsTo` l+ , StepsAvailable e l i+ , IsValOp e l i f+ )+ => (Term l i -> SemState l e -> SemWriter l e -> Bool) -- predicate over an I-MSOS configuration+ -> Term l i -- consisting of term after step, state and writer values+ -> SemMonad l e (Either (Term l i) (f (Term l) i))+stepsOrHaltWhen predicate t+ | Just v <- toValue @f @e @l t = return $ Right v+ | otherwise = do+ (t', w) <- listen @(SemWriter l e) (premise @e @l t)+ s <- get @(SemState l e)+ if predicate t' s w then return $ Left t'+ else stepsOrHaltWhen @e @l predicate t'++instance+ ( StepDefined l e f+ , StepDefined l e g+ )+ => StepDefined l e (f :+: g) where+ step (Inl f) = step @l @e f+ step (Inr g) = step @l @e g+++-- | A transition relation identifies which subsignature of a language's+-- signature identify the value operations of a particular sort +-- this will be used to determine termination of computations+-- according to the relation+class (ValOps l e i :<: SubSig l i, ValOps l e i :<: Sig l, HasSubSig l i) =>+ HasValOps (l :: Lang) (e :: Rel) (i :: Sort) where+ type ValOps l e i :: Signature++-- | ValOp are those terms that are built only from value operators+-- on the outermost level. +-- Useful to build semantic entities. +-- Only outermost operator is considered to support thunk-like values+type ValOp l e i = ValOps l e i (Term l) i++-- | Tests whether a term is a ValOp and converts if possible +toValue :: forall f e l i.+ (HasValOps l e i, f :<: ValOps l e i, f :<: SubSig l i) =>+ Term l i -> Maybe (f (Term l) i)+toValue = project @f++-- | Determine whether a term is constructed by a value constructor+-- of the relevant sort `i` (for a given `e` and `l`)+isValue :: forall e l i.+ (HasValOps l e i)+ => Term l i -> Bool+isValue = isJust . toValue @(ValOps l e i) @e @l @i++-- ───────────────────────────────────────────────+-- Error handling+-- ───────────────────────────────────────────────++class HasRulesErrors e where+ ruleAssertionError :: String -> e+ noApplicableRulesError :: symb -> f (Term l) i -> e+ stepOnValueError :: symb -> f (Term l) i -> e++instance (HasRulesErrors e, MonadError e (me e), Monoid w+ ,Traversable fl, Monad fl) =>+ MonadFail (MonadIMSOS r s w me e fl) where+ fail = throwError . ruleAssertionError @e++instance HasRulesErrors String where+ ruleAssertionError = id+ noApplicableRulesError _ _ = "No rules applicable to term"+ stepOnValueError _ _ = "attempting to step on value term"++type StepAvailable e l i =+ ( RelID e+ , StepDefined l e (SubSig l i))++type StepsAvailable e l i =+ (StepAvailable e l i+ ,HasSubSig l i+ ,HasValOps l e i)++type StepsTo e l i f =+ (StepsAvailable e l i+ ,IsValOp e l i f)++type IsValOp e l i f =+ (f :<: ValOps l e i+ ,IsLangOp l i f)++type IsLangOp l i f =+ (f :<: Sig l+ ,f :<: SubSig l i+ ,HasSubSig l i)++type SharedSemEntities l (e :: Rel) (e' :: Rel) =+ ( SemEntities l e+ , SemEntities l e+ , SemAlternative l e ~ SemAlternative l e'+ , SemMonadError l e ~ SemMonadError l e'+ , SemError l e ~ SemError l e'+ , SemState l e ~ SemState l e'+ , SemWriter l e ~ SemWriter l e'+ , SemReader l e ~ SemReader l e'+ )
+ src/Control/Monad/IMSOS/Rules/Relations.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}++module Control.Monad.IMSOS.Rules.Relations where++--import Data.Comp.Multi.Term (Term)++import Control.Monad.IMSOS.Monad (MonadIMSOS, runIMSOS)+import Control.Monad.IMSOS.Signatures+import Control.Applicative (Alternative(..))+import Control.Monad.Error.Class (MonadError(..))+import Data.Kind (Type)++-- |+-- Type class for introducing symbols used to identify transition _relations_+--+type Rel = Type+class RelID e where+ getRelID :: e++-- | Tags are used to abstract over transition relations, independent of sort+type Tag = Type++-- | Type family that gives a specific Rel for a given Tag and Sort +class HasTaggedRel (tag :: Tag) (s :: Sort) (i :: Sort) where + data TaggedRel tag s i :: Rel ++-- | Transition relations have associated semantic entities+-- and deal with non-determinism in a certain way (Alternative)+class ( Alternative (SemAlternative l e)+ , Monad (SemAlternative l e)+ , Traversable (SemAlternative l e)+ , MonadError (SemError l e) (SemMonadError l e (SemError l e))+ , Monoid (SemWriter l e)+ , HasDefaults l e+ ) => SemEntities (l :: Lang) (e :: Rel) where+ type SemReader l e :: Type+ type SemState l e :: Type+ type SemWriter l e :: Type+ type SemAlternative l e :: Type -> Type+ type SemError l e :: Type+ type SemMonadError l e :: Type -> Type -> Type++type SemMonad l e =+ MonadIMSOS (SemReader l e) (SemState l e) (SemWriter l e)+ (SemMonadError l e) (SemError l e) (SemAlternative l e)++type GivesSemanticsTo e l = (SemEntities l e, Language l, RelID e)++class Monoid (SemWriter l e) => HasDefaults l e where+ defaultReader :: SemReader l e+ defaultState :: SemState l e++runSem :: forall e l a.+ ( HasDefaults l e+ , SemEntities l e+ ) => SemMonad l e a -> SemMonadError l e (SemError l e) (SemAlternative l e (a, SemState l e, SemWriter l e))+runSem = runIMSOS (defaultReader @l @e) (defaultState @l @e)
+ src/Control/Monad/IMSOS/Rules/Step.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}++module Control.Monad.IMSOS.Rules.Step where++import Data.Comp.Multi.Ops ((:+:)(..), (:<:))++import Control.Monad.IMSOS.Rules.Relations+import Data.Comp.ProjectionExt ((:<~), pr)+import Control.Monad.IMSOS.Monad (listen, get, MonadIMSOS)+import Control.Monad.IMSOS.Signatures+import Data.Kind (Type)+import Data.Comp.Multi.Term (Term)+import Control.Monad.Error.Class (MonadError(throwError))+import Data.Comp.Multi.Sum (inject)+import Data.Comp.Multi ( unTerm, project )+import Data.Maybe (isJust)+import Control.Applicative (Alternative(empty))++-- ───────────────────────────────────────────────+-- Rules+-- ───────────────────────────────────────────────++data StepRes (l :: Lang) (e :: Sort -> Rel) (i :: Type) where+ -- | the constraint on NoStep ensures only value operators are used+ -- forcing the use of NoStep and ValOps mechanisms to be consistent+ NoStep :: (IsValOp e l i f) => f (Term (Sig l)) i -> StepRes l e i+ Step :: (IsLangOp l i f) => f (Term (Sig l)) i -> StepRes l e i++-- |+-- StepDefined is the main class to define by users to express I-MSOS rules+-- instances are identified by `l` (language), `e` (relation) and `f` (operator)+-- the sort `i` of the operator is implicit+-- note that in StepDefined the order of the `e` and `l` is purposefully different+-- compared to the helper functions defined below+--+class+ StepDefined (l :: Lang) (t :: Type) (f :: Signature) where+ step :: forall i. f (TermL l) i -> SemMonad l (TaggedRel t i i) (StepRes l (TaggedRel t i) i)++premiseE :: forall t l i.+ ( StepAvailable t l i+ , HasRulesErrors (SemError l (TaggedRel t i i))+ , TaggedRel t i i `GivesSemanticsTo` l+ )+ => TermL l i -> SemMonad l (TaggedRel t i i) (TermL l i)+premiseE t = step @l @t t' >>= \case+ NoStep _ -> throwError (stepOnValueError (getRelID @(TaggedRel t i i)) t')+ Step t'' -> return (inject t'')+ where t' = unTerm t++premise :: forall t l i.+ ( StepAvailable t l i+ , TaggedRel t i i `GivesSemanticsTo` l+ )+ => TermL l i -> SemMonad l (TaggedRel t i i) (TermL l i)+premise t = step @l @t t' >>= \case+ NoStep _ -> empty+ Step t'' -> return (inject t'')+ where t' = unTerm t++notrans :: forall e l i f. (e i `GivesSemanticsTo` l, IsValOp e l i f)+ => f (TermL l) i -> SemMonad l (e i) (StepRes l e i)+notrans = return . NoStep++trans :: forall e l i f. (e i `GivesSemanticsTo` l, IsLangOp l i f)+ => f (TermL l) i -> SemMonad l (e i) (StepRes l e i)+trans = return . Step++steps :: forall (t :: Tag) l f i.+ ( TaggedRel t i i `GivesSemanticsTo` l+ , StepsAvailable t l i+ , IsValOp (TaggedRel t i) l i f )+ => TermL l i -> SemMonad l (TaggedRel t i i) (f (TermL l) i)+steps t | Just v <- toValue @f @t @l t = return v+ | otherwise = premise @t @l t >>= steps @t @l++stepsOrHalt :: forall (t :: Tag) h l f i.+ ( TaggedRel t i i `GivesSemanticsTo` l+ , StepsAvailable t l i+ , IsValOp (TaggedRel t i) l i f+ , h :<~ SemWriter l (TaggedRel t i i), Monoid h, Eq h+ )+ => TermL l i -> SemMonad l (TaggedRel t i i) (Either (TermL l i) (f (TermL l) i))+stepsOrHalt = stepsOrHaltWhen @t @l @f predicate+ where predicate _ _ w = pr @h w /= mempty++stepsOrHaltWhen :: forall (t :: Tag) l f i.+ ( TaggedRel t i i `GivesSemanticsTo` l+ , StepsAvailable t l i+ , IsValOp (TaggedRel t i) l i f+ )+ => (TermL l i -> SemState l (TaggedRel t i i) -> SemWriter l (TaggedRel t i i) -> Bool) -- predicate over an I-MSOS configuration+ -> TermL l i -- consisting of term after step, state and writer values+ -> SemMonad l (TaggedRel t i i) (Either (TermL l i) (f (TermL l) i))+stepsOrHaltWhen predicate t+ | Just v <- toValue @f @t @l t = return $ Right v+ | otherwise = do+ (t', w) <- listen @(SemWriter l (TaggedRel t i i)) (premise @t @l t)+ s <- get @(SemState l (TaggedRel t i i))+ if predicate t' s w then return $ Left t'+ else stepsOrHaltWhen @t @l predicate t'++instance+ ( StepDefined l e f+ , StepDefined l e g+ )+ => StepDefined l e (f :+: g) where+ step (Inl f) = step @l @e f+ step (Inr g) = step @l @e g+++-- | A transition relation identifies which subsignature of a language's+-- signature identify the value operations of a particular sort +-- this will be used to determine termination of computations+-- according to the relation+class (ValOps l e i :<: Sig l) =>+ HasValOps (l :: Lang) (e :: Sort -> Rel) (i :: Sort) where+ type ValOps l e i :: Signature++-- | ValOp are those terms that are built only from value operators+-- on the outermost level. +-- Useful to build semantic entities. +-- Only outermost operator is considered to support thunk-like values+type ValOp l e i = ValOps l e i (TermL l) i++-- | Tests whether a term is a ValOp and converts if possible +toValue :: forall f t l i.+ (HasValOps l (TaggedRel t i) i, f :<: ValOps l (TaggedRel t i) i, f :<: Sig l) =>+ TermL l i -> Maybe (f (TermL l) i)+toValue = project @f++-- | Determine whether a term is constructed by a value constructor+-- of the relevant sort `i` (for a given `e` and `l`)+isValue :: forall t l i.+ (HasValOps l (TaggedRel t i) i)+ => TermL l i -> Bool+isValue = isJust . toValue @(ValOps l (TaggedRel t i) i) @t @l @i++-- ───────────────────────────────────────────────+-- Error handling+-- ───────────────────────────────────────────────++class HasRulesErrors e where+ ruleAssertionError :: String -> e+ noApplicableRulesError :: symb -> f (Term g) i -> e+ stepOnValueError :: symb -> f (Term g) i -> e++instance (HasRulesErrors e, MonadError e (me e), Monoid w+ ,Traversable fl, Monad fl) =>+ MonadFail (MonadIMSOS r s w me e fl) where+ fail = throwError . ruleAssertionError @e++instance HasRulesErrors String where+ ruleAssertionError = id+ noApplicableRulesError _ _ = "No rules applicable to term"+ stepOnValueError _ _ = "attempting to step on value term"++type StepAvailable t l i =+ ( RelID (TaggedRel t i i)+ , StepDefined l t (Sig l))++type StepsAvailable t l i =+ (StepAvailable t l i+ ,HasValOps l (TaggedRel t i) i)++type StepsTo t l i f =+ (StepsAvailable t l i+ ,IsValOp (TaggedRel t i) l i f)++type IsValOp e l i f =+ (f :<: ValOps l e i+ ,IsLangOp l i f)++type IsLangOp l i f =+ (f :<: Sig l)++type SharedSemEntities l (t :: Tag) i j =+ ( SemEntities l (TaggedRel t i i)+ , SemEntities l (TaggedRel t j j)+ , SemAlternative l (TaggedRel t i i) ~ SemAlternative l (TaggedRel t j j)+ , SemMonadError l (TaggedRel t i i) ~ SemMonadError l (TaggedRel t j j)+ , SemError l (TaggedRel t i i) ~ SemError l (TaggedRel t j j)+ , SemState l (TaggedRel t i i) ~ SemState l (TaggedRel t j j)+ , SemWriter l (TaggedRel t i i) ~ SemWriter l (TaggedRel t j j)+ , SemReader l (TaggedRel t i i) ~ SemReader l (TaggedRel t j j)+ )
+ src/Control/Monad/IMSOS/Signatures.hs view
@@ -0,0 +1,65 @@++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeApplications #-}++module Control.Monad.IMSOS.Signatures where++import Data.Kind (Type)+import Data.Comp.Multi.Term (Term)+import Data.Comp.Multi ( (:<:), (:+:), HFunctor )+import Data.Comp.Multi.Sum (project)++-- | A 'language' l captures the abstract syntax of a language +-- by specifying a signature ++type Lang = Type++-- | A language consists of several sorts +type Sort = Type++-- | The kind of signatures according to compdata (multi) library+type Signature = (Sort -> Type) -> Sort -> Type++class Language (l :: Lang) where+ type Sorts l :: [Sort]++-- | Subsignatures enable defining transition relations specific to +-- parts of the signature, as represented by a sort +class (Language l, HFunctor (SubSig l i), SubSig l i :<: Sig l) =>+ HasSubSig (l :: Lang) (i :: Sort) where+ type SubSig l i :: Signature++-- | Fold a type-level list of signatures into a nested coproduct+type family SumSigs (fs :: [Signature]) :: Signature where+ SumSigs '[f] = f+ SumSigs (f ': fs) = f :+: SumSigs fs++type family MapSubSig (l :: Lang) (sorts :: [Sort]) :: [Signature] where+ MapSubSig l '[] = '[]+ MapSubSig l (i ': is) = SubSig l i ': MapSubSig l is++-- | The signature of a language is automatically derived from the sorts+type Sig l = SumSigs (MapSubSig l (Sorts l))++-- | TermL is a convenience for referring to the type of terms of a language+type TermL l = Term (Sig l)++-- | Custom version of unTerm that ensures top-level operator is in relevant SubSig+-- Its usage is unsafe only in the case when an operator is of sort `i`+-- but is included in the signature for some other sort `j`+-- and either there is no semantics associated with the language or+-- there happens to be a semantics (instance of StepDefined) that yields+-- a result of sort j+-- unTerm :: forall l i. (SubSig l i :<: Sig l) => TermL l i -> SubSig l i (TermL l) i+-- unTerm t | Just t' <- project @(SubSig l i) t = t'+-- | otherwise = error "unTerm assertion failed: (Term (Sig l) i) not constructed by (SubSig l i)"
+ src/Data/Comp/ProjectionExt.hs view
@@ -0,0 +1,374 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeApplications #-}++-- ---------------------------------------------------------------------------+-- Module : Data.Comp.ProjectionExt+--+-- Extends Data.Comp.Projection with:+--+-- 1. Support for (:*:) products alongside tuples (,).+-- Because Elem is a closed type family in the original module,+-- we re-implement it here with the extra cases, and re-derive+-- everything on top of it. The original (:<) / pr are NOT+-- re-exported; use (:<~) and pr~ from this module instead.+--+-- 2. uncons — projects a component AND returns the remainder+-- of the product with that component removed, as an abstract type.+--+-- 3. recons — rebuilds the original product from a remainder+-- and a (possibly modified) projected component.+--+-- Supported product constructors:+-- (,) — Haskell built-in pair+-- (:*:) — GHC.Generics / Data.Functor.Product style functor product+-- (used at kind Type by applying both sides to a common index)+--+-- Adding further product constructors (e.g. Data.Strict.Pair) requires+-- one new equation in Elem, one in Remainder2, and two new instances+-- each in Proj, ProjR, and Reconstruct.+-- ---------------------------------------------------------------------------++module Data.Comp.ProjectionExt+ ( -- * Constraint and plain projection (replaces :< / pr)+ (:<~), (:<|), (:*:)(..)+ , pr+ -- * Projection with remainder+ , RemainderOf+ , uncons+ , recons+ , modify+ -- * Re-exported position machinery (for advanced use)+ , Elem+ ) where++import Data.Comp.SubsumeCommon+import Data.Kind (Type)++data a :*: b = a :*: b+infixr 6 :*:++-- ---------------------------------------------------------------------------+-- 1. Elem — closed type family recognising BOTH (,) and (:*:)+--+-- The structure exactly mirrors the original Elem, with every+-- tuple case duplicated for (:*:).+-- ---------------------------------------------------------------------------++type family Elem (f :: Type) (g :: Type) :: Emb where++ -- Base: exact match+ Elem f f = 'Found 'Here++ -- LHS is a tuple-pair: look for each component independently in g+ Elem (f1, f2) g = Sum' (Elem f1 g) (Elem f2 g)++ -- LHS is a (:*:)-pair: same idea+ Elem (f1 :*: f2) g = Sum' (Elem f1 g) (Elem f2 g)++ -- RHS is a tuple-pair: choose which side f lives in+ Elem f (g1, g2) = Choose (Elem f g1) (Elem f g2)++ -- RHS is a (:*:)-pair: same idea+ Elem f (g1 :*: g2) = Choose (Elem f g1) (Elem f g2)++ -- No match+ Elem f g = 'NotFound++-- ---------------------------------------------------------------------------+-- 2. Proj — plain projection, mirrors the original Proj class+-- but dispatches on both (,) and (:*:) at the value level.+-- ---------------------------------------------------------------------------++class Proj (e :: Emb) (p :: Type) (q :: Type) where+ proj' :: Proxy e -> q -> p++-- Base: p is q+instance Proj ('Found 'Here) f f where+ proj' _ x = x++-- p is in the left branch of a tuple+instance Proj ('Found pos) f g+ => Proj ('Found ('Le pos)) f (g, g') where+ proj' _ (l, _) = proj' (P :: Proxy ('Found pos)) l++-- p is in the right branch of a tuple+instance Proj ('Found pos) f g+ => Proj ('Found ('Ri pos)) f (g', g) where+ proj' _ (_, r) = proj' (P :: Proxy ('Found pos)) r++-- p is in the left branch of a (:*:)+instance Proj ('Found pos) f g+ => Proj ('Found ('Le pos)) f (g :*: g') where+ proj' _ (l :*: _) = proj' (P :: Proxy ('Found pos)) l++-- p is in the right branch of a (:*:)+instance Proj ('Found pos) f g+ => Proj ('Found ('Ri pos)) f (g' :*: g) where+ proj' _ (_ :*: r) = proj' (P :: Proxy ('Found pos)) r++-- p is a pair (f1, f2) scattered across q — tuple variant+instance ( Proj ('Found p1) f1 g+ , Proj ('Found p2) f2 g+ )+ => Proj ('Found ('Sum p1 p2)) (f1, f2) g where+ proj' _ x =+ ( proj' (P :: Proxy ('Found p1)) x+ , proj' (P :: Proxy ('Found p2)) x+ )++-- p is a pair (f1 :*: f2) scattered across q — (:*:) variant+instance ( Proj ('Found p1) f1 g+ , Proj ('Found p2) f2 g+ )+ => Proj ('Found ('Sum p1 p2)) (f1 :*: f2) g where+ proj' _ x =+ proj' (P :: Proxy ('Found p1)) x+ :*:+ proj' (P :: Proxy ('Found p2)) x++-- ---------------------------------------------------------------------------+-- 3. Public constraint and projection function (replaces :< / pr)+-- ---------------------------------------------------------------------------++-- | @p :\<~ q@ means @p@ is a uniquely-occurring component of @q@,+-- where @q@ may be built from either @(,)@ or @(':*:')@ products.+type p :<~ q = Proj (ComprEmb (Elem p q)) p q++-- | Project component @p@ out of product @q@.+-- Generalises 'Data.Comp.Projection.pr' to also handle @(':*:')@.+pr :: forall p q. (p :<~ q) => q -> p+pr = proj' (P :: Proxy (ComprEmb (Elem p q)))++-- ---------------------------------------------------------------------------+-- 4. Remainder type family+--+-- Remainder e p q is the type of q with the component at position e+-- removed. The result type depends on which product constructor was+-- used at each node, so we need cases for both (,) and (:*:).+-- ---------------------------------------------------------------------------++type family Remainder (e :: Emb) (p :: Type) (q :: Type) :: Type where++ -- Base: p was the whole product+ Remainder ('Found 'Here) p p = ()++ -- p was in the left branch of a tuple+ Remainder ('Found ('Le pos)) p (g, g') =+ (Remainder ('Found pos) p g, g')++ -- p was in the right branch of a tuple+ Remainder ('Found ('Ri pos)) p (g', g) =+ (g', Remainder ('Found pos) p g)++ -- p was in the left branch of a (:*:)+ Remainder ('Found ('Le pos)) p (g :*: g') =+ Remainder ('Found pos) p g :*: g'++ -- p was in the right branch of a (:*:)+ Remainder ('Found ('Ri pos)) p (g' :*: g) =+ g' :*: Remainder ('Found pos) p g++ -- p is a tuple-pair (f1, f2) scattered through q+ Remainder ('Found ('Sum pos1 pos2)) (f1, f2) q =+ Remainder ('Found pos2) f2 (Remainder ('Found pos1) f1 q)++ -- p is a (:*:)-pair (f1 :*: f2) scattered through q+ Remainder ('Found ('Sum pos1 pos2)) (f1 :*: f2) q =+ Remainder ('Found pos2) f2 (Remainder ('Found pos1) f1 q)++-- Convenience alias hiding the position evidence.+type RemainderOf p q = Remainder (ComprEmb (Elem p q)) p q++-- ---------------------------------------------------------------------------+-- 5. ProjR — projection that also returns the remainder+-- ---------------------------------------------------------------------------++class ProjR (e :: Emb) (p :: Type) (q :: Type) where+ prR' :: Proxy e -> q -> (p, Remainder e p q)++-- Base+instance ProjR ('Found 'Here) f f where+ prR' _ x = (x, ())++-- Left branch of a tuple+instance ProjR ('Found pos) f g+ => ProjR ('Found ('Le pos)) f (g, g') where+ prR' _ (l, r) =+ let (p, remL) = prR' (P :: Proxy ('Found pos)) l+ in (p, (remL, r))++-- Right branch of a tuple+instance ProjR ('Found pos) f g+ => ProjR ('Found ('Ri pos)) f (g', g) where+ prR' _ (l, r) =+ let (p, remR) = prR' (P :: Proxy ('Found pos)) r+ in (p, (l, remR))++-- Left branch of a (:*:)+instance ProjR ('Found pos) f g+ => ProjR ('Found ('Le pos)) f (g :*: g') where+ prR' _ (l :*: r) =+ let (p, remL) = prR' (P :: Proxy ('Found pos)) l+ in (p, remL :*: r)++-- Right branch of a (:*:)+instance ProjR ('Found pos) f g+ => ProjR ('Found ('Ri pos)) f (g' :*: g) where+ prR' _ (l :*: r) =+ let (p, remR) = prR' (P :: Proxy ('Found pos)) r+ in (p, l :*: remR)++-- Scattered tuple-pair (f1, f2)+instance ( ProjR ('Found pos1) f1 q+ , ProjR ('Found pos2) f2 (Remainder ('Found pos1) f1 q)+ )+ => ProjR ('Found ('Sum pos1 pos2)) (f1, f2) q where+ prR' _ x =+ let (f1, rem1) = prR' (P :: Proxy ('Found pos1)) x+ (f2, rem2) = prR' (P :: Proxy ('Found pos2)) rem1+ in ((f1, f2), rem2)++-- Scattered (:*:)-pair (f1 :*: f2)+instance ( ProjR ('Found pos1) f1 q+ , ProjR ('Found pos2) f2 (Remainder ('Found pos1) f1 q)+ )+ => ProjR ('Found ('Sum pos1 pos2)) (f1 :*: f2) q where+ prR' _ x =+ let (f1, rem1) = prR' (P :: Proxy ('Found pos1)) x+ (f2, rem2) = prR' (P :: Proxy ('Found pos2)) rem1+ in (f1 :*: f2, rem2)++-- ---------------------------------------------------------------------------+-- 6. Reconstruct — inverse of ProjR+-- ---------------------------------------------------------------------------++class Reconstruct (e :: Emb) (p :: Type) (q :: Type) where+ recons' :: Proxy e -> Remainder e p q -> p -> q++-- Base+instance Reconstruct ('Found 'Here) f f where+ recons' _ () x = x++-- Left branch of a tuple+instance Reconstruct ('Found pos) f g+ => Reconstruct ('Found ('Le pos)) f (g, g') where+ recons' _ (remL, r) p =+ (recons' (P :: Proxy ('Found pos)) remL p, r)++-- Right branch of a tuple+instance Reconstruct ('Found pos) f g+ => Reconstruct ('Found ('Ri pos)) f (g', g) where+ recons' _ (l, remR) p =+ (l, recons' (P :: Proxy ('Found pos)) remR p)++-- Left branch of a (:*:)+instance Reconstruct ('Found pos) f g+ => Reconstruct ('Found ('Le pos)) f (g :*: g') where+ recons' _ (remL :*: r) p =+ recons' (P :: Proxy ('Found pos)) remL p :*: r++-- Right branch of a (:*:)+instance Reconstruct ('Found pos) f g+ => Reconstruct ('Found ('Ri pos)) f (g' :*: g) where+ recons' _ (l :*: remR) p =+ l :*: recons' (P :: Proxy ('Found pos)) remR p++-- Scattered tuple-pair (f1, f2)+instance ( Reconstruct ('Found pos2) f2 (Remainder ('Found pos1) f1 q)+ , Reconstruct ('Found pos1) f1 q+ )+ => Reconstruct ('Found ('Sum pos1 pos2)) (f1, f2) q where+ recons' _ rem2 (f1, f2) =+ let rem1 = recons' (P :: Proxy ('Found pos2)) rem2 f2+ in recons' (P :: Proxy ('Found pos1)) rem1 f1++-- Scattered (:*:)-pair (f1 :*: f2)+instance ( Reconstruct ('Found pos2) f2 (Remainder ('Found pos1) f1 q)+ , Reconstruct ('Found pos1) f1 q+ )+ => Reconstruct ('Found ('Sum pos1 pos2)) (f1 :*: f2) q where+ recons' _ rem2 (f1 :*: f2) =+ let rem1 = recons' (P :: Proxy ('Found pos2)) rem2 f2+ in recons' (P :: Proxy ('Found pos1)) rem1 f1++-- ---------------------------------------------------------------------------+-- 7. Public API+-- ---------------------------------------------------------------------------++-- | Bundles all constraints needed for 'uncons' and 'recons'.+type p :<| q =+ ( ProjR (ComprEmb (Elem p q)) p q+ , Reconstruct (ComprEmb (Elem p q)) p q+ , p :<~ q+ )++-- | Project component @p@ from product @q@, also returning the remainder.+--+-- The remainder type is abstract — it should only be passed to 'recons'.+--+-- Law: @uncurry (flip recons) (uncons x) == x@+--+-- Examples:+-- uncons @Int (True, (42 :: Int, 'x')) == (42, (True, 'x'))+-- uncons @Bool (True :*: (42 :: Int)) == (True, () :*: 42)+uncons+ :: forall p q. (p :<| q)+ => q+ -> (p, RemainderOf p q)+uncons = prR' (P :: Proxy (ComprEmb (Elem p q)))++-- | Reconstruct the original product from a remainder and a+-- (possibly modified) projected component.+--+-- Examples:+-- let (n, rem) = uncons @Int (True, (42 :: Int, 'x'))+-- recons rem (n + 1) == (True, (43, 'x'))+--+-- let (b, rem) = uncons @Bool (True :*: (42 :: Int))+-- recons rem (not b) == (False :*: 42)+recons+ :: forall p q. (p :<| q)+ => RemainderOf p q+ -> p+ -> q+recons = recons' (P :: Proxy (ComprEmb (Elem p q)))++-- | Deconstruct and reconstruct a product, modifying a field+-- determined by the type of the given transformation function.+modify :: forall p q. (p :<| q)+ => (p -> p) -> q -> q+modify f q = recons rem (f p)+ where (p, rem) = uncons @p q++-- ---------------------------------------------------------------------------+-- 8. Sanity checks (unexported; remove before packaging)+-- ---------------------------------------------------------------------------++-- Tuple-only product+_t1 :: (Bool, (Int, Char))+_t1 = let (n, rem) = uncons @Int (True, (42 :: Int, 'x'))+ in recons rem (n + 1) -- (True, (43, 'x'))++-- (:*:)-only product+_t2 :: Bool :*: Int+_t2 = let (b, rem) = uncons @Bool (True :*: (42 :: Int))+ in recons rem (not b) -- (False :*: 42)++-- Mixed: tuple on the outside, (:*:) on the inside+_t3 :: (Bool :*: Int, Char)+_t3 = let (n, rem) = uncons @Int ((True :*: (42 :: Int)), 'x')+ in recons rem (n + 1) -- ((True :*: 43), 'x')++-- Mixed: (:*:) on the outside, tuple on the inside+_t4 :: Bool :*: Int :*: Char+_t4 = let (b, rem) = uncons @Bool (True :*: (42 :: Int) :*: 'x')+ in recons rem (not b) -- ((False, 42) :*: 'x')
+ src/Data/Comp/SubsumeCommon.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE PolyKinds #-}++--------------------------------------------------------------------------------+-- |+-- Module : Data.Comp.SubsumeCommon+-- Copyright : (c) 2014 Patrick Bahr+-- License : BSD3+-- Maintainer : Patrick Bahr <paba@diku.dk>+-- Stability : experimental+-- Portability : non-portable (GHC Extensions)+--+-- Shared parts of the implementation of signature subsumption for+-- both the base and the multi library.+--+--------------------------------------------------------------------------------++module Data.Comp.SubsumeCommon+ ( ComprEmb+ , Pos (..)+ , Emb (..)+ , Choose+ , Sum'+ , Proxy (..)+ ) where++-- | This type is used in its promoted form only. It represents+-- pointers from the left-hand side of a subsumption to the right-hand+-- side.+data Pos = Here | Le Pos | Ri Pos | Sum Pos Pos++-- | This type is used in its promoted form only. It represents+-- possible results for checking for subsumptions. 'Found' indicates a+-- subsumption was found; 'NotFound' indicates no such subsumption was+-- found. 'Ambiguous' indicates that there are duplicates on the left-+-- or the right-hand side.+data Emb = Found Pos | NotFound | Ambiguous++data Proxy a = P+++type family Choose (e1 :: Emb) (r :: Emb) :: Emb where+ Choose (Found x) (Found y) = Ambiguous+ Choose Ambiguous y = Ambiguous+ Choose x Ambiguous = Ambiguous+ Choose (Found x) y = Found (Le x)+ Choose x (Found y) = Found (Ri y)+ Choose x y = NotFound+++type family Sum' (e1 :: Emb) (r :: Emb) :: Emb where+ Sum' (Found x) (Found y) = Found (Sum x y)+ Sum' Ambiguous y = Ambiguous+ Sum' x Ambiguous = Ambiguous+ Sum' NotFound y = NotFound+ Sum' x NotFound = NotFound+++-- | This type family takes a position type and compresses it. That+-- means it replaces each nested occurrence of+--+-- @+-- Sum (prefix (Le Here)) (prefix (Ri Here))@+-- @+---+-- with+--+-- @+-- prefix Here@+-- @+--+-- where @prefix@ is some composition of @Le@ and @Ri@. The rational+-- behind this type family is that it provides a more compact proof+-- term of a subsumption, and thus yields more efficient+-- implementations of 'inj' and 'prj'.++type family ComprPos (p :: Pos) :: Pos where+ ComprPos Here = Here+ ComprPos (Le p) = Le (ComprPos p)+ ComprPos (Ri p) = Ri (ComprPos p)+ ComprPos (Sum l r) = CombineRec (ComprPos l) (ComprPos r)+++-- | Helper type family for 'ComprPos'. Note that we could have+-- defined this as a type synonym. But if we do that, performance+-- becomes abysmal. I presume that the reason for this huge impact on+-- performance lies in the fact that right-hand side of the defining+-- equation duplicates the two arguments @l@ and @r@.+type family CombineRec l r where+ CombineRec l r = CombineMaybe (Sum l r) (Combine l r)++-- | Helper type family for 'ComprPos'.+type family CombineMaybe (p :: Pos) (p' :: Maybe Pos) where+ CombineMaybe p (Just p') = p'+ CombineMaybe p p' = p+++-- | Helper type family for 'ComprPos'.+type family Combine (l :: Pos) (r :: Pos) :: Maybe Pos where+ Combine (Le l) (Le r) = Le' (Combine l r)+ Combine (Ri l) (Ri r) = Ri' (Combine l r)+ Combine (Le Here) (Ri Here) = Just Here+ Combine l r = Nothing++-- | 'Ri' lifted to 'Maybe'.+type family Ri' (p :: Maybe Pos) :: Maybe Pos where+ Ri' Nothing = Nothing+ Ri' (Just p) = Just (Ri p)++-- | 'Le' lifted to 'Maybe'.+type family Le' (p :: Maybe Pos) :: Maybe Pos where+ Le' Nothing = Nothing+ Le' (Just p) = Just (Le p)+++-- | If the argument is not 'Found', this type family is the+-- identity. Otherwise, the argument is of the form @Found p@, and+-- this type family does two things: (1) it checks whether @p@ the+-- contains duplicates; and (2) it compresses @p@ using 'ComprPos'. If+-- (1) finds no duplicates, @Found (ComprPos p)@ is returned;+-- otherwise @Ambiguous@ is returned.+--+-- For (1) it is assumed that @p@ does not contain 'Sum' nested+-- underneath a 'Le' or 'Ri' (i.e. only at the root or underneath a+-- 'Sum'). We will refer to such positions below as /atomic position/.+-- Positions not containing 'Sum' are called /simple positions/.+type family ComprEmb (e :: Emb) :: Emb where+ ComprEmb (Found p) = Check (Dupl p) (ComprPos p)+ ComprEmb e = e++-- | Helper type family for 'ComprEmb'.+type family Check (b :: Bool) (p :: Pos) where+ Check False p = Found p+ Check True p = Ambiguous++-- | This type family turns a list of /atomic position/ into a list of+-- /simple positions/ by recursively splitting each position of the+-- form @Sum p1 p2@ into @p1@ and @p2@.+type family ToList (s :: [Pos]) :: [Pos] where+ ToList (Sum p1 p2 ': s) = ToList (p1 ': p2 ': s)+ ToList (p ': s) = p ': ToList s+ ToList '[] = '[]++-- | This type checks whether the argument (atomic) position has+-- duplicates.+type Dupl s = Dupl' (ToList '[s])++-- | This type family checks whether the list of positions given as an+-- argument contains any duplicates.+type family Dupl' (s :: [Pos]) :: Bool where+ Dupl' (p ': r) = OrDupl' (Find p r) r+ Dupl' '[] = False++-- | This type family checks whether its first argument is contained+-- its second argument.+type family Find (p :: Pos) (s :: [Pos]) :: Bool where+ Find p (p ': r) = True+ Find p (p' ': r) = Find p r+ Find p '[] = False++-- | This type family returns @True@ if the first argument is true;+-- otherwise it checks the second argument for duplicates.+type family OrDupl' (a :: Bool) (b :: [Pos]) :: Bool where+ OrDupl' True c = True+ OrDupl' False c = Dupl' c