packages feed

unbound-generics 0.3.1 → 0.3.2

raw patch · 11 files changed

+1836/−28 lines, 11 filesdep +ansi-wl-pprintdep +exceptionsdep +semigroupsdep ~criteriondep ~deepseqdep ~deepseq-genericsPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: ansi-wl-pprint, exceptions, semigroups, unix

Dependency ranges changed: criterion, deepseq, deepseq-generics

API changes (from Hackage documentation)

- Unbound.Generics.LocallyNameless.Rebind: instance (GHC.Classes.Eq p1, GHC.Classes.Eq p2) => GHC.Classes.Eq (Unbound.Generics.LocallyNameless.Rebind.Rebind p1 p2)
+ Unbound.Generics.LocallyNameless.Alpha: instance Data.Semigroup.Semigroup Unbound.Generics.LocallyNameless.Alpha.NamePatFind
+ Unbound.Generics.LocallyNameless.Alpha: instance Data.Semigroup.Semigroup Unbound.Generics.LocallyNameless.Alpha.NthPatFind
+ Unbound.Generics.LocallyNameless.Alpha: instance GHC.Classes.Eq a => Data.Semigroup.Semigroup (Unbound.Generics.LocallyNameless.Alpha.DisjointSet a)
+ Unbound.Generics.LocallyNameless.Fresh: instance Control.Monad.Catch.MonadCatch m => Control.Monad.Catch.MonadCatch (Unbound.Generics.LocallyNameless.Fresh.FreshMT m)
+ Unbound.Generics.LocallyNameless.Fresh: instance Control.Monad.Catch.MonadMask m => Control.Monad.Catch.MonadMask (Unbound.Generics.LocallyNameless.Fresh.FreshMT m)
+ Unbound.Generics.LocallyNameless.Fresh: instance Control.Monad.Catch.MonadThrow m => Control.Monad.Catch.MonadThrow (Unbound.Generics.LocallyNameless.Fresh.FreshMT m)
+ Unbound.Generics.LocallyNameless.LFresh: instance Control.Monad.Catch.MonadCatch m => Control.Monad.Catch.MonadCatch (Unbound.Generics.LocallyNameless.LFresh.LFreshMT m)
+ Unbound.Generics.LocallyNameless.LFresh: instance Control.Monad.Catch.MonadMask m => Control.Monad.Catch.MonadMask (Unbound.Generics.LocallyNameless.LFresh.LFreshMT m)
+ Unbound.Generics.LocallyNameless.LFresh: instance Control.Monad.Catch.MonadThrow m => Control.Monad.Catch.MonadThrow (Unbound.Generics.LocallyNameless.LFresh.LFreshMT m)
+ Unbound.Generics.LocallyNameless.Rebind: instance (GHC.Classes.Eq p2, GHC.Classes.Eq p1) => GHC.Classes.Eq (Unbound.Generics.LocallyNameless.Rebind.Rebind p1 p2)
+ Unbound.Generics.PermM: instance GHC.Classes.Ord a => Data.Semigroup.Semigroup (Unbound.Generics.PermM.Perm a)
- Unbound.Generics.LocallyNameless.Alpha: class (Show a) => Alpha a where aeq' c = (gaeq c) `on` from fvAny' c nfn = fmap to . gfvAny c nfn . from close c b = to . gclose c b . from open c b = to . gopen c b . from isPat = gisPat . from isTerm = gisTerm . from isEmbed _ = False nthPatFind = gnthPatFind . from namePatFind = gnamePatFind . from swaps' ctx perm = to . gswaps ctx perm . from lfreshen' ctx m cont = glfreshen ctx (from m) (cont . to) freshen' ctx = retractFFM . liftM (first to) . gfreshen ctx . from acompare' c = (gacompare c) `on` from
+ Unbound.Generics.LocallyNameless.Alpha: class (Show a) => Alpha a
- Unbound.Generics.LocallyNameless.Embed: class IsEmbed e where type Embedded e :: * where {
+ Unbound.Generics.LocallyNameless.Embed: class IsEmbed e where {
- Unbound.Generics.LocallyNameless.Operations: class IsEmbed e where type Embedded e :: * where {
+ Unbound.Generics.LocallyNameless.Operations: class IsEmbed e where {
- Unbound.Generics.LocallyNameless.Subst: class Subst b a where isvar _ = Nothing isCoerceVar _ = Nothing subst n u x = if (isFreeName n) then case (isvar x :: Maybe (SubstName a b)) of { Just (SubstName m) -> if m == n then u else x Nothing -> case (isCoerceVar x :: Maybe (SubstCoerce a b)) of { Just (SubstCoerce m f) -> if m == n then maybe x id (f u) else x Nothing -> to $ gsubst n u (from x) } } else error $ "Cannot substitute for bound variable " ++ show n substs ss x | all (isFreeName . fst) ss = case (isvar x :: Maybe (SubstName a b)) of { Just (SubstName m) -> case find ((== m) . fst) ss of { Just (_, u) -> u Nothing -> x } Nothing -> case isCoerceVar x :: Maybe (SubstCoerce a b) of { Just (SubstCoerce m f) -> case find ((== m) . fst) ss of { Just (_, u) -> maybe x id (f u) Nothing -> x } Nothing -> to $ gsubsts ss (from x) } } | otherwise = error $ "Cannot substitute for bound variable in: " ++ show (map fst ss)
+ Unbound.Generics.LocallyNameless.Subst: class Subst b a

Files

Changelog.md view
@@ -1,3 +1,16 @@+# NEXT++# 0.3.2++* Bump `deepseq >= 1.4.0.0` remove benchmark dependency on `deepseq-generics`+* Tested with GHC 8.4.1+* Tested with GHC 8.2.2+* Compile with `-Wcompat`+* Add `Semigroup` instances for all types that were previously `Monoid` instances+* Added more examples to the [examples/ directory](https://github.com/lambdageek/unbound-generics/tree/master/examples)+* Added "exceptions" dependency and `MonadThrow`, `MonadCatch`, `MonadMask` instances for `FreshMT` and `LFreshMT`.+  Thanks Alex McKenna.+ # 0.3.1  * Tested with GHC 8.0.1
README.md view
@@ -14,8 +14,11 @@  This is a reimplementation of (parts of) [unbound](http://hackage.haskell.org/package/unbound) but using [GHC generics](http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.7.0.1/GHC-Generics.html) instead of [RepLib](https://hackage.haskell.org/package/RepLib). -## Example+## Examples +Some examples are in the `examples/` directory in the source.  And also at [unbound-generics on GitHub Pages](https://lambdageek.github.io/unbound-generics)++### Example: Untyped lambda calculus interpreter Here is how you would implement call by value evaluation for the untyped lambda calculus:  ```haskell
benchmarks/BenchLam.hs view
@@ -1,5 +1,5 @@ -- | Untyped lambda calc for benchmarking-{-# LANGUAGE DeriveGeneric, DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric, DeriveDataTypeable, CPP #-} module BenchLam where  import Control.Applicative@@ -10,7 +10,10 @@ import Data.Typeable (Typeable)  import Control.DeepSeq (NFData(..), deepseq)+#if MIN_VERSION_deepseq (1,4,0)+#else import Control.DeepSeq.Generics (genericRnf)+#endif import Criterion (Benchmark, env, bench, nf)  import Unbound.Generics.LocallyNameless@@ -24,7 +27,11 @@     deriving (Show, Generic, Typeable)  instance Alpha Term+#if MIN_VERSION_deepseq (1,4,0)+instance NFData Term+#else instance NFData Term where rnf = genericRnf+#endif   -- | lambda abstract over all the given vars
+ examples/CanonicalLF.lhs view
@@ -0,0 +1,535 @@+% Canonical LF using unbound-generics+% Aleksey Kliger+% June 2016++Canonical LF+============++This is a representation of [LF](http://www.twelf.org/) in which all terms are+automatically in *canonical form*.  The key idea is to segregate the type families+and the terms into *atomic* and *normal* forms where the term variables only stand+for atomic terms, and not arbitrary ones.  Then, a substitution procedure is defined+that takes terms in normal form and performs a substitution for a variable while+simultaneously normalizing any redices that occur.++> {-# LANGUAGE DeriveGeneric, StandaloneDeriving, DeriveDataTypeable,+>     ViewPatterns, RankNTypes, FlexibleContexts, FlexibleInstances,+>     FunctionalDependencies, TypeFamilies+>   #-}+> module CanonicalLF where+> import Unbound.Generics.LocallyNameless+> import GHC.Generics (Generic)+> import Data.Typeable (Typeable)+> import qualified Data.Map as M+> import Control.Monad.Reader+> import Control.Monad.Except+> import Data.Functor.Identity+> import Control.Applicative (Const(..))++Syntax+------++An LF signature introduces type family atoms and constant terms.++> data Signature = NilS+>   | SnocAtom (Rebind Signature (Atm, Embed Kind))+>   | SnocConst (Rebind Signature (Cnst, Embed Type))+>   deriving (Show, Generic, Typeable)++The type families are classified by kinds and may either be plain+types, or pi-kinds for families of types indexed by a term+variable.++> data Kind = TypeK | PiK (Bind (Var, Embed Type) Kind)+>   deriving (Show, Generic, Typeable)++The atomic type families are either type familiy atoms applied to zero+or more terms in normal form.++> type Atm = Name P+> data P = AtmP Atm | AppP P Term+>   deriving (Show, Generic, Typeable)++Type families in normal form are either atomic type families or dependent product+types indexed by a term variable of normal type.++> data Type = PT P | PiT (Bind (Var, Embed Type) Type)+>   deriving (Show, Generic, Typeable)++The atomic terms are either variables or constants applied to zero or+more terms in normal form.++> type Cnst = Name R+> type Var = Name R+> data R = VarR Var | ConstR Cnst | AppR R Term+>   deriving (Show, Generic, Typeable)++A term in normal form is either an atomic term or a lambda abstraction+that binds a term variable.++> data Term = RM R | LamM (Bind Var Term)+>   deriving (Show, Generic, Typeable)++When typechecking kinds, types or terms, new term variables may come+into scope. They are collected in contexts.++> data Context = NilC+>              | Snoc (Rebind Context (Var, Embed Type))+>              deriving (Show, Generic, Typeable)++All the syntactic objects are equivalent upto renaming of bound variables.++> instance Alpha Signature+> instance Alpha Kind+> instance Alpha P+> instance Alpha Type+> instance Alpha R+> instance Alpha Term+> instance Alpha Context++The metatheory of Canonical LF uses simple types to prove the+termination of hereditary substitution (defined below).  But they+aren't needed in the implementation.  (Although it would be+interesting to lift them to Haskell kinds and index the terms by the+simple types to disallow some malformed terms.)++> data SimpleType = AtmS Atm | ArrS SimpleType SimpleType+>   deriving (Show, Generic, Typeable)+>+> instance Alpha SimpleType++Hereditary Substitution+-----------------------++Variables in Canonical LF stand for atomic terms, but we will need to+subtitute terms for them.  If we used ordinary capture-avoiding+substitution, such substitution would produce redices, which we are+precisely what we don't want.  However redices will potentially only+occur when the variable for which we're substituting occurs at the+head of an atomic term.++> isHeadVarR :: Var -> R -> Bool+> isHeadVarR x (VarR y) = x == y+> isHeadVarR _ (ConstR _) = False+> isHeadVarR x (AppR r _) = isHeadVarR x r++Just using a boolean to decide if the variable is at the head of an+atomic term is fine, but we can actually partition an atomic term into+its head variable or constant together with a spine of applications.++> data Spine a = NilSp a | AppSp (Spine a) Term+> data Head = VarH Var | ConstH Cnst++If the variable for which we'll be substituting is at the head we only+really care about the spine.  Otherwise we have some other variable,+or perhaps a constant at the head.++> headSpine :: Var -> R -> Either (Spine Head) (Spine ())+> headSpine x (VarR y) | x == y    = Right (NilSp ())+>                      | otherwise = Left (NilSp (VarH y))+> headSpine _ (ConstR c)           = Left (NilSp (ConstH c))+> headSpine x (AppR r m)           = case headSpine x r of+>   Left s -> Left (AppSp s m)+>   Right s -> Right (AppSp s m)++Substitution in a kind just carries out substitution in the types.+Likewise substutition in normal type families.++> substKind :: Fresh m => Term -> Var -> Kind -> m Kind+> substKind _ _ TypeK = return TypeK+> substKind m x (PiK bnd) = do+>   ((y, unembed -> a), k) <- unbind bnd+>   a' <- substType m x a+>   k' <- substKind m x k+>   return $ PiK $ bind (y, embed a') k'+>+> substType :: Fresh m => Term -> Var -> Type -> m Type+> substType m x (PT p) = do+>   p' <- substP m x p+>   return (PT p')+> substType m x (PiT bnd) = do+>   ((y, unembed -> a), b) <- unbind bnd+>   a' <- substType m x a+>   b' <- substType m x b+>   return $ PiT $ bind (y, embed a') b'++Atomic type family application substitutes a term for a variable in the (normal) index terms.++> substP :: Fresh m => Term -> Var -> P -> m P+> substP _ _ (AtmP a) = return (AtmP a)+> substP m x (AppP p n) = do+>   p' <- substP m x p+>   n' <- substTerm m x n+>   return (AppP p' n')++Normal term substitution goes under a lambda (freshness is ensured by+the library) and into the atomic term.++> substTerm :: Fresh m => Term -> Var -> Term -> m Term+> substTerm m x (RM r) = substR m x r+> substTerm m x (LamM bnd) = do+>   (y, n) <- unbind bnd+>   n' <- substTerm m x n+>   return $ LamM $ bind y n'++To substitute in an atomic term we separate the head and the spine and+proceed according to whether the variable at the head is the one we+are substituting for.++> substR :: Fresh m => Term -> Var -> R -> m Term+> substR m x r =+>   case headSpine x r of+>     Left rsp -> RM <$> substRRsp m x rsp+>     Right msp -> substMsp m x msp++If there is another variable or a constant at the head, we will get+some kind of atomic term out since the head is unchanged and we only+substitute into the index terms.++> substRRsp :: Fresh m => Term -> Var -> Spine Head -> m R+> substRRsp _ _ (NilSp h) = return (headR h)+> substRRsp m x (AppSp sp n) = do+>   n' <- substTerm m x n+>   r <- substRRsp m x sp+>   return $ AppR r n'+>+> headR :: Head -> R+> headR (VarH y) = VarR y+> headR (ConstH c) = ConstR c++When the variable we care about is at the head, we apply the+substitution to the rest of the spine to get a normal term (which, by+the metatheory will turn out to be some kind of a lambda), apply the+substitution to the index, and then carry out a new substitution of+the new index for the variable bound by the lambda to the body of the+lambda.  This last step is the heredetary part of heredetary+substitution.  The metatheory guarantees that this process will+terminate.  (Because the simple type of the body of the lambda is+smaller than the simple type of the original term).++> substMsp :: Fresh m => Term -> Var -> Spine () -> m Term+> substMsp m _ (NilSp ()) = return m+> substMsp m x (AppSp s n) = do+>   o_ <- substMsp m x s+>   n' <- substTerm m x n+>   case o_ of+>     LamM bnd -> do+>       (y, o) <- unbind bnd+>       substTerm n' y o+>     _ -> error "can't happen"++Typechecking+============++We typecheck in an environment that maps type family atoms, term+costants and variables to their respective kinds and types.++> data Env = Env { _envAtm :: M.Map Atm Kind,+>                  _envConst :: M.Map Cnst Type,+>                  _envCtx :: M.Map Var Type }++> emptyEnv :: Env+> emptyEnv = Env M.empty M.empty M.empty++Some lenses to work with the environment++> envAtm :: Lens' Env (M.Map Atm Kind)+> envAtm afb s = fmap (\atms -> s { _envAtm = atms }) $ afb (_envAtm s)+>+> envCtx :: Lens' Env (M.Map Var Type)+> envCtx afb s = fmap (\ctx -> s { _envCtx = ctx } ) $ afb (_envCtx s)+>+> envConst :: Lens' Env (M.Map Cnst Type)+> envConst afb s = fmap (\sig -> s { _envConst = sig} ) $ afb (_envConst s)++And some combinators to perform lookups.++> lookupOver :: (MonadReader e m, MonadError String m) => Getting p e p -> String -> (p -> Maybe c) -> m c+> lookupOver l s f = do+>   mk <- views l f+>   case mk of+>     Nothing -> throwError $ "unbound " ++ s+>     Just c -> return c+>+> lookupAtom :: (MonadReader Env m, MonadError String m) => Atm -> m Kind+> lookupAtom = lookupOver envAtm "atom" . M.lookup +>+> lookupVar :: (MonadReader Env m, MonadError String m) => Var -> m Type+> lookupVar = lookupOver envCtx "variable" . M.lookup+>+> lookupConst :: (MonadReader Env m, MonadError String m) => Cnst -> m Type+> lookupConst = lookupOver envConst "constant" . M.lookup++To check a signature we check the kind or type classifying the atom or+constant and then continue in an environment extended with the new+binding.++> withSigOk :: (Fresh m, MonadReader Env m, MonadError String m) => Signature -> m r -> m r+> withSigOk NilS kont = kont+> withSigOk (SnocAtom (unrebind -> (s, (a, unembed -> k)))) kont =+>   withSigOk s $ do+>   local (set envCtx M.empty) $ wfk k+>   local (over envAtm (M.insert a k)) kont+> withSigOk (SnocConst (unrebind -> (s, (c, unembed -> a)))) kont =+>   withSigOk s $ do+>   local (set envCtx M.empty) $ wfType a+>   local (over envConst (M.insert c a)) kont++Kind and normal type formation is unsurprising.  Note that PT is only+well-formed when the atomic type family is fully applied and is of+base kind.++> wfk :: (Fresh m, MonadReader Env m, MonadError String m) => Kind -> m ()+> wfk TypeK = return ()+> wfk (PiK bnd) = do+>   ((x, unembed -> t), k) <- unbind bnd+>   wfType t+>   local (over envCtx (M.insert x t)) $ wfk k+>+> wfType :: (Fresh m, MonadReader Env m, MonadError String m) => Type -> m ()+> wfType (PT p) = do+>   k <- inferP p+>   case k of+>     TypeK -> return ()+>     _ -> throwError "expected a type"+> wfType (PiT bnd) = do+>   ((x, unembed -> a), b) <- unbind bnd+>   wfType a+>   local (over envCtx (M.insert x a)) $ wfType b++For atomic type families we infer their kinds.  For atoms we read the+kind off from the environment.  For applications we infer the kind of+the type family (which had better be a pi kind), and then check that+the term argument has the expected type and then return the resulting+kind where we (heredeterily) substitute the term for the index+variable.++> inferP :: (Fresh m, MonadReader Env m, MonadError String m) => P -> m Kind+> inferP (AtmP atm) = lookupAtom atm+> inferP (AppP p m) = do+>   k <- inferP p+>   case k of+>     TypeK -> throwError "expected a pi kind"+>     PiK bnd -> do+>       ((x, unembed -> a), k') <- unbind bnd+>       checkTerm m a+>       substKind m x k'++To check that a term in normal form has the expected normal type, we+check that its either a lambda of pi type, or an atomic term of atomic+type.  The latter ensures that terms are in eta long form by requiring+all variables and constants to be fully applied.++We infer the type of an atomic term (which had better be atomic) and+then check that it is alpha-equivalent to the given atomic type.+Because the calculus is constructed to only allow terms in normal+form, alpha equivalence suffices and we don't have to do any+normalization.  (We paid that price in heredetary substitution.)++> checkTerm :: (Fresh m, MonadReader Env m, MonadError String m) => Term -> Type -> m ()+> checkTerm (LamM bnd) (PiT bnd') = do+>   mmatch <- unbind2 bnd bnd'+>   case mmatch of+>     Just (x, m, (_, unembed -> a), b) -> do+>       wfType a+>       local (over envCtx (M.insert x a)) $ checkTerm m b+>     Nothing -> throwError "did not match"+> checkTerm (RM r) (PT p) = do+>   t <- inferTerm r+>   case t of+>     (PT p') | p `aeq` p' -> return ()+>     _ -> throwError "atomic term doesn't have the expected atomic type."+> checkTerm (LamM {}) _ = throwError "lambda with no-PI type"+> checkTerm (RM {}) _ = throwError "atomic term with non-atomic type"++To infer the type of a term, we lookup variables and constants in the+environment.  For applications we ensure that the head has some kind+of pi type and then check the index against the argument type, and+then return the result type with the argument substituted for the+index variable.++> inferTerm :: (Fresh m, MonadReader Env m, MonadError String m) => R -> m Type+> inferTerm (VarR x) = lookupVar x+> inferTerm (ConstR c) = lookupConst c+> inferTerm (AppR r m) = do+>   p_ <- inferTerm r+>   case p_ of+>     PiT bnd -> do+>       ((x, unembed -> a), b) <- unbind bnd+>       checkTerm m a+>       substType m x b+>     PT {} -> throwError "expected a function in application position"++Smart Constructors+==================++This section defines a little DSL for writing Canonical LF terms in+Haskell.  It's a higher-order encoding that uses haskell variable+binding to represent LF binding constructs.++We use a higher-kinded repr parameter to allow for different sorts of+interpretations for this DSL.  Although in this example we only build+one using the Syn newtype to wrap a fresh-name monad computation that+just builds a term in our original AST, above.++> class TermSyntax repr r n | r -> n, n -> r where+>   lam :: String -> ((repr r) -> (repr n)) -> (repr n)+>   app :: repr r -> repr n -> repr r+>   rm :: repr r -> repr n+>+> newtype Syn m a = Syn { unSyn :: m a } +>+> instance Fresh m => TermSyntax (Syn m) R Term where+>   lam hint f = Syn $ do+>     x <- fresh (s2n hint)+>     m <- unSyn $ f (Syn $ return $ VarR x)+>     return $ LamM (bind x m)+>   app r n = Syn (AppR <$> unSyn r <*> unSyn n)+>   rm r = Syn (RM <$> unSyn r)++> class  TypeSyntax repr p a | a -> p, p -> a where+>   type TermInType repr a :: *+>   type RInType repr a :: *+>   piT :: String -> repr a -> (repr (RInType repr a) -> repr a) -> repr a+>   arrT :: repr a -> repr a -> repr a+>   arrT a b = piT "_" a (const b)+>   appP :: repr p -> repr (TermInType repr a) -> repr p+>   pt :: repr p -> repr a++> instance Fresh m => TypeSyntax (Syn m) P Type where+>   type TermInType (Syn m) Type = Term+>   type RInType (Syn m) Type = R+>   piT hint sa f = Syn $ do+>     x <- fresh (s2n hint)+>     b <- unSyn $ f (Syn $ return $ VarR x)+>     a <- unSyn sa+>     return $ PiT $ bind (x, embed a) b+>   appP p m = Syn (AppP <$> unSyn p <*> unSyn m)+>   pt p = Syn (PT <$> unSyn p)++> class KindSyntax repr k where+>   type TypeInKind repr k :: *+>   type RInKind repr k :: *+>   typeK :: repr k+>   piK :: String -> repr (TypeInKind repr k) -> (repr (RInKind repr k) -> repr k) -> repr k+>   arrK :: repr (TypeInKind repr k) -> repr k -> repr k+>   arrK a k = piK "_" a (const k)++> instance Fresh m => KindSyntax (Syn m) Kind where+>   type TypeInKind (Syn m) Kind = Type+>   type RInKind (Syn m) Kind = R+>   typeK = Syn $ return TypeK+>   piK hint sa sk = Syn $ do+>     x <- fresh (s2n hint)+>     a <- unSyn sa+>     k <- unSyn $ sk (Syn $ return $ VarR x)+>     return $ PiK $ bind (x, embed a) k++> class SignatureSyntax repr sig p r | sig -> p r where+>   type KindInSig repr sig :: *+>   type TypeInSig repr sig :: *+>   letAtom :: String -> repr (KindInSig repr sig) -> (repr p -> repr sig) -> repr sig+>   letConstant :: String -> repr (TypeInSig repr sig) -> (repr r -> repr sig) -> repr sig+>   endSig :: repr sig+>+> instance Fresh m => SignatureSyntax (Syn m) (Signature -> Signature) P R where+>   type KindInSig (Syn m) (Signature -> Signature) = Kind+>   type TypeInSig (Syn m) (Signature -> Signature) = Type+>+>   letAtom hint sk kont = Syn $ do+>     a <- fresh (s2n hint)+>     k <- unSyn sk+>     f <- unSyn $ kont $ Syn $ return $ AtmP a+>     return $ \sig -> f (SnocAtom $ rebind sig (a, embed k))+>+>   letConstant hinst st kont = Syn $ do+>     c <- fresh (s2n hinst)+>     t <- unSyn st+>     f <- unSyn $ kont $ Syn $ return $ ConstR c+>     return $ \sig -> f (SnocConst $ rebind sig (c, embed t))+>+>   endSig = Syn $ return id++> infixr 6 `arrT`, `arrK`+> infixl 6 `appP`, `app`++Example+-------++An LF signature fragment for first-order logic.++> example1 :: Fresh m => Syn m (Signature -> Signature)+> example1 =+>   letAtom "o" typeK $ \o ->+>   letConstant "tt" (pt o) $ \tt ->+>   letConstant "ff" (pt o) $ \ff ->+>   letConstant "not" (pt o `arrT` pt o) $ \not ->+>   letConstant "and" (pt o `arrT` pt o `arrT` pt o) $ \and ->+>   letAtom "nd" (pt o `arrK` typeK) $ \nd ->+>   letConstant "tti" (pt (nd `appP` rm tt)) $ \tti ->+>   letConstant "ffe" (piT "a" (pt o) $ \a ->+>                        pt (nd `appP` rm ff)+>                        `arrT`+>                        pt (nd `appP` rm a)) $ \ffe ->+>   letConstant "noti" (piT "a" (pt o) $ \a ->+>                          (piT "p" (pt o) $ \p ->+>                              pt (nd `appP` rm a)+>                              `arrT`+>                              pt (nd `appP` rm p))+>                          `arrT`+>                          pt (nd `appP` rm (not `app` rm a))) $ \noti ->+>   letConstant "note" (piT "a" (pt o) $ \a -> piT "c" (pt o) $ \c ->+>                          pt (nd `appP` rm (not `app` rm a))+>                          `arrT`+>                          pt (nd `appP` rm a)+>                          `arrT`+>                          pt (nd `appP` rm c)) $ \note ->+>   letConstant "andi" (piT "a" (pt o) $ \a -> piT "b" (pt o) $ \b ->+>                          pt (nd `appP` rm a)+>                          `arrT`+>                          pt (nd `appP` rm b)+>                          `arrT`+>                          pt (nd `appP` rm (and `app` rm a `app` rm b))) $ \andi ->+>   letConstant "ande1" (piT "a" (pt o) $ \a -> piT "b" (pt o) $ \b ->+>                           pt (nd `appP` rm (and `app` rm a `app` rm b))+>                           `arrT`+>                           pt (nd `appP` rm a)) $ \ande1 ->+>   letConstant "ande2" (piT "a" (pt o) $ \a -> piT "b" (pt o) $ \b ->+>                           pt (nd `appP` rm (and `app` rm a `app` rm b))+>                           `arrT`+>                           pt (nd `appP` rm b)) $ \ande2 ->+>   endSig++> checkSynSig :: (Fresh m, MonadReader Env m, MonadError String m) => Syn m (Signature -> Signature) -> m ()+> checkSynSig sig = do+>   sigf <- unSyn sig+>   withSigOk (sigf NilS) $ return ()++Example:+```haskell+  >>> runExceptT $ runFreshMT $ runReaderT (checkSynSig example1) emptyEnv+  Right ()+```++Appendix: Lens utilities+==============++Some machinery to work with records.++> type Lens s t a b = forall f . Functor f => (a -> f b) -> s -> f t+> type Lens' s a = Lens s s a a++> type Setting s t a b = (a -> Identity b) -> s -> Identity t+> type Setting' s a = Setting s s a a++> over :: Setting s t a b -> (a -> b) -> s -> t+> over l f = runIdentity . l (Identity . f)++> set :: Setting s t a b -> b -> s -> t+> set l = over l . const++> type Getting r s a = (a -> Const r a) -> s -> Const r s++> views :: MonadReader s m => Getting a s a -> (a -> r) -> m r+> views l f = asks (\s -> f (getConst (l Const s)))+
+ examples/Nanevski.lhs view
@@ -0,0 +1,1149 @@+% "Meta-programming with names and necessity" using unbound-generics+% Aleksey Kliger+% August 2016+++Introduction+============++This example is based on Nanevski's “Meta-programming with Names and+Necessity”[1](http://reports-archive.adm.cs.cmu.edu/anon/2002/CMU-CS-02-123R.pdf).+In particular, it demonstrates how to represent a language with+multiple sorts of names (value variables, code variables, nominals and+support variables) some with non-standard notions of substitution.++As a tutorial example, we show how to represent the language and+demonstrate its operational semantics.  Additionally, this document+includes a typecheker and pretty printer.++This file is Literate Haskell, so we start with some boilerplate.++Every `unbound-generics` syntax representation needs `deriving (Show, Generic, Typeable)` declarations for all the datatypes.++> {-# language DeriveDataTypeable, DeriveGeneric #-}++> {-# language MultiParamTypeClasses,+>     FlexibleContexts, FlexibleInstances, DefaultSignatures, ViewPatterns, RankNTypes,+>     GeneralizedNewtypeDeriving #-}+> module Nanevski where+> import GHC.Generics (Generic)+> import Data.Typeable (Typeable)+> import Unbound.Generics.LocallyNameless+> import Unbound.Generics.LocallyNameless.Internal.Fold (Fold, foldMapOf)+> import qualified Unbound.Generics.PermM as PermM+>+> import Control.Monad.Except+> import Control.Monad.State+> import Control.Monad.Writer+> import Control.Monad.Reader+> import Data.List (partition, nub, sort, (\\), isSubsequenceOf)+> import Data.Monoid (Any (..))+> import Data.Either (partitionEithers)+> import qualified System.IO as IO+> import qualified Text.PrettyPrint.ANSI.Leijen as PP+> import Text.PrettyPrint.ANSI.Leijen ()++Introduction to meta-programming+--------------------------------++Meta-programming is the process of writing programs that create,+manipulate or reason about other programs.  The meta-program is+written in the *meta-language* and it manipulates *object-language*+programs.++In the system in this document, the object language and the+meta-language coincide.  In general they could be different (for+example the meta-language could be compiled down to machine code,+while the object-language remains as some sort of abstract syntax+trees).++The type discipline of the system in this document applies both to the+meta- and object-languages: a well-typed meta-program can only+construct well-typed object-language programs.++One important concept is the *support* of an object-language+expression: it is the set of meta-language names that appear within+the object-language program fragment.  The meta-language program may+compose larger object-language fragments by substituting+object-language expressions for the names.++Example+-------++Suppose we wanted to construct the object-language cube function `“λx:int. 1 * x * x * x”`++The following program constructs such a function:++> -- >>> ppretty (pexp @@ number 3)+> -- (λn:int.+> --  choose+> --  (νX∼int.+> --   let □w = (Λp.+> --             λe:□_{p} int.+> --             rec+> --               go (m:int) : □_{p} int+> --               is if (□_{p} int;+> --                      m ≤ 0;+> --                      λ_:(). “1”;+> --                      λ_:(). let □u = go (m + -1) in let □w = e in “⟨⟩u * ⟨⟩w”)+> --                  ()) {X}+> --            “X”+> --            n in “λx:int. ⟨X ↦ x⟩w”))+> -- 3+++Syntax+======++Types+-----+++> data BaseType = UnitT | BoolT | IntT+>   deriving (Typeable, Generic, Show)++> data Support = Support { supportNominals :: ![Nominal],+>                          supportVars :: ![SupportVar] }+>   deriving (Typeable, Generic, Show)+> type SupportVar = Name Support++> data Type =+>   BaseT BaseType          -- some base types+>   | ArrT Type Type        -- τ₁→τ₂ functions+>   | BoxT Type Support   -- □_C τ is the type of code of type τ with support C+>   | NomArrT Type Type      -- τ₁ ↛ τ₂ is the type of νN:τ₁.e brings a+>                           -- new name N associated with type τ₁ into+>                           -- scope in a body of type τ₂.  The new+>                           -- name cannot appear in the type τ₂ or in+>                           -- the support of e.  It is thus+>                           -- guaranteed to either be substituted+>                           -- away or only appear in code in e, not+>                           -- in subexpresisons that may be evaluated+>                           -- in teh course of evaluating e.+>   | ForallSupT (Bind SupportVar Type) -- support-polymorphic functions+>   deriving (Typeable, Generic, Show)++Meta-language expressions+-------------------------++> data Expr =+>   V Var+>   | U NominalSubst CodeVar+>   | N Nominal+>   | C BaseConst+>   | P PrimOp [Value] [Expr] -- partially evaluated primop p v₁...vₙ e₁...eₘ+>   | Lambda (Bind (Var, Embed Type) Expr)+>   | RecFun (Bind (Var, Var, Embed Type, Embed Type) Expr) -- fun f (x : τ₁) : τ₂ = e+>   | App Expr Expr+>   | Let Expr (Bind Var Expr)+>   | Box Code  -- A box expression represents some syntactic code as a data structure.+>   | LetBox Expr (Bind CodeVar Expr)+>   | New (Bind (Nominal, Embed Type) Expr)   -- the New operation brings names into scope; it is the job of the type system to ensure that the new name does not appear in the type of the body, nor its support.+>   | Choose Expr+>   | PLamSupport (Bind SupportVar Expr)+>   | PAppSupport Expr Support+>   deriving (Typeable, Generic, Show)+>+> data BaseConst = UnitC | BoolC !Bool | IntC !Int+>   deriving (Typeable, Generic, Show)+> data PrimOp = IfPrim Type | AddPrim | MulPrim | LeqPrim+>   deriving (Typeable, Generic, Show)++>+> type Var = Name Expr++Ordinary variables just stand for expressions.++> type Value = Expr+++> isValue :: Expr -> Bool+> isValue (C _) = True+> isValue (Lambda _) = True+> isValue (Box _) = True+> isValue (New _) = True+> isValue (PLamSupport _) = True+> isValue _ = False++Object-level Code+-----------------++Think of Code as some data structure that the expressions can build+up.  Code is a first-class value in this language.  You can build it+up, pass it to functions, return it as a result etc.  Code that is+open has non-empty support is guaranteed by the type system not to be+evaluated.  Code that is closed and has empty support , on the other+hand may appear in expressions in positions where it may potentially+be run.++> newtype Code = Code { codeExpr :: Expr }+>   deriving (Typeable, Generic, Show)+> type CodeVar = Name Code++Code variables stand for code, but they only occur in expressions+together with an explicit subtitution that substitutes away some Nominals.++> newtype NominalSubst = NominalSubst { nominalSubst :: [(Nominal, Nom)] }+>   deriving (Typeable, Generic, Show)++Nominals+--------++A Nominal can appear in code (but not in an expression that may be+evaluated).  It stands for an expression just like Var, but in+unbound-generics since we want to treat Nominal and Var distinctly, we+add a newtype wrapper around Expr and call it a Nom.++> newtype Nom = Nom {nomExpr :: Expr }+>   deriving (Typeable, Generic, Show)+> type Nominal = Name Nom++Alpha renaming, free names, alpha-equivalence boilerplate+---------------------------------------------------------++All the types we defined will participate in various Alpha operations:+we can collect the free Variabes, Nominals or CodeVars of all the+syntactic categories.  They are also subject to alpha equivalence upto+renaming of bound occurrances, etc.++> instance Alpha Nom+> instance Alpha Code+> instance Alpha Support+> instance Alpha NominalSubst+> instance Alpha Type+> instance Alpha Expr+> instance Alpha BaseType+> instance Alpha BaseConst+> instance Alpha PrimOp++Substitution+============++We also have notions of substitution for variables, nominals and code variables.++For base types, constants and primitive operations, we give some+catch-all substitution operations since they cannot contain any sort+of name.++> instance Subst a BaseType where+>   subst _ _ = id+>   substs _ = id+>+> instance Subst a BaseConst where+>   subst _ _ = id+>   substs _ = id+>+> instance Subst Expr PrimOp where+>   subst _ _ = id+>   substs _ = id+> instance Subst Code PrimOp where+>   subst _ _ = id+>   substs _ = id+> instance Subst Nom PrimOp+> instance Subst Support PrimOp++Expression substitution+-----------------------++Ordinary variables can occur in expressions as well as code, noms and+nominal substitutions.  They don't occur in types, so we can+short-circuit substitution there and return the type unchanged.++> instance Subst Expr Type where+>   subst _ _ = id+>   substs _ = id+> instance Subst Expr Expr where+>   isvar (V v) = Just (SubstName v)+>   isvar _ = Nothing+> instance Subst Expr NominalSubst+> instance Subst Expr Nom+> instance Subst Expr Support where+>   subst _ _ = id+>   substs _ = id++Nominal substitution+--------------------++> instance Subst Nom Nom+> instance Subst Nom Support++To substitute for a nominal N in an expression e, we use need to use+\texttt{isCoerceVar} to pull out the expression from the Nom being+substituted.++> instance Subst Nom Expr where+>   isCoerceVar (N n) = Just $ SubstCoerce n (Just . nomExpr)+>   isCoerceVar _ = Nothing++An important property (justified by the type system) of this language+is that when substituting for a name N or ordinary variable v in an+expression Box (Code e) we can just return Box (Code e) unchanged+since the type system prevents Code from depending on the ordinary+variable context or by using names that do not contribute to the+support of a term.++> instance Subst Nom Code where+>   subst _ _ = id+>   substs _ = id+> instance Subst Expr Code where+>   subst _ _ = id+>   substs _ = id++> instance Subst Nom Type+> instance Subst Nom NominalSubst++Code substitution+-----------------++> instance Subst Code Code++We can substitute for code variables u in expressions, but since+u's appear only together with an explicit substitution for its+Nominals, we use \texttt{isCoerceVar} to have unbound-generics perform+the nominal substitution.++> instance Subst Code Expr where+>   isCoerceVar (U noms u) = Just $ SubstCoerce u (Just . substituteSupported noms)+>   isCoerceVar _ = Nothing+>++Note that in this case we peek inside a \texttt{(Code e)} using+codeExpr. (Just using \texttt{substs (nominalSubst noms) c} would give+us back the same unchanged syntactic object!)++> substituteSupported :: NominalSubst -> Code -> Expr+> substituteSupported noms c = substs (nominalSubst noms) (codeExpr c)++> instance Subst Code NominalSubst+> instance Subst Code Nom++> instance Subst Code Type where+>   subst _ _ = id+>   substs _ = id++> instance Subst Code Support where+>   subst _ _ = id+>   substs _ = id++Support polymorphism substitution+---------------------------------++Support variables stand for support sets.  We have to do a bit of+juggling to normalize the result of the substitution.++> instance Subst Support Support where+>   subst v sup sup0@(Support noms vs) =+>     case Data.List.partition (== v) vs of+>       ((_:_), vs') -> let+>         noms' = sort (supportNominals sup ++ noms)+>         vs'' = sort (supportVars sup ++ vs')+>         in Support noms' vs''+>       _ -> sup0+>   substs ss (Support noms vs) =+>     let f v = case lookup v ss of+>           Just sup -> Left sup+>           Nothing -> Right v+>         (sups, vs') = partitionEithers (map f vs)+>         noms' = sort (concatMap supportNominals sups ++ noms)+>         vs'' = sort (concatMap supportVars sups ++ vs')+>         in Support noms' vs''++> instance Subst Support Type++> instance Subst Support Expr+> instance Subst Support Nom+> instance Subst Support NominalSubst++As with Nominals, since SupportVars stand for the support of an+expression, and boxed code is meant to have empty support until it is+unboxed and evaluated, the support variable substitution on code is+the identity.++> instance Subst Support Code where+>   subst _ _ = id+>   substs _ = id++Operational Semantics+=====================++The operational semantics take configurations consisting of a context+of Nominals together with their associated types and an expression+with empty support to another such configuration.++> data NomCtx = NilNC | SnocNC (Rebind NomCtx (Nominal, Embed Type))+>             | SnocSupNC (NomCtx, SupportVar)+>   deriving (Typeable, Generic, Show)+> instance Alpha NomCtx++A configuration just pairs together a nominal ctx and an expression in some manner.+We will use a state monad. But we could also bind the names of the context in the expression.++> type ClosedConfig = Bind NomCtx Expr++We will need to work in a monad that also gives us fresh names and a way to signal errors++> step :: (MonadError String m, MonadState NomCtx m, Fresh m) => Expr -> m Expr+> step e0 = case e0 of+>   V v -> evalError ("unbound variable: "  ++ show v)+>   U _ u -> evalError ("unbound code variable: " ++ show u)+>   N n -> evalError ("unbound name: " ++ show n)+>   C c -> evalError ("already a value: " ++ show c)+>   P p vs [] -> applyPrim p vs+>   P p vs (e:es) | isValue e -> pure (P p (vs ++ [e]) es) +>                 | otherwise -> P p vs <$> ((:) <$> step e <*> pure es)+>   Lambda _ -> evalError ("already a value: " ++ show e0)+>   efun@(RecFun bnd) -> do+>     ((f, x, t1, _), e) <- unbind bnd+>     return $ Lambda $ bind (x, t1) (subst f efun e)+>   Let e1 bnd | isValue e1 -> do+>                  (x, e2) <- unbind bnd+>                  return $ subst x e1 e2+>   Let e1 bnd -> Let <$> step e1 <*> pure bnd+>   Box _ -> evalError ("already a value: " ++ show e0)+>   App e1@(Lambda bnd) e2 | isValue e2 -> do+>                              ((x, _), ebody) <- unbind bnd+>                              return $ subst x e2 ebody+>                          | otherwise -> do+>                              App e1 <$> step e2+>   App e1 e2 -> App <$> step e1 <*> pure e2+>   LetBox (Box c) bnd -> do+>     (u, ebody) <- unbind bnd+>     return $ subst u c ebody+>   LetBox e1 bnd -> LetBox <$> step e1 <*> pure bnd+>   New _ -> evalError ("already a value: " ++ show e0)+>   Choose (New bnd) -> do+>     -- In the paper there's a side-condition here that the chosen+>     -- name has to be fresh (with respect to the nominal ctx.+>     -- Fortunately unbound-generics will always give us a fresh+>     -- name.+>     (nt, ebody) <- unbind bnd+>     modify (\ctx -> SnocNC $ rebind ctx nt)+>     return ebody+>   Choose e -> Choose <$> step e+>   PLamSupport _ -> evalError ("already a value: " ++ show e0)+>   PAppSupport (PLamSupport bnd) sup -> do+>     (sv, ebody) <- unbind bnd+>     return $ subst sv sup ebody+>   PAppSupport e1 sup -> PAppSupport <$> step e1 <*> pure sup++> evalError :: MonadError String m => String -> m a+> evalError = throwError++> applyPrim :: MonadError String m => PrimOp -> [Value] -> m Value+> applyPrim (IfPrim _t) [C (BoolC b), v1, v2] = return $ if b then v1 else v2+> applyPrim AddPrim [C (IntC x), C (IntC y)] = return $ C $ IntC $ x + y+> applyPrim MulPrim [C (IntC x), C (IntC y)] = return $ C $ IntC $ x * y+> applyPrim LeqPrim [C (IntC x), C (IntC y)] = return $ C $ BoolC $ x <= y+> applyPrim p vs = evalError (show p ++ show vs ++ " does not step")++> eval :: (Fresh m, MonadState NomCtx m, MonadError String m) => Expr -> m Expr+> eval e = if isValue e then return e else step e >>= eval+  +Evaluation Example+==================++A little DSL for term construction+----------------------------------++> lam :: String -> Type -> (Expr -> Expr) -> Expr+> lam s t f =+>   let x = s2n s+>   in Lambda $ bind (x, embed t) (f $ V x)++> recFun :: String -> String -> Type -> Type -> (Expr -> Expr -> Expr) -> Expr+> recFun sfn sx t1 t2 f =+>   let fn = s2n sfn+>       x = s2n sx+>   in RecFun $ bind (fn, x, embed t1, embed t2) (f (V fn) (V x))++> plam :: String -> (SupportVar -> Expr) -> Expr+> plam s f =+>   let sv = s2n s+>   in PLamSupport (bind sv (f sv))++> intT, unitT, boolT :: Type+> intT = BaseT IntT+> unitT = BaseT UnitT+> boolT = BaseT BoolT+> boxT :: Type -> [Nominal] -> [SupportVar] -> Type+> boxT t noms svs = BoxT t (Support noms svs)+> boxT_ :: Type -> [Nominal] -> Type+> boxT_ t noms = boxT t noms []+> arrT :: Type -> Type -> Type+> arrT = ArrT+> infixr 5 `arrT`+> forallSupT :: String -> (SupportVar -> Type) -> Type+> forallSupT s f =+>   let sv = s2n s+>   in ForallSupT (bind sv (f sv))++> (@@) :: Expr -> Expr -> Expr+> (@@) = App+> infixl 5 @@++> papp :: Expr -> [Nominal] -> [SupportVar] -> Expr+> papp e noms svs = PAppSupport e (Support noms svs)++> chooseNew :: String -> Type -> (Nominal -> Expr) -> Expr+> chooseNew s t f =+>   let n = s2n s+>   in Choose $ New $ bind (n, embed t) (f n)++> letExp :: String -> Expr -> (Expr -> Expr) -> Expr+> letExp s e1 f =+>   let x = s2n s+>   in Let e1 (bind x (f $ V x))++> letBox :: String -> Expr -> (CodeVar -> Expr) -> Expr+> letBox s e1 f =+>   let u = s2n s+>   in LetBox e1 (bind u (f u))++> box :: Expr -> Expr+> box = Box . Code++> code :: [(Nominal, Expr)] -> CodeVar -> Expr+> code nome = U (NominalSubst $ map (fmap Nom) nome)++> runCode :: CodeVar -> Expr+> runCode = U (NominalSubst []) ++> name :: Nominal -> Expr+> name = N++> number :: Int -> Expr+> number = C . IntC ++> ifLeqZ :: Type -> Expr -> Expr -> Expr -> Expr+> ifLeqZ tres ex etrue efalse =+>   App (P (IfPrim tres) [] [etest, thunk etrue, thunk efalse]) (C UnitC)+>   where+>     etest = P LeqPrim [] [ex, number 0]+>     thunk e = lam "_" unitT (\_ -> e)++> sub1 :: Expr -> Expr+> sub1 e = P AddPrim [] [e, number (-1)]++> add, mul :: Expr -> Expr -> Expr+> add e1 e2 = P AddPrim [] [e1, e2]+> mul e1 e2 = P MulPrim [] [e1, e2]++> infixl 6 `add`+> infixl 7 `mul`++> (~~) :: a -> b -> (a, b)+> (~~) = (,)+> infix 5 ~~++Example: Staged exponential function+------------------------------------++(Note that within the calculus itself there's not a way to abstract over a nominal like this - the example below chooses a new X which appears in the definition of this helper function.)++First a little recursive helper function that expands out to the m-fold multiplication code \texttt{X * X * X * ... * 1}++> exp' :: Nominal -> Expr+> exp' nX = recFun "exp'" "m" intT (boxT_ intT [nX]) $ \exp' m ->+>   ifLeqZ (boxT_ intT [nX]) m (box $ number 1) (letBox "u" (exp' @@ (sub1 m)) $ \u ->+>                                      box $ mul (name nX) (runCode u))+>   ++And the example exponential function takes an integer n and then constructs a piece of code consiting of a lambda abstraction whose argument x is multiplied with itself n times.++> expon :: Expr+> expon = lam "n" intT $ \n ->+>   chooseNew "X" intT $ \nX ->+>   letExp "exp'" (exp' nX) $ \exp' ->+>   letBox "v"  (exp' @@ n) $ \v ->+>   box (lam "x" intT $ \x -> code [nX ~~ x] v)++Let's set up an environment for running evaluations.++> run :: StateT NomCtx (ExceptT String FreshM) a -> Either String (a, NomCtx)+> run comp = runFreshM (runExceptT (runStateT comp NilNC))++Running the example \texttt{expon 3} we get the expected final+configuration of \texttt{box (λx : int . x * x * x * 1)} and the used+name X1 (which doesn't appear in the code, and therefore we could run+the code).++> -- >>> run (eval (expon @@ number 3))+> -- Right (Box (Code {codeExpr = Lambda (<(x,{BaseT IntT})> P MulPrim [] [V 0@0,P MulPrim [] [V 0@0,P MulPrim [] [V 0@0,C (IntC 1)]]])}),SnocNC (<<NilNC>> (X1,{BaseT IntT})))++> -- >>> run (eval (letBox "exp3" (expon @@ number 3) $ \exp3 -> (runCode exp3) @@ number 2))+> -- Right (C (IntC 8),SnocNC (<<NilNC>> (X1,{BaseT IntT})))+++Example: Staged support-polymorphic exponential kernel+------------------------------------------------------++> pexpKernel :: Expr+> pexpKernel = plam "p" $ \sp ->+>   let tResult = boxT intT [] [sp]+>   in lam "e" tResult $ \e ->+>     recFun "go" "m" intT tResult $ \go m ->+>     ifLeqZ tResult m (box $ number 1) (letBox "u" (go @@ (sub1 m)) $ \u ->+>                                           letBox "w" e $ \w ->+>                                           box $ mul (runCode u) (runCode w))++> -- >>> run $ eval (papp pexpKernel [] [] @@ box (number 42) @@ number 3)+> -- Right (Box (Code {codeExpr = P MulPrim [] [P MulPrim [] [P MulPrim [] [C (IntC 1),C (IntC 42)],C (IntC 42)],C (IntC 42)]}),NilNC)++> pexp :: Expr+> pexp = lam "n" intT $ \n ->+>   chooseNew "X" intT $ \nX ->+>   letBox "w" (papp pexpKernel [nX] [] @@ box (name nX) @@ n) $ \w ->+>   box (lam "x" intT $ \x -> code [nX ~~ x] w)++> -- >>> run $ eval (pexp @@ number 5)+> -- Right (Box (Code {codeExpr = Lambda (<(x,{BaseT IntT})> P MulPrim [] [P MulPrim [] [P MulPrim [] [P MulPrim [] [P MulPrim [] [C (IntC 1),V 0@0],V 0@0],V 0@0],V 0@0],V 0@0])}),SnocNC (<<NilNC>> (X1,{BaseT IntT})))++> -- >>> run $ eval (letBox "c" (pexp @@ number 5) $ \c -> runCode c @@ number 2)+> -- Right (C (IntC 32),SnocNC (<<NilNC>> (X1,{BaseT IntT})))+++Appendix: Type checking and support inference+=============================================++> class Fresh m => TC m where+>  lookupSupportVar :: SupportVar -> m () -- just check it exists+>  lookupVar :: Var -> m Type+>  lookupCodeVar :: CodeVar -> m (Type, Support)+>  lookupNom :: Nominal -> m Type+>  extendVar :: Var -> Type -> m a -> m a+>  extendCodeVar :: CodeVar -> Type -> Support -> m a -> m a+>  extendNom :: Nominal -> Type -> m a -> m a+>  extendSupportVar :: SupportVar -> m a -> m a+>+>  tcError :: String -> m a+>  default tcError :: (MonadError String m) => String -> m a+>  tcError = throwError+>+>  inSupport :: Nominal -> m ()+>  inSupport n = includeSupport (Support [n] [])+>  includeSupport :: Support -> m ()+>  default includeSupport :: (MonadWriter Support m) => Support -> m ()+>  includeSupport = tell+>+>  -- run the subcomputation, grab its support and then completely censor it+>  withEmptySupport :: m a -> m (a, Support)+>  default withEmptySupport :: (MonadWriter Support m) => m a -> m (a, Support)+>  withEmptySupport comp =+>    let censorEverything = const mempty+>    in pass ((\asup -> (asup, censorEverything)) <$> listen comp)+>    ++> wellFormed :: TC m => Type -> m ()+> wellFormed t0 =+>   case t0 of+>     BaseT {} -> return ()+>     (ArrT t1 t2) -> wellFormed t1 >> wellFormed t2+>     (NomArrT t1 t2) -> wellFormed t1 >> wellFormed t2+>     (BoxT t sup) -> wellFormed t >> wellFormedSupport sup+>     (ForallSupT bnd) -> do+>       (sv, t) <- unbind bnd+>       extendSupportVar sv $ wellFormed t+>     _ -> tcError ("Unimplemented! " ++ show t0)++> wellFormedSupport :: TC m => Support -> m ()+> wellFormedSupport (Support _noms svs) = mapM_ lookupSupportVar svs++> newtype Expected a = Expecting { unExpecting :: a }++> inferExpr :: TC m => Expr -> m Type+> inferExpr e = inferExpr_ e (Expecting Nothing)++> expecting :: TC m => Expected (Maybe Type) -> Type -> m Type+> expecting (Expecting Nothing) t = return t+> expecting (Expecting (Just texp)) t = do+>   unless (t `aeq` texp) $ tcError $ "expected type " ++ show texp ++ " but got " ++ show t+>   return texp++> expectingSup :: TC m => Expected (Maybe Support) -> Support -> m Support+> expectingSup (Expecting Nothing) sup = return sup+> expectingSup (Expecting (Just supexp)) sup = do+>   unless (sup `subsup` supexp) $ tcError $ "expected support " ++ show supexp ++ " but got " ++ show sup+>   return supexp++> subsup :: Support -> Support -> Bool+> subsup (Support noms svs) (Support noms' svs') =+>   let nomsLeq = noms `isSubsequenceOf` noms'+>       svsLeq = svs `isSubsequenceOf` svs'+>   in nomsLeq && svsLeq++> checkExpr :: TC m => Expr -> Type -> m ()+> checkExpr e t = inferExpr_ e (Expecting (Just t)) >> return ()+> ++> inferExpr_ :: (TC m) => Expr -> Expected (Maybe Type) -> m Type+> inferExpr_ e0 xpt =+>   case e0 of+>     V x -> lookupVar x >>= expecting xpt+>     U noms u -> do+>       (t, supIn) <- lookupCodeVar u+>       checkSubst noms supIn+>       expecting xpt t+>     N nX -> do+>       t <- lookupNom nX+>       inSupport nX+>       expecting xpt t+>     C bc -> expecting xpt $ BaseT $ inferConst bc+>     P primOp vs es -> do+>       checkPrimitive primOp (vs ++ es) >>= expecting xpt+>     Lambda bnd -> do+>       ((x, unembed -> tDom), e) <- unbind bnd+>       wellFormed tDom+>       xptCod <- unExpectArrType xpt tDom+>       tCod <- extendVar x tDom $ inferExpr_ e xptCod+>       return (tDom `ArrT` tCod)+>     App e1 e2 -> do+>       tf <- inferExpr e1+>       (tDom, tCod) <- matchArrType tf+>       checkExpr e2 tDom+>       expecting xpt tCod+>     RecFun bnd -> do+>       ((f, x, unembed -> tDom, unembed -> tCod), e) <- unbind bnd+>       let funT = tDom `ArrT` tCod+>       extendVar f funT $ extendVar x tDom $ checkExpr e tCod+>       expecting xpt funT+>     Let e1 bnd -> do+>       t <- inferExpr e1+>       (x, e2) <- unbind bnd+>       extendVar x t $ inferExpr_ e2 xpt+>     Box c -> do+>       (xpt, xpsup) <- unExpectBoxType xpt+>       inferCode c xpt xpsup+>     LetBox e1 bnd -> do+>       tbox <- inferExpr e1+>       (t, sup) <- matchBoxType tbox+>       (u, e2) <- unbind bnd+>       extendCodeVar u t sup $ inferExpr_ e2 xpt+>     New bnd -> do+>       ((nX, unembed -> tDom), e) <- unbind bnd+>       wellFormed tDom+>       xptCod <- unExpectNomArrType xpt tDom+>       (tCod, supOut) <- withEmptySupport $ extendNom nX tDom $ inferExpr_ e xptCod+>       let inType = anyOf fv (== nX) tCod+>           inSup = anyOf fv (== nX) (supportNominals supOut)+>       when inType $ tcError ("Name " ++ show nX ++ " appears in the result type of a ν-expression " ++ show tCod)+>       when inSup $ tcError ("Name " ++ show nX ++ " appears in the support of ν-expression " ++ show supOut)+>       includeSupport supOut+>       return (tDom `NomArrT` tCod)+>     Choose e -> do+>       tarr <- inferExpr e+>       (_, tCod) <- matchNomArrType tarr+>       expecting xpt tCod+>     PLamSupport bnd -> do+>       (sv, e) <- unbind bnd+>       xptout <- unExpectForallSupType xpt sv +>       (t, supOut) <- withEmptySupport $ extendSupportVar sv $ inferExpr_ e xptout+>       let inSup = anyOf fv (== sv) (supportVars supOut)+>       when inSup $ tcError ("support variable " ++ show sv ++ " appears in the support of the body of the support-polymorphic function")+>       includeSupport supOut+>       expecting xpt $ ForallSupT (bind sv t)+>     PAppSupport e sup -> do+>       tall <- inferExpr e+>       wellFormedSupport sup+>       (sv, t) <- matchForallSup tall+>       expecting xpt $ subst sv sup t++> inferCode :: TC m => Code -> Expected (Maybe Type) -> Expected (Maybe Support) -> m Type+> inferCode (Code e) xpt xpsup = do+>   (t, supOut) <- withEmptySupport (inferExpr_ e xpt)+>   BoxT t <$> expectingSup xpsup supOut++> matchArrType :: TC m => Type -> m (Type, Type)+> matchArrType (ArrT tdom tcod) = return (tdom, tcod)+> matchArrType t0 = tcError ("Expected an expression of function type, got " ++ show t0)++> matchNomArrType :: TC m => Type -> m (Type, Type)+> matchNomArrType (NomArrT tdom tcod) = return (tdom, tcod)+> matchNomArrType t0 = tcError ("Expected an expression of nominal-function type, got " ++ show t0)++> matchForallSup :: TC m => Type -> m (SupportVar, Type)+> matchForallSup (ForallSupT bnd) = unbind bnd+> matchForallSup t0 = tcError ("Expected a support-polymorphic type, got " ++ show t0)++> matchBoxType :: TC m => Type -> m (Type, Support)+> matchBoxType (BoxT t sup) = return (t, sup)+> matchBoxType t0 = tcError ("Expected an expression of code type, got " ++ show t0)++> unExpectArrType :: TC m => Expected (Maybe Type) -> Type -> m (Expected (Maybe Type))+> unExpectArrType (Expecting Nothing) _ = return (Expecting Nothing)+> unExpectArrType (Expecting (Just texp)) t' = do+>   (tdom, tcod) <- matchArrType texp+>   expecting (Expecting (Just tdom)) t'+>   return (Expecting (Just tcod))++> unExpectNomArrType :: TC m => Expected (Maybe Type) -> Type -> m (Expected (Maybe Type))+> unExpectNomArrType (Expecting Nothing) _ = return (Expecting Nothing)+> unExpectNomArrType (Expecting (Just texp)) t' = do+>   (tdom, tcod) <- matchNomArrType texp+>   expecting (Expecting (Just tdom)) t'+>   return (Expecting (Just tcod))++> unExpectForallSupType :: TC m => Expected (Maybe Type) -> SupportVar -> m (Expected (Maybe Type))+> unExpectForallSupType (Expecting Nothing) _ = return (Expecting Nothing)+> unExpectForallSupType (Expecting (Just texp)) sv = do+>   (sv', t) <- matchForallSup texp+>   return (Expecting (Just (swaps (PermM.single (AnyName sv) (AnyName sv')) t)))++> unExpectBoxType :: TC m => Expected (Maybe Type) -> m (Expected (Maybe Type), Expected (Maybe Support))+> unExpectBoxType (Expecting Nothing) = return (Expecting Nothing, Expecting Nothing)+> unExpectBoxType (Expecting (Just t)) = do+>   (t', sup) <- matchBoxType t+>   return (Expecting (Just t'), Expecting (Just sup))++> inferConst :: BaseConst -> BaseType+> inferConst UnitC = UnitT+> inferConst (BoolC _) = BoolT+> inferConst (IntC _) = IntT++> checkPrimitive :: TC m => PrimOp -> [Expr] -> m Type+> checkPrimitive (IfPrim t) [e, thunk1, thunk2] = do+>   wellFormed t+>   let thunkT = unitT `ArrT` t+>   checkExpr e boolT+>   checkExpr thunk1 thunkT+>   checkExpr thunk2 thunkT+>   return thunkT+> checkPrimitive (IfPrim _) es = tcError $ "if expression expected 2 branches, got " ++ show (length es)+> checkPrimitive AddPrim [e1,e2] = checkExpr e1 intT >> checkExpr e2 intT >> return intT+> checkPrimitive AddPrim es = binOpPrimitiveError AddPrim es+> checkPrimitive MulPrim [e1,e2] = checkExpr e1 intT >> checkExpr e2 intT >> return intT+> checkPrimitive MulPrim es = binOpPrimitiveError MulPrim es+> checkPrimitive LeqPrim [e1,e2] = checkExpr e1 intT >> checkExpr e2 intT >> return boolT+> checkPrimitive LeqPrim es = binOpPrimitiveError LeqPrim es+>+> binOpPrimitiveError :: TC m => PrimOp -> [Expr] -> m a+> binOpPrimitiveError p es = tcError $ "expected 2 arguments to " ++ show p ++ ", got " ++ show (length es)+++> checkSubst :: (TC m) => NominalSubst -> Support -> m ()+> checkSubst = go . nominalSubst+>   where+>     go :: (TC m) => [(Nominal, Nom)] -> Support -> m ()+>     go [] = includeSupport+>     go ((nX, ne):noms) = \supIn -> do+>       t <- lookupNom nX+>       checkExpr (nomExpr ne) t+>       go noms (supIn `excludingNominal` nX)++> excludingNominal :: Support -> Nominal -> Support+> excludingNominal (Support noms svs) nX = Support (noms \\ [nX]) svs++> instance Monoid Support where+>   mempty = Support [] []+>   (Support noms svs) `mappend` (Support noms' svs') = Support noms'' svs''+>     where+>       noms'' = nub $ sort (noms ++ noms')+>       svs'' = nub $ sort (svs ++ svs')++> anyOf :: Fold s a -> (a -> Bool) -> s -> Bool+> anyOf l f =  getAny . foldMapOf l (Any . f)++> data Env = Env { envSigma :: NomCtx, envDelta :: [(CodeVar, Embed (Type, Support))], envGamma :: [(Var, Embed Type)] }++> newtype TypeCheck a = TypeCheck { unTypeCheck :: ReaderT Env (WriterT Support (ExceptT String FreshM)) a }+>                     deriving (Functor, Applicative, Monad, Fresh)++> hasSupportNomCtx :: SupportVar -> NomCtx -> Bool+> hasSupportNomCtx  _ NilNC = False+> hasSupportNomCtx sv (SnocNC (unrebind -> (ctx, _))) = hasSupportNomCtx sv ctx+> hasSupportNomCtx sv (SnocSupNC (ctx, sv')) | sv == sv' = True+>                                            | otherwise = hasSupportNomCtx sv ctx+>   ++> lookupNominalNomCtx :: Nominal -> NomCtx -> Maybe (Embed Type)+> lookupNominalNomCtx _ NilNC = Nothing+> lookupNominalNomCtx nX (SnocNC (unrebind -> (ctx, (nY, t)))) | nX == nY = Just t+>                                                              | otherwise = lookupNominalNomCtx nX ctx+> lookupNominalNomCtx nX (SnocSupNC (ctx, _)) = lookupNominalNomCtx nX ctx++> instance TC TypeCheck where+>   lookupSupportVar sv = do+>     b <- TypeCheck $ asks (hasSupportNomCtx sv . envSigma)+>     unless b $ tcError ("Support variable " ++ show sv ++ " not in scope")+>   lookupVar x = do+>     m <- TypeCheck $ asks (lookup x . envGamma)+>     case m of+>       Just t -> return (unembed t)+>       Nothing -> tcError ("Variable " ++ show x ++ " not in scope")+>   lookupCodeVar u = do+>     m <- TypeCheck $ asks (lookup u . envDelta)+>     case m of+>       Just ts -> return (unembed ts)+>       Nothing -> tcError ("Code variable " ++ show u ++ " not in scope")+>   lookupNom nX = do+>     m <- TypeCheck $ asks (lookupNominalNomCtx nX . envSigma)+>     case m of+>       Just t -> return (unembed t)+>       Nothing -> tcError ("Nominal " ++ show nX ++ " not in scope")+>   extendVar x t = TypeCheck . local (\env -> env { envGamma = (x, embed t) : envGamma env  }) . unTypeCheck+>   extendCodeVar u t sup = TypeCheck . local (\env -> env { envDelta = (u, embed (t, sup)) : envDelta env } ) . unTypeCheck+>   extendNom nX t = TypeCheck . local (\env -> env { envSigma = SnocNC (rebind (envSigma env) (nX, embed t)) } ) . unTypeCheck+>   extendSupportVar sv = TypeCheck . local (\env -> env { envSigma = SnocSupNC (envSigma env, sv) } ) . unTypeCheck+> +>   tcError = TypeCheck . throwError+>+>   includeSupport = TypeCheck . tell+>+>   withEmptySupport comp =+>     let censorEverything = const mempty+>     in TypeCheck (pass ((\asup -> (asup, censorEverything)) <$> listen (unTypeCheck comp)))++> inferClosedConfig :: TC m => ClosedConfig -> m Type+> inferClosedConfig bnd = do+>   (ctx, expr) <- unbind bnd+>   inferConfiguration ctx expr++> inferConfiguration :: TC m => NomCtx -> Expr -> m Type+> inferConfiguration ctx = inWellFormedNomCtx ctx . inferExpr++> inWellFormedNomCtx :: TC m => NomCtx -> m a -> m a+> inWellFormedNomCtx NilNC = id+> inWellFormedNomCtx (SnocNC (unrebind -> (ctx, (nX, unembed -> t)))) = \k ->+>   inWellFormedNomCtx ctx $ do+>   wellFormed t+>   extendNom nX t k+> inWellFormedNomCtx (SnocSupNC (ctx,sv)) = inWellFormedNomCtx ctx . extendSupportVar sv ++> runTypeCheck :: TypeCheck a -> Either String (a, Support)+> runTypeCheck comp = runFreshM (runExceptT (runWriterT (runReaderT (unTypeCheck comp) emptyEnv)))+>   where emptyEnv = Env emptySigma emptyDelta emptyGamma+>         emptySigma = NilNC+>         emptyDelta = []+>         emptyGamma = []++Appendix: Pretty Printing+==========================++We need our own pretty printer class because we want locally fresh+names when we descened under binders.++> type Precedence = Int++> class Pretty a where+>   pp :: LFresh m => a -> Precedence -> m PP.Doc++> paren :: Functor m => Bool -> m PP.Doc -> m PP.Doc+> paren b = if b then fmap PP.parens else id++> lowest, quantBodyPrec, arrPrec, arrPrecLeft, boxPrec, quantPrec :: Precedence+> lowest = -1+> quantBodyPrec = 2+> arrPrec = 5+> arrPrecLeft = 6+> boxPrec = 9+> quantPrec = 2++> lamBodyPrec, appPrec :: Precedence+> semiPrec = 1+> letBodyPrec = 1+> lamBodyPrec = 2+> leqPrec = 3+> addPrec = 4+> addPrecRight = 5+> mulPrec = 5+> mulPrecRight = 6+> lambdaPrec = 7+> letPrec = 7+> bindingPrec = 8+> appPrec = 10++> instance Pretty BaseType where+>   pp b _p = pure $ PP.text $ case b of+>     UnitT -> "()"+>     BoolT -> "bool"+>     IntT  -> "int"++> instance Pretty Type where+>   pp t0 p = case t0 of+>     BaseT b -> pp b lowest+>     ArrT t1 t2 -> paren (p > arrPrec) (infixBinary <$> pp t1 arrPrecLeft <*> pure (PP.text "→") <*> pp t2 arrPrec)+>     NomArrT t1 t2 -> paren (p > arrPrec) (infixBinary <$> pp t1 arrPrecLeft <*> pure (PP.text "↛") <*> pp t2 arrPrec)+>     BoxT t sup -> paren (p > boxPrec) (ppboxType <$> pp t boxPrec <*> pp sup lowest)+>     ForallSupT bnd -> paren (p > quantPrec) (quantifier "∀" <$> lunbind bnd (\(sv, t) -> (,) <$> pp sv lowest <*> pp t quantBodyPrec))++> instance Pretty Support where+>   pp (Support noms svs) _dk = ppsupport <$> traverse (fmap decorNominal . flip pp lowest) noms <*> traverse (fmap decorSupportVar . flip pp lowest) svs++> decorSupportVar :: PP.Doc -> PP.Doc+> decorSupportVar = PP.dullblue++> decorCodeVar :: PP.Doc -> PP.Doc+> decorCodeVar = PP.dullmagenta++> decorNominal :: PP.Doc -> PP.Doc+> decorNominal = PP.bold++> instance Pretty (Name a) where+>   pp a _dk = pure $ PP.text $ show a++> ppboxType :: PP.Doc -> PP.Doc -> PP.Doc+> ppboxType t sup = PP.hang 2 (PP.text "□_" PP.<> sup PP.</> PP.group t)++> ppsupport :: [PP.Doc] -> [PP.Doc] -> PP.Doc+> ppsupport noms svs = PP.group $ PP.hang 2 $ PP.braces $ PP.fillSep $ PP.punctuate PP.comma (noms ++ svs)+++> infixBinary :: PP.Doc -> PP.Doc -> PP.Doc -> PP.Doc+> infixBinary p1 s p2 = PP.group $ PP.fillSep [p1, s, p2]++> quantifier :: String -> (PP.Doc, PP.Doc) -> PP.Doc+> quantifier q (obj, body) = PP.text q PP.<//> obj PP.<//> PP.dot PP.<//> body++> instance Pretty Expr where+>   pp e0 p = case e0 of+>     V x -> pp x lowest+>     U subs u -> (PP.<>) <$> pp subs lowest <*> (fmap decorCodeVar . flip pp lowest) u+>     N nom -> decorNominal <$> pp nom lowest+>     C c -> pp c lowest+>     P prim vs es -> paren (p > primPrecedence prim) (ppPrimitive prim (vs ++ es))+>     Lambda bnd -> paren (p > lambdaPrec) (pplambda "λ" <$> lunbind bnd (\((x, unembed -> t), e) -> (,) <$> (colonclass <$> pp x lowest <*> pp t lowest) <*> pp e lamBodyPrec))+>     RecFun bnd -> paren (p > lambdaPrec) (pprecfun <$> lunbind bnd (\((f, x, unembed -> targ, unembed -> tres), e) -> (,,,,) <$> pp f lowest <*> pp x lowest <*> pp targ lowest <*> pp tres lowest <*> pp e lamBodyPrec ))+>     App e1 e2 -> paren (p > appPrec) (ppapp <$> pp e1 appPrec <*> pp e2 (appPrec + 1))+>     Let e1 bnd -> paren (p > letPrec) (pplet PP.empty <$> pp e1 bindingPrec <*> lunbind bnd (\(x, e2) -> (,) <$> pp x lowest <*> pp e2 letBodyPrec))+>     LetBox e1 bnd -> paren (p > letPrec) (pplet (PP.text "□") <$> pp e1 bindingPrec <*> lunbind bnd (\(u, e2) -> (,) <$> (fmap decorCodeVar . flip pp lowest) u <*> pp e2 letBodyPrec))+>     Box code -> pp code lowest+>     New bnd -> paren (p > lambdaPrec) (pplambda "ν" <$> lunbind bnd (\((nX, unembed -> t), e) -> (,) <$> ppnombind nX t <*> pp e lamBodyPrec))+>     Choose e -> paren (p > appPrec) (ppapp (PP.text "choose") <$> pp e appPrec)+>     PLamSupport bnd -> paren (p > lambdaPrec) (pplambda "Λ" <$> lunbind bnd (\(sv, e) -> (,) <$> (fmap decorSupportVar . flip pp lowest) sv <*> pp e lamBodyPrec))+>     PAppSupport e sup -> paren (p > appPrec) (PP.nest 2 <$> ((PP.</>) <$> (fmap PP.group . flip pp appPrec) e <*> pp sup lowest))++> ppnombind nX t = assocclass <$> fmap decorNominal (pp nX lowest) <*> pp t lowest++> instance Pretty Code where+>   pp (Code e) _dk = ppcode <$> pp e lowest+>     where+>       ppcode c = PP.enclose (PP.text "“") (PP.text "”") (PP.underline c)++> instance Pretty BaseConst where+>   pp bc _dk = pure $ case bc of+>     UnitC -> PP.text "()"+>     BoolC b -> PP.text $ show b+>     IntC i -> PP.int i++> primPrecedence :: PrimOp -> Precedence+> primPrecedence (IfPrim _) = appPrec+> primPrecedence AddPrim = addPrec+> primPrecedence MulPrim = mulPrec+> primPrecedence LeqPrim = leqPrec+>+> ppPrimitive :: LFresh m => Pretty a => PrimOp -> [a] -> m PP.Doc+> ppPrimitive (IfPrim t) es = do+>   let prettyExpr = fmap PP.group . flip pp semiPrec+>   pes <- traverse prettyExpr es+>   pt <- pp t lowest+>   return $ PP.group (PP.text "if" PP.</> (PP.parens $ PP.group $ PP.align $ PP.vsep $ PP.punctuate PP.semi (pt:pes)))+> ppPrimitive AddPrim [e1,e2] = infixBinary <$> pp e1 addPrec <*> pure (PP.text "+") <*> pp e2 addPrecRight+> ppPrimitive MulPrim [e1,e2] = infixBinary <$> pp e1 mulPrec <*> pure (PP.text "*") <*> pp e2 mulPrecRight+> ppPrimitive LeqPrim [e1,e2] = infixBinary <$> pp e1 leqPrec <*> pure (PP.text "≤") <*> pp e2 leqPrec+> ppPrimitive _ _ = error "cant' happen (in well-typed code)"++> colonclass :: PP.Doc -> PP.Doc -> PP.Doc+> colonclass v clas = v PP.<//> PP.colon PP.<//> clas++> assocclass :: PP.Doc -> PP.Doc -> PP.Doc+> assocclass v clas = v PP.<//> PP.text "∼" PP.<//> clas++> pplambda :: String -> (PP.Doc, PP.Doc) -> PP.Doc+> pplambda lam (vclas, body) = PP.group $ PP.align $ PP.vsep [PP.group (PP.vcat [PP.text lam, vclas, PP.dot]), PP.group body]++> ppapp :: PP.Doc -> PP.Doc -> PP.Doc+> ppapp e1 e2 = PP.group $ PP.align $ PP.vsep [PP.group e1, PP.nest 2 (PP.group e2)]++> pprecfun :: (PP.Doc, PP.Doc, PP.Doc, PP.Doc, PP.Doc) -> PP.Doc+> pprecfun (f, x, targ, tres, e) = PP.group $ PP.hang 2 $ PP.vsep [PP.text "rec", PP.group (PP.sep [f, binding, PP.colon, tres]), PP.group (PP.text "is" PP.</> PP.nest 2 (PP.group e))]+>   where+>     binding = PP.parens (colonclass x targ)++> pplet :: PP.Doc -> PP.Doc -> (PP.Doc, PP.Doc) -> PP.Doc+> pplet unclas e1 (x,e2) = PP.fillSep [PP.text "let", PP.hang 2 binding] PP.</> PP.group (PP.hang 2 (PP.text "in" PP.</> PP.nest 2 e2))+>   where+>     binding = PP.group (PP.fillSep [unclas PP.<//> x, PP.text "="] PP.</> PP.hang 2 e1)++> instance Pretty NominalSubst where+>   pp (NominalSubst subs) _dk =+>     ppsubstitution <$> traverse ppsub subs+>     where+>       ppsubstitution = PP.enclose (PP.text "⟨") (PP.text "⟩") . PP.fillSep . PP.punctuate PP.comma+>       ppsub (nX, nom) = mapto <$> (fmap decorNominal . flip pp lowest) nX <*> pp nom lowest+>       mapto nX nom = PP.group (nX PP.</> PP.text "↦") PP.</> nom++> instance Pretty Nom where+>   pp = pp . nomExpr++> pretty :: Pretty a => a -> String+> pretty a = flip PP.displayS "" $ PP.renderSmart 0.8 80 $ runLFreshM $ pp a lowest++> ppretty :: Pretty a => a -> IO ()+> ppretty a = do+>   PP.displayIO IO.stdout $ PP.renderSmart 0.8 80 $ runLFreshM $ pp a lowest+>   putStrLn ""++Example+-------++> -- >>> ppretty (pexp @@ number 3)+> -- (λn:int.+> -- choose+> --  (νX∼int.+> --   let □w = (Λp.+> --             λe:□_{p} int.+> --             rec+> --               go (m:int) : □_{p} int+> --               is if (□_{p} int;+> --                      m ≤ 0;+> --                      λ_:(). “1”;+> --                      λ_:(). let □u = go (m + -1) in let □w = e in “⟨⟩u * ⟨⟩w”)+> --                  ()) {X}+> --            “X”+> --            n in “λx:int. ⟨X ↦ x⟩w”))+> -- 3++Appendix: Tracing evaluator+===========================++> instance Pretty NomCtx where+>   pp NilNC _ = pure PP.empty+>   pp (SnocNC (unrebind -> (NilNC, (nX, unembed -> t)))) _ = ppnombind nX t+>   pp (SnocNC (unrebind -> (ctx, (nX, unembed -> t)))) _ = (PP.</>) <$> fmap (\pctx -> pctx PP.</> PP.comma) (pp ctx lowest) <*> ppnombind nX t+>   pp (SnocSupNC (NilNC, sv)) _ = decorSupportVar <$> pp sv lowest+>   pp (SnocSupNC (ctx, sv)) _ = (PP.</>) <$> fmap (\pctx -> pctx PP.</> PP.comma) (pp ctx lowest) <*> (decorSupportVar <$> pp sv lowest)++> data WellTypedProgram = WellTypedProgram NomCtx Expr Type Support+>+> instance Pretty WellTypedProgram where+>   pp (WellTypedProgram ctx e t sup) _ = do+>     pctx <- pp ctx lowest+>     pe <- pp e lowest+>     pt <- pp t lowest+>     psup <- pp sup lowest+>     return $ PP.group $ PP.vcat [pctx, PP.text "⊢", pe, PP.nest 2 PP.colon, PP.nest 4 pt, PP.nest 4 psup]++> traceProgram :: Expr -> IO ()+> traceProgram initialExpr = do+>   let tc ctx e = case runTypeCheck (inferConfiguration ctx e) of+>         Right (t, sup) -> ppretty (WellTypedProgram ctx e t sup)+>         Left err -> putStrLn err >> fail "(Not running)"+>   let+>     trace :: (Fresh m, MonadState NomCtx m, MonadError String m, MonadIO m) => Expr -> m ()+>     trace e =+>         if isValue e+>         then return ()+>         else+>         do+>           e' <- step e+>           ctx <- get+>           liftIO $ do+>             tc ctx e'+>             putStrLn "————"+>           trace e'+>   tc NilNC initialExpr+>   putStrLn "————"+>   _ <- runFreshMT (runExceptT (runStateT (trace initialExpr) NilNC))+>   return ()+> _ = ()++
+ examples/Prof.hs view
@@ -0,0 +1,42 @@+{-# language GADTs, RankNTypes #-}+module Prof where++import Data.Profunctor++data Shop a b s t where+  Shop :: (s -> a) -> (s -> b -> t) -> Shop a b s t++type Optic p s t a b  = p a b -> p s t+type Lens s t a b = forall p . (Strong p) => Optic p s t a b++lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b+lens get set pab = dimap (\s -> (s, get s)) (uncurry set) (second' pab)++instance Profunctor (Shop a b) where+  dimap f g (Shop get set) = Shop (get . f) (\s -> g . set (f s))++instance Strong (Shop a b) where+   first' (Shop get set) = Shop (get . fst) (\(s,c) b -> (set s b, c))++withLens :: Lens s t a b -> ((s -> a) -> (s -> b -> t) -> r) -> r+withLens l k = case l (Shop id (const id)) of+                 Shop getter setter -> k getter setter+++type Prism s t a b = forall p . (Choice p) => Optic p s t a b++prism :: (s -> Either t a) -> (b -> t) -> Prism s t a b+prism view review pab = dimap view (either id review) (right' pab)++data Market a b s t where+  Market :: (s -> Either t a) -> (b -> t) -> Market a b s t++instance Profunctor (Market a b) where+  dimap f g (Market view review) = Market (either (Left . g) Right . (view . f)) (g . review)++instance Choice (Market a b) where+  left' (Market view review) = Market (either (either (Left . Left) Right . view) (Left . Right)) (Left . review)++withPrism :: Prism s t a b -> ((s -> Either t a) -> (b -> t) -> r) -> r+withPrism l k = case l (Market Right id) of+                  Market view review -> k view review
src/Unbound/Generics/LocallyNameless/Alpha.hs view
@@ -58,8 +58,9 @@ import Data.Functor.Contravariant (Contravariant(..)) import Data.Foldable (Foldable(..)) import Data.List (intersect)-import Data.Monoid (Monoid(..), (<>), All(..))+import Data.Monoid (Monoid(..), All(..)) import Data.Ratio (Ratio)+import Data.Semigroup as Sem import Data.Typeable (Typeable, gcast, typeOf) import GHC.Generics @@ -117,13 +118,17 @@ -- @ newtype DisjointSet a = DisjointSet (Maybe [a]) -instance Eq a => Monoid (DisjointSet a) where-  mempty = DisjointSet (Just [])-  mappend s1 s2 =+-- | @since 0.3.2+instance Eq a => Sem.Semigroup (DisjointSet a) where+  (<>) = \s1 s2 ->     case (s1, s2) of       (DisjointSet (Just xs), DisjointSet (Just ys)) | disjointLists xs ys -> DisjointSet (Just (xs <> ys))       _ -> inconsistentDisjointSet +instance Eq a => Monoid (DisjointSet a) where+  mempty = DisjointSet (Just [])+  mappend = (<>)+ instance Foldable DisjointSet where   foldMap summarize (DisjointSet ms) = foldMap (foldMap summarize) ms @@ -262,12 +267,13 @@   {-# INLINE fmap #-}  instance Applicative (FFM f) where-  pure = return+  pure x = FFM (\r _j -> r x)+  {-# INLINE pure #-}   (FFM h) <*> (FFM k) = FFM (\r j -> h (\f -> k (r . f) j) j)   {-# INLINE (<*>) #-}  instance Monad (FFM f) where-  return x = FFM (\r _j -> r x)+  return = pure   {-# INLINE return #-}   (FFM h) >>= f = FFM (\r j -> h (\x -> runFFM (f x) r j) j)   {-# INLINE (>>=) #-}@@ -291,13 +297,17 @@ -- is the @i@th name in @a@ newtype NthPatFind = NthPatFind { runNthPatFind :: Integer -> Either Integer AnyName } -instance Monoid NthPatFind where-  mempty = NthPatFind Left-  mappend (NthPatFind f) (NthPatFind g) =+-- | @since 0.3.2+instance Sem.Semigroup NthPatFind where+  (<>) = \(NthPatFind f) (NthPatFind g) ->     NthPatFind $ \i -> case f i of     Left i' -> g i'     found@Right {} -> found +instance Monoid NthPatFind where+  mempty = NthPatFind Left+  mappend = (<>)+ -- | The result of @'namePatFind' a x@ is either @Left i@ if @a@ is a pattern that -- contains @i@ free names none of which are @x@, or @Right j@ if @x@ is the @j@th name -- in @a@@@ -306,14 +316,18 @@                                                       -- Right - index of the name we found                                                       -> Either Integer Integer } -instance Monoid NamePatFind where-  mempty = NamePatFind (\_ -> Left 0)-  mappend (NamePatFind f) (NamePatFind g) =+-- | @since 0.3.2+instance Sem.Semigroup NamePatFind where+  (<>) = \(NamePatFind f) (NamePatFind g) ->     NamePatFind $ \nm -> case f nm of     ans@Right {} -> ans     Left n -> case g nm of       Left m -> Left $! n + m       Right i -> Right $! n + i++instance Monoid NamePatFind where+  mempty = NamePatFind (\_ -> Left 0)+  mappend = (<>)  -- | The "Generic" representation version of 'Alpha' class GAlpha f where
src/Unbound/Generics/LocallyNameless/Fresh.hs view
@@ -19,6 +19,7 @@  import Control.Monad.Identity +import Control.Monad.Catch (MonadThrow, MonadCatch, MonadMask) import Control.Monad.Trans import Control.Monad.Trans.Except import Control.Monad.Trans.Error@@ -53,7 +54,18 @@ --   still globally unused, and increments the index every time it is --   asked for a fresh name. newtype FreshMT m a = FreshMT { unFreshMT :: St.StateT Integer m a }-  deriving (Functor, Applicative, Alternative, Monad, MonadPlus, MonadIO, MonadFix)+  deriving+    ( Functor+    , Applicative+    , Alternative+    , Monad+    , MonadIO+    , MonadPlus+    , MonadFix+    , MonadThrow+    , MonadCatch+    , MonadMask+    )  -- | Run a 'FreshMT' computation (with the global index starting at zero). runFreshMT :: Monad m => FreshMT m a -> m a
src/Unbound/Generics/LocallyNameless/LFresh.hs view
@@ -63,6 +63,7 @@ import Data.Monoid import Data.Typeable (Typeable) +import Control.Monad.Catch (MonadThrow, MonadCatch, MonadMask) import Control.Monad.Reader import Control.Monad.Identity import Control.Applicative (Applicative, Alternative)@@ -102,7 +103,18 @@ -- avoid, and when asked for a fresh one will choose the first numeric -- prefix of the given name which is currently unused. newtype LFreshMT m a = LFreshMT { unLFreshMT :: ReaderT (Set AnyName) m a }-  deriving (Functor, Applicative, Alternative, Monad, MonadIO, MonadPlus, MonadFix)+  deriving+    ( Functor+    , Applicative+    , Alternative+    , Monad+    , MonadIO+    , MonadPlus+    , MonadFix+    , MonadThrow+    , MonadCatch+    , MonadMask+    )  -- | Run an 'LFreshMT' computation in an empty context. runLFreshMT :: LFreshMT m a -> m a
src/Unbound/Generics/PermM.hs view
@@ -47,9 +47,10 @@   ) where  import Prelude (Eq(..), Show(..), (.), ($), Monad(return), Ord(..), Maybe(..), otherwise, (&&), Bool(..), id, uncurry, Functor(..))-import Data.Monoid+import Data.Monoid hiding ((<>)) import Data.List import Data.Map (Map)+import Data.Semigroup as Sem import qualified Data.Map as M import qualified Data.Set as S import Control.Arrow ((&&&))@@ -97,10 +98,15 @@   Perm (M.fromList ([ (x,M.findWithDefault y y b) | (x,y) <- M.toList a]          ++ [ (x, M.findWithDefault x x b) | x <- M.keys b, M.notMember x a])) --- | Permutations form a monoid under composition.+-- | Permutations form a semigroup under 'compose'.+-- @since 0.3.2+instance Ord a => Sem.Semigroup (Perm a) where+  (<>) = compose++-- | Permutations form a monoid with identity 'empty'. instance Ord a => Monoid (Perm a) where   mempty  = empty-  mappend = compose+  mappend = (<>)  -- | Is this the identity permutation? isid :: Ord a => Perm a -> Bool
unbound-generics.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                unbound-generics-version:             0.3.1+version:             0.3.2 synopsis:            Support for programming with names and binders using GHC Generics description:         Specify the binding structure of your data type with an                      expressive set of type combinators, and unbound-generics@@ -12,7 +12,7 @@                      @Unbound.Generics.LocallyNameless@ to get started.                      .                      This is an independent re-implementation of <http://hackage.haskell.org/package/unbound Unbound>-                     but using <http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.9.0.0/GHC-Generics.html GHC.Generics>+                     but using <https://hackage.haskell.org/package/base/docs/GHC-Generics.html GHC.Generics>                      instead of <http://hackage.haskell.org/package/RepLib RepLib>.                      See the accompanying README for some porting notes.                      @@ -22,14 +22,14 @@ license-file:        LICENSE author:              Aleksey Kliger maintainer:          aleksey@lambdageek.org-copyright:           (c) 2014-2016, Aleksey Kliger+copyright:           (c) 2014-2018, Aleksey Kliger category:            Language build-type:          Simple-extra-source-files:  examples/*.hs,+extra-source-files:  examples/*.hs, examples/*.lhs,                      README.md,                      Changelog.md cabal-version:       >=1.10-tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1+tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1, GHC == 8.2.2, GHC == 8.4.1               library   exposed-modules:     Unbound.Generics.LocallyNameless@@ -54,16 +54,22 @@   -- other-extensions:       build-depends:       base >=4.6 && <5,                        template-haskell >= 2.8.0.0,-                       deepseq >= 1.3,+                       deepseq >= 1.3.0.0,                        mtl >= 2.1,                        transformers >= 0.3,                        transformers-compat >= 0.3,                        containers == 0.5.*,                        contravariant >= 0.5,-                       profunctors >= 4.0+                       profunctors >= 4.0,+                       ansi-wl-pprint >= 0.6.7.2 && < 0.7,+                       exceptions >= 0.8 && < 0.9   hs-source-dirs:      src   default-language:    Haskell2010   ghc-options:         -Wall+  if impl (ghc >= 8.0.0)+    ghc-options:     -Wcompat+  if !impl(ghc >= 8.0)+    build-depends: semigroups == 0.18.*  Test-Suite test-unbound-generics   type:                exitcode-stdio-1.0@@ -88,6 +94,8 @@   hs-source-dirs:      test   default-language:    Haskell2010   ghc-options:         -Wall+  if impl (ghc >= 8.0.0)+    ghc-options:     -Wcompat  Benchmark benchmark-unbound-generics   type:                exitcode-stdio-1.0@@ -95,12 +103,19 @@   hs-source-dirs:      benchmarks   main-is:             benchmark-main.hs   build-depends:       base-                     , criterion+                     , criterion >= 1.0.0.1                      , deepseq >= 1.3.0.0-                     , deepseq-generics >= 0.1.1.2                      , unbound-generics+  if impl (ghc == 7.6.*)+    build-depends:     unix <= 2.6.0.1+  if impl (ghc == 7.6.*) || impl (ghc == 7.8.*)+    build-depends:     deepseq-generics+  else+    build-depends:     deepseq >= 1.4.0.0   other-modules:       BenchLam   ghc-options:       -Wall+  if impl (ghc >= 8.0.0)+    ghc-options:     -Wcompat  source-repository head   type:                git