Safe Haskell | None |
---|
Basics for implementing functional EDSLs
- newtype Name = Name Integer
- data Construct a where
- Construct :: Signature sig => String -> Denotation sig -> Construct sig
- data Binding a where
- maxLam :: Binding :<: s => AST s a -> Name
- lam :: Binding :<: s => (ASTF s a -> ASTF s b) -> ASTF s (a -> b)
- fromDeBruijn :: Binding :<: sym => ASTF sym a -> ASTF sym a
- data BindingT a where
- maxLamT :: BindingT :<: s => AST s a -> Name
- lamT :: forall s a b. (BindingT :<: s, Typeable a) => (ASTF s a -> ASTF s b) -> ASTF s (a -> b)
- class BindingDomain sym where
- data MONAD m sig where
- newtype Remon sym m a where
- desugarMonad :: (MONAD m :<: sym, Monad m) => Remon sym m (ASTF sym a) -> ASTF sym (m a)
- type AlphaEnv = [(Name, Name)]
- alphaEq' :: (Equality sym, BindingDomain sym) => AlphaEnv -> ASTF sym a -> ASTF sym b -> Bool
- alphaEq :: (Equality sym, BindingDomain sym) => ASTF sym a -> ASTF sym b -> Bool
- type family Denotation sig
- class Eval s where
- evalSym :: s sig -> Denotation sig
- evalDen :: Eval s => AST s sig -> Denotation sig
- type family DenotationM m sig
- liftDenotationM :: forall m sig proxy1 proxy2. (Monad m, Signature sig) => proxy1 m -> proxy2 sig -> Denotation sig -> DenotationM m sig
- type RunEnv = [(Name, Dynamic)]
- class EvalEnv sym env where
- compileSym :: proxy env -> sym sig -> DenotationM (Reader env) sig
- compileSymDefault :: forall proxy env sym sig. (Eval sym, Signature sig) => proxy env -> sym sig -> DenotationM (Reader env) sig
- evalOpen :: EvalEnv sym env => env -> ASTF sym a -> a
- evalClosed :: EvalEnv sym RunEnv => ASTF sym a -> a
- class Ext ext orig where
- lookEnv :: forall env a e. Ext env (a, e) => Proxy e -> Reader env a
- data BindingWS a where
- lamWS :: forall a e sym b. BindingWS :<: sym => ((forall env. Ext env (a, e) => ASTF sym (Reader env a)) -> ASTF sym (Reader (a, e) b)) -> ASTF sym (Reader e (a -> b))
- evalOpenWS :: Eval s => env -> ASTF s (Reader env a) -> a
- evalClosedWS :: Eval s => ASTF s (Reader () a) -> a
- type family LiftReader env sig
- type family UnReader a
- type family LowerReader sig
- data ReaderSym sym a where
- ReaderSym :: (Signature sig, Denotation (LiftReader env sig) ~ DenotationM (Reader env) sig, LowerReader (LiftReader env sig) ~ sig) => Proxy env -> sym sig -> ReaderSym sym (LiftReader env sig)
- type WS sym env a = ASTF (BindingWS :+: ReaderSym sym) (Reader env a)
- fromWS :: WS sym env a -> ASTF (Binding :+: sym) a
- smartWS :: forall sig sig' bsym f sub sup env a. (Signature sig, Signature sig', sub :<: sup, bsym ~ (BindingWS :+: ReaderSym sup), f ~ SmartFun bsym sig', sig' ~ SmartSig f, bsym ~ SmartSym f, sig' ~ LiftReader env sig, Denotation (LiftReader env sig) ~ DenotationM (Reader env) sig, LowerReader (LiftReader env sig) ~ sig, Reader env a ~ DenResult sig') => sub sig -> f
Syntactic constructs
Variable name
Generic N-ary syntactic construct
Construct
gives a quick way to introduce a syntactic construct by giving its name and semantic
function.
Construct :: Signature sig => String -> Denotation sig -> Construct sig |
Variables and binders
StringTree Binding | |
Render Binding | |
Equality Binding |
|
BindingDomain Binding |
lam :: Binding :<: s => (ASTF s a -> ASTF s b) -> ASTF s (a -> b)Source
Higher-order interface for variable binding
Assumptions:
- The body
f
does not inspect its argument. - Applying
f
to a term with ordered binders results in a term with ordered binders [1].
[1] Ordered binders means that the names of Lam
nodes are decreasing along every path from
the root.
See "Using Circular Programs for Higher-Order Syntax" (ICFP 2013, http://www.cse.chalmers.se/~emax/documents/axelsson2013using.pdf).
Typed variables and binders
VarT :: Typeable a => Name -> BindingT (Full a) | |
LamT :: Typeable a => Name -> BindingT (b :-> Full (a -> b)) |
StringTree BindingT | |
Render BindingT | |
Equality BindingT |
|
BindingDomain BindingT | |
EvalEnv BindingT RunEnv |
lamT :: forall s a b. (BindingT :<: s, Typeable a) => (ASTF s a -> ASTF s b) -> ASTF s (a -> b)Source
Higher-order interface for typed variable binding
Assumptions:
- The body
f
does not inspect its argument. - Applying
f
to a term with ordered binders results in a term with ordered binders [1].
[1] Ordered binders means that the names of LamT
nodes are decreasing along every path from
the root.
See "Using Circular Programs for Higher-Order Syntax" (ICFP 2013, http://www.cse.chalmers.se/~emax/documents/axelsson2013using.pdf).
class BindingDomain sym whereSource
Domains that "might" include variables and binders
BindingDomain sym | |
BindingDomain BindingT | |
BindingDomain Binding | |
BindingDomain sym => BindingDomain (AST sym) | |
(BindingDomain sym1, BindingDomain sym2) => BindingDomain (:+: sym1 sym2) | |
BindingDomain sym => BindingDomain (:&: sym i) |
Monadic constructs
See "Generic Monadic Constructs for Embedded Languages" (Persson et al., IFL 2011 http://www.cse.chalmers.se/~emax/documents/persson2011generic.pdf).
newtype Remon sym m a whereSource
Reifiable monad
See "Generic Monadic Constructs for Embedded Languages" (Persson et al., IFL 2011 http://www.cse.chalmers.se/~emax/documents/persson2011generic.pdf).
It is advised to convert to/from Mon
using the Syntactic
instance provided in the modules
Data.Syntactic.Sugar.Monad
or Data.Syntactic.Sugar.MonadT
.
Monad m => Monad (Remon dom m) | |
Functor (Remon sym m) | |
Applicative m => Applicative (Remon sym m) | |
(Syntactic a, ~ (* -> *) (Domain a) sym, :<: Binding sym, :<: (MONAD m) sym, Monad m) => Syntactic (Remon sym m a) | |
(Syntactic a, ~ (* -> *) (Domain a) sym, :<: BindingT sym, :<: (MONAD m) sym, Monad m, Typeable (Internal a)) => Syntactic (Remon sym m a) |
desugarMonad :: (MONAD m :<: sym, Monad m) => Remon sym m (ASTF sym a) -> ASTF sym (m a)Source
One-layer desugaring of monadic actions
Alpha-equivalence
alphaEq :: (Equality sym, BindingDomain sym) => ASTF sym a -> ASTF sym b -> BoolSource
Alpha-equivalence
Evaluation
type family Denotation sig Source
Semantic function type of the given symbol signature
evalSym :: s sig -> Denotation sigSource
evalDen :: Eval s => AST s sig -> Denotation sigSource
Evaluation
type family DenotationM m sig Source
Monadic denotation; mapping from a symbol signature
a :-> b :-> Full c
to
m a -> m b -> m c
liftDenotationM :: forall m sig proxy1 proxy2. (Monad m, Signature sig) => proxy1 m -> proxy2 sig -> Denotation sig -> DenotationM m sigSource
Lift a Denotation
to DenotationM
class EvalEnv sym env whereSource
Evaluation
compileSym :: proxy env -> sym sig -> DenotationM (Reader env) sigSource
compileSymDefault :: forall proxy env sym sig. (Eval sym, Signature sig) => proxy env -> sym sig -> DenotationM (Reader env) sigSource
Simple implementation of compileSym
from a Denotation
evalClosed :: EvalEnv sym RunEnv => ASTF sym a -> aSource
Evaluation of closed terms where RunEnv
is used as the internal environment
(Note that there is no guarantee that the term is actually closed.)
Well-scoped terms
class Ext ext orig whereSource
Environment extension
lookEnv :: forall env a e. Ext env (a, e) => Proxy e -> Reader env aSource
Lookup in an extended environment
Well-scoped variable binding
Well-scoped terms are introduced to be able to evaluate without type casting. The implementation
is inspired by "Typing Dynamic Typing" (Baars and Swierstra, ICFP 2002,
http://doi.acm.org/10.1145/581478.581494) where expressions are represented as (essentially)
after "compilation". However, a major difference is that
"Typing Dynamic Typing" starts from an untyped term, and thus needs (safe) dynamic type casting
during compilation. In contrast, the denotational semantics of Reader
env aBindingWS
(the Eval
instance)
uses no type casting.
lamWS :: forall a e sym b. BindingWS :<: sym => ((forall env. Ext env (a, e) => ASTF sym (Reader env a)) -> ASTF sym (Reader (a, e) b)) -> ASTF sym (Reader e (a -> b))Source
Higher-order interface for well-scoped variable binding
Inspired by Conor McBride's I am not a number, I am a classy hack (http://mazzo.li/epilogue/index.html%3Fp=773.html).
evalOpenWS :: Eval s => env -> ASTF s (Reader env a) -> aSource
Evaluation of open well-scoped terms
evalClosedWS :: Eval s => ASTF s (Reader () a) -> aSource
Evaluation of closed well-scoped terms
type family LiftReader env sig Source
Mapping from a symbol signature
a :-> b :-> Full c
to
Reader env a :-> Reader env b :-> Full (Reader env c)
type family LowerReader sig Source
Mapping from a symbol signature
Reader e a :-> Reader e b :-> Full (Reader e c)
to
a :-> b :-> Full c
data ReaderSym sym a whereSource
Wrap a symbol to give it a LiftReader
signature
ReaderSym :: (Signature sig, Denotation (LiftReader env sig) ~ DenotationM (Reader env) sig, LowerReader (LiftReader env sig) ~ sig) => Proxy env -> sym sig -> ReaderSym sym (LiftReader env sig) |
smartWS :: forall sig sig' bsym f sub sup env a. (Signature sig, Signature sig', sub :<: sup, bsym ~ (BindingWS :+: ReaderSym sup), f ~ SmartFun bsym sig', sig' ~ SmartSig f, bsym ~ SmartSym f, sig' ~ LiftReader env sig, Denotation (LiftReader env sig) ~ DenotationM (Reader env) sig, LowerReader (LiftReader env sig) ~ sig, Reader env a ~ DenResult sig') => sub sig -> fSource
Make a smart constructor for well-scoped terms. smartWS
has any type of the form:
smartWS :: (sub :<: sup, bsym ~ (BindingWS :+: ReaderSym sup)) => sub (a :-> b :-> ... :-> Full x) -> ASTF bsym (Reader env a) -> ASTF bsym (Reader env b) -> ... -> ASTF bsym (Reader env x)