packages feed

bound-extras (empty) → 0

raw patch · 19 files changed

+1623/−0 lines, 19 filesdep +basedep +bifunctorsdep +boundsetup-changed

Dependencies added: base, bifunctors, bound, bound-extras, containers, deepseq, filepath, hashable, pretty, tasty, tasty-golden, text-short, transformers, utf8-string

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2018, Oleg Grenrus++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Oleg Grenrus nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bound-extras.cabal view
@@ -0,0 +1,95 @@+cabal-version: 2.2+name:          bound-extras+version:       0++synopsis: ScopeH and ScopeT extras for bound+category: Language, Compilers, Interpreters+description:+  Provides more complex @Scope@ variants; @ScopeT@ and @ScopeH@:+  .+  @+  Scope  b f a   ~ ScopeT b IdentityT f a ~ ScopeH b f f a+  ScopeT b t f a ~ ScopeH b (t f) f a+  @+  .+  'ScopeH' probably should be preferred over 'ScopeT'.+  Latter is left here for completeness.+  .+  Simple implementations of @ScopeH@ and @ScopeT@ would be similar+  (sans type arguments) to @Bound.Scope.Simple@.+  .+  Look into @examples/@ directory for /System F/ and /Bidirectional STLC/+  implemented with a help of 'ScopeH'.++license:      BSD-3-Clause+license-file: LICENSE+copyright:    (c) 2018 Oleg Grenrus++author:       Oleg Grenrus, Edward Kmett+maintainer:   Oleg Grenrus <oleg.grenrus@iki.fi>+homepage:     https://github.com/phadej/bound-extras+bug-reports:  https://github.com/phadej/bound-extras/issues++tested-with:+  GHC ==8.0.2+   || ==8.2.2+   || ==8.4.3+   || ==8.6.1++extra-source-files:+  examples/*.txt++source-repository head+  type: git+  location: https://github.com/phadej/bound-extras++library+  default-language: Haskell2010+  hs-source-dirs:   src+  ghc-options:      -Wall++  exposed-modules:+    Bound.ScopeH+    Bound.ScopeT+    Control.Monad.Module++  build-depends:+    -- GHC boot libraries+    , base           ^>=4.9.1.0 || ^>= 4.10.1.0 || ^>=4.11.1.0+    , deepseq        ^>=1.4.2.0+    , transformers   ^>=0.5.0.0++    -- other deps+    , bound          ^>=2.0.1+    , hashable       ^>=1.2.7.0++  if !impl(ghc >= 8.2)+    build-depends:+      bifunctors ^>=5.5.3+++test-suite examples+  type:             exitcode-stdio-1.0+  main-is:          Examples.hs+  other-modules:+    Pretty+    BiSTLC+    SystemF++  default-language: Haskell2010+  hs-source-dirs:   examples+  ghc-options:      -Wall++  build-depends: base, bound, bound-extras+    , containers   ^>=0.5.7.1+    , filepath     ^>=1.4.1.1+    , pretty       ^>=1.1.3.3+    , tasty        ^>=1.1.0.3+    , text-short   ^>=0.1.2+    , tasty-golden ^>=2.3.2+    , transformers ^>=0.5.0.0+    , utf8-string  ^>=1.0.1.1++  if !impl(ghc >= 8.2)+    build-depends:+      bifunctors ^>=5.5.3
+ examples/BiSTLC.hs view
@@ -0,0 +1,329 @@+{-# LANGUAGE DeriveFoldable         #-}+{-# LANGUAGE DeriveFunctor          #-}+{-# LANGUAGE DeriveTraversable      #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses  #-}+{-# LANGUAGE OverloadedStrings      #-}+module BiSTLC (tests) where++import Bound.ScopeH+import Control.Monad        (ap)+import Control.Monad.Module+import Data.String          (IsString (..))+import System.FilePath      ((-<.>), (</>))+import Test.Tasty           (TestTree, testGroup)+import Test.Tasty.Golden    (goldenVsString)++import qualified Data.ByteString.Lazy.UTF8 as UTF8++import Pretty++-------------------------------------------------------------------------------+-- Types+-------------------------------------------------------------------------------++-- | Types.+data Ty+    = TBool+    | TNat+    | Ty :-> Ty+  deriving Eq++infixr 2 :->++-------------------------------------------------------------------------------+-- Infession+-------------------------------------------------------------------------------++-- | Inferable terms+data Inf a+    = V a++    -- term abstraction+    | App (Inf a) (Chk a)++    -- annotated term+    | Ann (Chk a) Ty++    -- Booleans+    | TT+    | FF++    -- Numbers+    | Zero+    | Succ (Chk a)+  deriving (Functor, Foldable, Traversable)++(.:) :: Chk a -> Ty -> Inf a+(.:) = Ann+infix 1 .:++-- | Checkable terms+data Chk a+    = Inf (Inf a)+    | Lam (ScopeH () Chk Inf a)++    -- : Bool -> a -> a -> a+    | If (Chk a) (Chk a) (Chk a)++    -- : a -> (a -> a) -> Nat -> a+    | FoldNat (Chk a) (Chk a) (Chk a)+  deriving (Functor, Foldable, Traversable)++-------------------------------------------------------------------------------+-- Instances+-------------------------------------------------------------------------------++instance IsString a => IsString (Inf a) where fromString = V . fromString+instance IsString a => IsString (Chk a) where fromString = Inf . fromString++instance Applicative Inf where+    pure = V+    (<*>) = ap++instance Monad Inf where+    return = V++    V x      >>= k = k x+    Ann x t  >>= k = Ann (x >>== k) t+    App f x  >>= k = App (f >>= k) (x >>== k)++    TT >>= _ = TT+    FF >>= _ = FF++    Zero   >>= _ = Zero+    Succ n >>= k = Succ (n >>== k)++instance Module Chk Inf where+    Inf x         >>== k = Inf (x >>= k)+    Lam b         >>== k = Lam (b >>== k)+    If c t e      >>== k = If (c >>== k) (t >>== k) (e >>== k)+    FoldNat z s n >>== k = FoldNat (z >>== k) (s >>== k) (n >>== k)++lam_ :: Eq a => a -> Chk a -> Chk a+lam_ x b = Lam (abstract1H x b)++-------------------------------------------------------------------------------+-- Normal form+-------------------------------------------------------------------------------++ann :: Chk a -> Ty -> Inf a+ann (Inf x) _ = x+ann x       t = Ann x t++annNf :: Chk a -> Ty -> Inf a+annNf x t = ann (nfChk x t) t++nf :: Inf a -> Inf a+nf (V x)     = V x+nf (Ann x t) = annNf x t+nf (App f x) = case nf f of+    Ann (Lam f') (a :-> b) -> annNf (instantiate1H (ann x a) f') b+    f'                     -> App f' x -- not normalising, because type unclear++nf TT = TT+nf FF = FF++nf Zero     = Zero+nf (Succ n) = Succ (nfChk n TNat)++nfChk :: Chk a -> Ty -> Chk a+nfChk (Lam x) (_ :-> b) = Lam (toScopeH $ flip nfChk b $ fromScopeH x)+nfChk (Lam x) _         = Lam x -- not simplifying+nfChk (Inf x) _ = case nf x of+    Ann x' _ -> x'+    x'       -> Inf x'+nfChk (If c t e) ty = case nfChk c TBool of+    Inf TT -> nfChk t ty+    Inf FF -> nfChk e ty+    c'     -> If c' t e -- doesn't normalise branches+nfChk (FoldNat z s n) ty = case iter n' of+    Just x  -> nfChk x ty+    Nothing -> FoldNat z s n'+  where+    iter (Inf Zero)       = Just z+    iter (Inf (Succ n'')) = (s' $$) <$> iter n''+    iter _                = Nothing++    n' = nfChk n TNat+    s' = s .: ty :-> ty++-------------------------------------------------------------------------------+-- Pretty+-------------------------------------------------------------------------------++instance Pretty Ty where+    ppr = return . pprTy++pprTy :: Ty -> Doc+pprTy TNat      = text "Nat"+pprTy TBool     = text "Bool"+pprTy (a :-> b) = sexpr (text "->") $ map pprTy $ a : peelArr b++instance Pretty a => Pretty (Inf a) where ppr x = traverse ppr x >>= pprInf+instance Pretty a => Pretty (Chk a) where ppr x = traverse ppr x >>= pprChk++pprInf :: Inf Doc -> PrettyM Doc+pprInf (V x) = pure x+pprInf (App f x) = case peelApp f of+    (f', xs) -> sexpr+        <$> pprInf f'+        <*> traverse pprChk (xs ++ [x])+pprInf (Ann x t) = do+    x' <- pprChk x+    t' <- ppr t+    return $ sexpr (text "the") [t', x']++pprInf TT = return (text "#t")+pprInf FF = return (text "#f")++pprInf Zero     = return (integer 0)+pprInf (Succ n) = case peelNat n of+    Just n' -> return (integer (succ n'))+    Nothing -> sexpr (text "S") . pure <$> pprChk n++pprChk :: Chk Doc -> PrettyM  Doc+pprChk (Inf i) = pprInf i+pprChk (Lam b) = do+    n <- text <$> fresh "x"+    b' <- pprChk (instantiate1H (V n) b)+    return $ sexpr (text "fn") [ n, b' ]+pprChk (If c t e) = sexpr (text "if") <$> traverse pprChk [c, t, e]+pprChk (FoldNat z f n) = sexpr (text "fold-Nat") <$> traverse pprChk [z, f, n]++-- We output+--   (0 1 2 3)+-- instead of+--   (((0 1) 2) 3)+-- small, but nice improvement!+peelApp :: Inf a -> (Inf a, [Chk a])+peelApp (App a b)   = (++ [b]) <$> peelApp a+peelApp e           = (e, [])++peelArr :: Ty -> [Ty]+peelArr (a :-> b) = a : peelArr b+peelArr x         = [x]++peelNat :: Chk a -> Maybe Integer+peelNat (Inf Zero)     = Just 0+peelNat (Inf (Succ n)) = succ <$> peelNat n+peelNat _              = Nothing++-------------------------------------------------------------------------------+-- peelApp+-------------------------------------------------------------------------------++infixl 2 $$++class SApp f g h | h -> f g where+    ($$) :: f a -> g a -> h a++instance SApp Inf Chk Inf where ($$) = App+instance SApp Inf Chk Chk where f $$ x = Inf (f $$ x)++class SBool f where+    tt :: f a+    ff :: f a++instance SBool Inf where+    tt = TT+    ff = FF++instance SBool Chk where+    tt = Inf tt+    ff = Inf ff++-------------------------------------------------------------------------------+-- Type-checking+-------------------------------------------------------------------------------++infer :: (a -> Ty) -> Inf a -> Maybe Ty+infer f = infer' . fmap f++-- No error reporting :)+infer' :: Inf Ty -> Maybe Ty+infer' (V a) = Just a+infer' (App f x) = do+    f' <- infer' f+    case f' of+        a :-> b -> do+            check' x a+            Just b+        _       -> Nothing+infer' (Ann x t) = do+    check' x t+    Just t++infer' TT = Just TBool+infer' FF = Just TBool++infer' Zero     = Just TNat+infer' (Succ n) = do+    check' n TNat+    Just TNat++check' :: Chk Ty -> Ty -> Maybe ()+check' (Lam x) t = case t of+    a :-> b -> check' (instantiate1H (V a) x) b+    _       -> Nothing+check' (Inf x) t = do+    t' <- infer' x+    if t == t'+    then Just ()+    else Nothing+check' (If c t e) ty = do+    check' c TBool+    check' t ty+    check' e ty+check' (FoldNat z f n) ty = do+    check' z ty+    check' f (ty :-> ty)+    check' n TNat++-------------------------------------------------------------------------------+-- Examples+-------------------------------------------------------------------------------++id_ :: Inf ShortText+id_ = lam_ "x" "x" .: TNat :-> TNat++not_ :: Inf ShortText+not_ = lam_ "x" (If "x" ff tt) .: TBool :-> TBool++two_ :: Inf ShortText+two_ = Succ (Inf (Succ (Inf Zero)))++plus_ :: Inf ShortText+plus_ = term .: TNat :-> TNat :-> TNat where+    term = lam_ "n" $ lam_ "m" $ FoldNat "m" s "n"+    s = lam_ "k" $ Inf (Succ "k")++mult_ :: Inf ShortText+mult_ = term .: TNat :-> TNat :-> TNat where+    term = lam_ "n" $ lam_ "m" $ FoldNat (Inf Zero) (plus_ $$ "m") "n"++demo :: String -> Inf ShortText -> [String]+demo name e = case infer (const TBool) e of+    Nothing ->+        [ name ++ " = " ++ pretty e+        , "DOESN'T TYPECHECK"+        ]+    Just t ->+        [ name ++ " : " ++ pretty t+        , name ++ " = " ++ pretty e+        , name ++ " = " ++ pretty (nf e)+        ]++tests :: TestTree+tests = testGroup "Bi-directional STLC"+    [ demo' "id" id_+    , demo' "not-tt"    $ not_ $$ tt+    , demo' "four-plus" $ plus_ $$ Inf two_ $$ Inf two_+    , demo' "four-mult" $ mult_ $$ Inf two_ $$ Inf two_+    ]+  where+    demo' name e = goldenVsString name ("examples" </> name' -<.> "txt")+        $ return $ UTF8.fromString $ unlines+        $ demo name e+      where+        name' = "stlc-" ++ name
+ examples/Examples.hs view
@@ -0,0 +1,12 @@+module Main (main) where++import qualified BiSTLC+import qualified SystemF++import Test.Tasty           (testGroup, defaultMain)++main :: IO ()+main = defaultMain $ testGroup "Examples"+    [ BiSTLC.tests+    , SystemF.tests+    ]
+ examples/Pretty.hs view
@@ -0,0 +1,63 @@+module Pretty (+    Pretty (..),+    pretty,+    PrettyM,+    ShortText,+    Doc,+    sexpr,+    fresh,+    PP.text,+    PP.integer,+    ) where++import Control.Monad.Trans.State.Strict+import Data.Text.Short                  (ShortText)+import Data.Char (isDigit)++import qualified Data.Text.Short  as TS+import qualified Text.PrettyPrint as PP++import qualified Data.Set as Set++type S = Set.Set ShortText+type Doc = PP.Doc+type PrettyM = State S++pretty :: Pretty a => a -> String+pretty x = PP.render (evalState (ppr x) Set.empty)++markUsed :: ShortText -> State S ()+markUsed t = modify' (Set.insert t)++sexpr :: Doc -> [Doc] -> Doc+sexpr car cdr = PP.parens $ PP.hang car 2 $ PP.sep cdr++fresh :: String -> PrettyM String+fresh s = state (pick names)+  where+    pick []       u = (s, u) -- shouldn't happen, names is infinite+    pick (x : xs) u+        | x' `Set.member` u = pick xs u+        | otherwise         = (x, Set.insert x' u)+      where+        x' = TS.pack x++    names = n : [ n ++ show m | m <- [d .. ] ]++    (ds, rn) = span isDigit (reverse s)++    n :: String+    n = reverse rn++    d :: Int+    d = case ds of+      [] -> 0+      _  -> read (reverse ds)++class Pretty a where+    ppr :: a -> PrettyM Doc++instance Pretty ShortText where+    ppr t = do+        markUsed t+        return $ PP.text $ TS.unpack t
+ examples/SystemF.hs view
@@ -0,0 +1,335 @@+{-# LANGUAGE DeriveFoldable        #-}+{-# LANGUAGE DeriveFunctor         #-}+{-# LANGUAGE DeriveTraversable     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+module SystemF (tests) where++import Bound.Class          ((>>>=))+import Bound.Scope+import Bound.ScopeH+import Bound.Var            (Var (..))+import Control.Monad        (ap)+import Control.Monad.Module+import Data.Bifoldable      (Bifoldable (..))+import Data.Bifunctor       (Bifunctor (..))+import Data.Bitraversable   (Bitraversable (..), bifoldMapDefault, bimapDefault)+import Data.Functor.Classes (Eq1 (..), eq1)+import Data.String          (IsString (..))+import System.FilePath      ((-<.>), (</>))+import Test.Tasty           (TestTree, testGroup)+import Test.Tasty.Golden    (goldenVsString)++import qualified Data.ByteString.Lazy.UTF8 as UTF8++import Pretty++-------------------------------------------------------------------------------+-- Types+-------------------------------------------------------------------------------++-- | Types.+--+-- Noteworthy thing is the absence of any /application/.+-- 'TForall' abstract only over types, not type-constructors;+-- so we don't have type application either.+-- In short: everything is well-kinded by construction.+data Ty a+    = TV a+    | Ty a :-> Ty a+    | TForall (Scope () Ty a)+  deriving (Functor, Foldable, Traversable)++infixr 1 :->++instance Applicative Ty where+    pure = TV+    (<*>) = ap++instance Monad Ty where+    return = TV+    TV x      >>= k = k x+    (a :-> b) >>= k = (a >>= k) :-> (b >>= k)+    TForall t >>= k = TForall (t >>>= k)++instance Eq1 Ty where+    liftEq eq (TV a)      (TV a')      = eq a a'+    liftEq eq (a :-> b)   (a' :-> b')  = liftEq eq a a' && liftEq eq b b'+    liftEq eq (TForall a) (TForall a') = liftEq eq a a'++    liftEq _ TV {} _ = False+    liftEq _ (:->) {} _ = False+    liftEq _ TForall {} _ = False++instance Eq a => Eq (Ty a) where (==) = eq1++instance IsString a => IsString (Ty a) where+    fromString = TV . fromString++forall_ :: Eq a => a -> Ty a -> Ty a+forall_ n t = TForall (abstract1 n t)++-------------------------------------------------------------------------------+-- Expression+-------------------------------------------------------------------------------++data Expr b a+    = V a++    -- term abstraction+    | Lam (Ty b) (Scope () (Expr b) a)+    | App (Expr b a) (Expr b a)++    -- type abstraction+    | TyApp (Expr b a) (Ty b)+    | Forall (ScopeH () (Expr' a) Ty b)++instance IsString a => IsString (Expr b a) where+    fromString = V . fromString++{-+-- | In practice we should write Bitraversable instance, and use+-- 'bimapDefault' as Bifunctor implementation.+--+-- That's an argument (not a good one) to omit 'bimapScope' from @bound@.+instance Bifunctor Expr where+    bimap f g = go where+        go (V x) = V (g x)+        go (App a b) = App (go a) (go b)+        go (TyApp a b) = TyApp (go a) (fmap f b)+        go (Lam t b) = Lam (fmap f t) (bimapScope f g b)+        go (Forall (ScopeH b)) = Forall $ ScopeH $+            bimap g (fmap (fmap f)) b+-}++instance Bifunctor  Expr  where bimap     = bimapDefault+instance Bifunctor  Expr' where bimap     = bimapDefault+instance Bifoldable Expr  where bifoldMap = bifoldMapDefault+instance Bifoldable Expr' where bifoldMap = bifoldMapDefault++instance Bitraversable Expr' where+    bitraverse f g = fmap Expr' . bitraverse g f . unExpr'++instance Bitraversable Expr where+    bitraverse f g = go where+        go (V x)       = V <$> g x+        go (App a b)   = App <$> go a <*> go b+        go (TyApp a b) = TyApp <$> go a <*> traverse f b+        go (Lam t b)   = Lam <$> traverse f t <*> bitraverseScope f g b+        go (Forall s)  = Forall <$> bitransverseScopeH (bitraverse g) traverse f s++instance Functor (Expr b) where+    fmap = second++instance Applicative (Expr b) where+    pure = V+    (<*>) = ap++instance Monad (Expr b) where+    return = V++    V x       >>= k = k x+    App a b   >>= k = App (a >>= k) (b >>= k)+    Lam t b   >>= k = Lam t (b >>>= k)+    TyApp a b >>= k = TyApp (a >>= k) b+    Forall b  >>= k = Forall $ ScopeH $ overExpr' (>>= k') $ unscopeH b where+        k' = first (F . TV) . k++-- | @'Flip' 'Expr'@.+newtype Expr' a b = Expr' { unExpr' :: Expr b a }++overExpr :: (Expr' a b -> Expr' a b') -> Expr b a -> Expr b' a+overExpr f = unExpr' . f . Expr'++overExpr' :: (Expr b a -> Expr b' a') -> Expr' a b -> Expr' a' b'+overExpr' f = Expr' . f . unExpr'++instance Functor (Expr' a) where+    fmap f = overExpr' (first f)++instance Module (Expr' c) Ty where+    Expr' (V a) >>== _ = Expr' (V a)++    Expr' (Lam t s) >>== k = Expr' $ Lam (t >>= k) $ hoistScope (overExpr (>>== k)) s+    Expr' (Forall s) >>== k = Expr' $ Forall $ s >>== k++    Expr' (App a b) >>== k = Expr' $ App+        (unExpr' (Expr' a >>== k))+        (unExpr' (Expr' b >>== k))++    Expr' (TyApp a b) >>== k = Expr' $ TyApp+        (unExpr' (Expr' a >>== k))+        (b >>= k)++tyLam_ :: Eq b => b -> Expr b a -> Expr b a+tyLam_ n e = Forall $ abstract1H n (Expr' e)++lam_ :: Eq a => a -> Ty b -> Expr b a -> Expr b a+lam_ x t b = Lam t (abstract1 x b)++-------------------------------------------------------------------------------+-- Normal form+-------------------------------------------------------------------------------++tnf :: Ty b -> Ty b+tnf (TV x) = TV x+tnf (a :-> b) = tnf a :-> tnf b+tnf (TForall a) = TForall (toScope $ tnf $ fromScope a)++nf :: Expr b a -> Expr b a+nf (V x) = V x+nf (Lam t b) = Lam (tnf t) (toScope $ nf $ fromScope b)+nf (App a b) = case nf a of+    Lam _ a' -> nf $ instantiate1 b a'+    a'       -> App a' (nf b)+nf (Forall e) = Forall $ toScopeH $ overExpr' nf $ fromScopeH e+nf (TyApp a b) = case nf a of+    Forall a' -> nf $ unExpr' $ instantiate1H b a'+    a'        -> TyApp a' (tnf b)++-------------------------------------------------------------------------------+-- Pretty+-------------------------------------------------------------------------------++instance Pretty a => Pretty (Ty a) where+    ppr x = traverse ppr x >>= pprTy++pprTy :: Ty Doc -> PrettyM Doc+pprTy (TV x)      = return x+pprTy (a :-> b)   = sexpr (text "->") <$> traverse pprTy [a, b]+pprTy (TForall s) = do+    a <- text <$> fresh "a"+    pprTy (instantiate1 (TV a) s)++instance (Pretty a, Pretty b) => Pretty (Expr b a) where+    ppr x = bitraverse ppr ppr x >>= pprExpr++pprExpr :: Expr Doc Doc -> PrettyM Doc+pprExpr (V x)       = return x+pprExpr (App a b)   = pprApplications $ applications a ++ [Right b]+pprExpr (TyApp a b) = pprApplications $ applications a ++ [Left b]+pprExpr (Lam t b)   = do+    x <- text <$> fresh "x"+    t' <- pprTy t+    b' <- pprExpr $ instantiate1 (V x) b+    return $ sexpr (text "fn") [ sexpr "the" [t', x], b']+pprExpr (Forall b) = do+    t <- text <$> fresh "b"+    b' <- pprExpr $ unExpr' $ instantiate1H (TV t) b+    return $ sexpr (text "poly") [ t , b']++pprApplications :: [Either (Ty Doc) (Expr Doc Doc)] -> PrettyM Doc+pprApplications []       = return $ text "()"+pprApplications (x : xs) = sexpr <$> pp x <*> traverse pp xs+  where+    pp = either pprTy pprExpr++-- We output+--   (0 1 2 3)+-- instead of+--   (((0 1) 2) 3)+-- small, but nice improvement!+applications :: Expr a b -> [Either (Ty a) (Expr a b)]+applications (App a b)   = applications a ++ [Right b]+applications (TyApp a b) = applications a ++ [Left b]+applications e           = [Right e]++-------------------------------------------------------------------------------+-- Applications+-------------------------------------------------------------------------------++infixl 2 $$, @@++($$) :: Expr b a -> Expr b a -> Expr b a+($$) = App++(@@) :: Expr b a -> Ty b -> Expr b a+(@@) = TyApp++-------------------------------------------------------------------------------+-- Type-checking+-------------------------------------------------------------------------------++-- | Type-check assuming that free variables have the similarly named type.+-- In systemf type and term namespaces are different!+check :: Eq a => Expr a a -> Maybe (Ty a)+check = check' . fmap TV++-- No error reporting :)+check' :: Eq a => Expr a (Ty a) -> Maybe (Ty a)+check' (V a) = Just a+check' (App f x) = do+    f' <- check' f+    x' <- check' x+    case f' of+        a' :-> b' | a' == x' -> return b'+        _                    -> Nothing+check' (TyApp x t) = do+    x' <- check' x+    case x' of+        TForall b -> return (instantiate1 t b)+        _         -> Nothing+check' (Lam t b) = do+    b' <- check' (instantiate1 (V t) b)+    return (t :-> b')++check' (Forall b) = do+    let b' = unExpr' $ fromScopeH b+    b'' <- check' (fmap (fmap F) b')+    return $ TForall $ toScope b''++-------------------------------------------------------------------------------+-- Identity function+-------------------------------------------------------------------------------++-- idType_ :: Ty ShortText+-- idType_ = forall_ "n" $ "n" :-> "n"++id_ :: Expr ShortText ShortText+id_ = tyLam_ "a" $ lam_ "x" "a" "x"++-------------------------------------------------------------------------------+-- Church numerals+-------------------------------------------------------------------------------++natType :: Ty ShortText+natType = forall_ "a" $ ("a" :-> "a") :-> "a" :-> "a"++zero :: Expr ShortText ShortText+zero = tyLam_ "a" $ lam_ "f" ("a" :-> "a") $ lam_ "z" "a" "z"++-- sucType :: Ty ShortText+-- sucType = natType :-> natType++suc :: Expr ShortText ShortText+suc+    = lam_ "n" natType+    $ tyLam_ "a" $ lam_ "f" ("a" :-> "a") $ lam_ "z" "a"+    $ "n" @@ "a" $$ "f" $$ ("f" $$ "z")++demo :: String -> Expr ShortText ShortText -> [String]+demo name e = case check e of+    Nothing ->+        [ name ++ " = " ++ pretty e+        , "DOESN'T TYPECHECK"+        ]+    Just t ->+        [ name ++ " : " ++ pretty t+        , name ++ " = " ++ pretty e+        , name ++ " = " ++ pretty (nf e)+        ]++tests :: TestTree+tests = testGroup "System F"+    [ demo' "id" id_+    , demo' "0" zero+    , demo' "suc" suc+    , demo' "1" (suc $$ zero)+    , demo' "2" (suc $$ (suc $$ zero))+    ]+  where+    demo' name e = goldenVsString name ("examples" </> name' -<.> "txt")+        $ return $ UTF8.fromString $ unlines+        $ demo name e+      where+        name' = "sysf-" ++ name
+ examples/stlc-four-mult.txt view
@@ -0,0 +1,15 @@+four-mult : Nat+four-mult = ((the+    (-> Nat Nat Nat)+    (fn+       x+       (fn+          x0+          (fold-Nat+             0+             ((the+                 (-> Nat Nat Nat) (fn x1 (fn x2 (fold-Nat x2 (fn x3 (S x3)) x1))))+                x0)+             x))))+   2 2)+four-mult = 4
+ examples/stlc-four-plus.txt view
@@ -0,0 +1,5 @@+four-plus : Nat+four-plus = ((the+    (-> Nat Nat Nat) (fn x (fn x0 (fold-Nat x0 (fn x1 (S x1)) x))))+   2 2)+four-plus = 4
+ examples/stlc-id.txt view
@@ -0,0 +1,3 @@+id : (-> Nat Nat)+id = (the (-> Nat Nat) (fn x x))+id = (the (-> Nat Nat) (fn x x))
+ examples/stlc-not-tt.txt view
@@ -0,0 +1,3 @@+not-tt : Bool+not-tt = ((the (-> Bool Bool) (fn x (if x #f #t))) #t)+not-tt = #f
+ examples/sysf-0.txt view
@@ -0,0 +1,3 @@+0 : (-> (-> a a) (-> a a))+0 = (poly b (fn (the (-> b b) x) (fn (the b x0) x0)))+0 = (poly b (fn (the (-> b b) x) (fn (the b x0) x0)))
+ examples/sysf-1.txt view
@@ -0,0 +1,6 @@+1 : (-> (-> a a) (-> a a))+1 = ((fn+    (the (-> (-> a a) (-> a a)) x)+    (poly b (fn (the (-> b b) x0) (fn (the b x1) (x b x0 (x0 x1))))))+   (poly b0 (fn (the (-> b0 b0) x2) (fn (the b0 x3) x3))))+1 = (poly b (fn (the (-> b b) x) (fn (the b x0) (x x0))))
+ examples/sysf-2.txt view
@@ -0,0 +1,10 @@+2 : (-> (-> a a) (-> a a))+2 = ((fn+    (the (-> (-> a a) (-> a a)) x)+    (poly b (fn (the (-> b b) x0) (fn (the b x1) (x b x0 (x0 x1))))))+   ((fn+       (the (-> (-> a0 a0) (-> a0 a0)) x2)+       (poly+          b0 (fn (the (-> b0 b0) x3) (fn (the b0 x4) (x2 b0 x3 (x3 x4))))))+      (poly b1 (fn (the (-> b1 b1) x5) (fn (the b1 x6) x6)))))+2 = (poly b (fn (the (-> b b) x) (fn (the b x0) (x (x x0)))))
+ examples/sysf-id.txt view
@@ -0,0 +1,3 @@+id : (-> a a)+id = (poly b (fn (the b x) x))+id = (poly b (fn (the b x) x))
+ examples/sysf-suc.txt view
@@ -0,0 +1,7 @@+suc : (-> (-> (-> a a) (-> a a)) (-> (-> a0 a0) (-> a0 a0)))+suc = (fn+   (the (-> (-> a a) (-> a a)) x)+   (poly b (fn (the (-> b b) x0) (fn (the b x1) (x b x0 (x0 x1))))))+suc = (fn+   (the (-> (-> a a) (-> a a)) x)+   (poly b (fn (the (-> b b) x0) (fn (the b x1) (x b x0 (x0 x1))))))
+ src/Bound/ScopeH.hs view
@@ -0,0 +1,330 @@+{-# LANGUAGE CPP                   #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE UndecidableInstances  #-}+-- | 'ScopeH' scope, which allows substitute 'f' into 'g' to get new 'g'.+--+-- Compare following signatures:+--+-- @+-- 'instantiate1'  :: ... => m a -> 'Scope'  b   m a -> m a+-- 'instantiate1H' :: ... => m a -> 'ScopeH' b f m a -> f a+-- @+--+-- 'ScopeH' variant allows to encode e.g. Hindley-Milner types, where+-- we diffentiate between @Poly@ and @Mono@-morphic types.+--+-- @+-- specialise :: Poly a -> Mono a -> Poly a +-- specialise (Forall p) m = 'instantiate1H' m p+-- specialise _          _ = error "ill-kinded"+-- @+--+-- Another applications are /bidirectional/ type-systems or representing+-- normal forms with /normal/ and  /neutral/ terms,+-- aka /introduction/ and /elimination/ terms.+--  +-- Look into @examples/@ directory for /System F/ and /Bidirectional STLC/+-- implemented with a help of 'ScopeH'.+--+module Bound.ScopeH (+    ScopeH (..),+    -- * Abstraction+    abstractH, abstract1H, abstractHEither,+    -- ** Name+    abstractHName, abstract1HName,+    -- * Instantiation+    instantiateH, instantiate1H, instantiateHEither,+    -- * Traditional de Bruijn+    fromScopeH,+    toScopeH,+    -- * Bound variable manipulation+    lowerScopeH,+    convertFromScope,+    splatH,+    bindingsH,+    mapBoundH,+    mapScopeH,+    foldMapBoundH,+    foldMapScopeH,+    traverseBoundH_,+    traverseScopeH_,+    traverseBoundH,+    traverseScopeH,+    bitraverseScopeH,+    bitransverseScopeH,+    ) where++import Bound                (Scope (..), Var (..))+import Bound.Name           (Name (..))+import Control.DeepSeq      (NFData (..))+import Control.Monad.Module (Module (..))+import Data.Bifoldable      (bifoldMap, bitraverse_)+import Data.Bifunctor       (bimap)+import Data.Bitraversable   (Bitraversable (..))+import Data.Foldable        (traverse_)+import Data.Functor.Classes+import Data.Hashable        (Hashable (..))+import Data.Hashable.Lifted (Hashable1 (..), hashWithSalt1)++-- | @'ScopeH' b f m a@ is a @f@ expression abstracted over @g@,+-- with bound variables in @b@, and free variables in @a@.+--+-- @+-- 'Scope' b f a ~ 'ScopeH' n f f a+-- 'ScopeT' b t f a ~ 'ScopeH' b (t f) f a+-- @+--+newtype ScopeH b f m a = ScopeH { unscopeH :: f (Var b (m a)) }++instance (Functor f, Monad m) => Module (ScopeH b f m) m where+    ScopeH s >>== k = ScopeH $ fmap (fmap (>>= k)) s++-------------------------------------------------------------------------------+-- Instances+-------------------------------------------------------------------------------++instance (Functor f, Functor m) => Functor (ScopeH b f m) where+   fmap f (ScopeH a) = ScopeH $ fmap (fmap (fmap f)) a++instance (Foldable f, Foldable m) => Foldable (ScopeH b f m) where+    foldMap f (ScopeH a) = foldMap (foldMap (foldMap f)) a+    foldr f z (ScopeH a) = foldr (flip (foldr (flip (foldr f))))  z a++instance (Traversable f, Traversable m) => Traversable (ScopeH b f m) where+    traverse f (ScopeH a) = ScopeH <$> traverse (traverse (traverse f)) a++instance (Hashable b, Module f m, Hashable1 f, Hashable1 m) => Hashable1 (ScopeH b f m) where+    liftHashWithSalt h s m = liftHashWithSalt (liftHashWithSalt h) s (fromScopeH m)+    {-# INLINE liftHashWithSalt #-}++instance (Hashable b, Module f m, Hashable1 f, Hashable1 m, Hashable a) => Hashable (ScopeH b f m a) where+    hashWithSalt n m = hashWithSalt1 n (fromScopeH m)+    {-# INLINE hashWithSalt #-}++instance NFData (f (Var b (m a))) => NFData (ScopeH b f m a) where+  rnf scope = rnf (unscopeH scope)++instance (Module f m, Eq b, Eq1 f, Eq1 m, Eq a) => Eq  (ScopeH b f m a) where (==) = eq1+instance (Module f m, Ord b, Ord1 f, Ord1 m, Ord a) => Ord  (ScopeH b f m a) where compare = compare1+instance (Show b, Show1 f, Show1 m, Show a) => Show (ScopeH b f m a) where showsPrec = showsPrec1+instance (Read b, Read1 f, Read1 m, Read a) => Read (ScopeH b f m a) where readsPrec = readsPrec1++-------------------------------------------------------------------------------+-- * transformers 0.5 Data.Functor.Classes+-------------------------------------------------------------------------------++instance (Module f m, Eq b, Eq1 f, Eq1 m) => Eq1 (ScopeH b f m) where+  liftEq f m n = liftEq (liftEq f) (fromScopeH m) (fromScopeH n)++instance (Module f m, Ord b, Ord1 f, Ord1 m) => Ord1 (ScopeH b f m) where+  liftCompare f m n = liftCompare (liftCompare f) (fromScopeH m) (fromScopeH n)++instance (Show b, Show1 f, Show1 m) => Show1 (ScopeH b f m) where+    liftShowsPrec sp sl d (ScopeH x) = showsUnaryWith+        (liftShowsPrec (liftShowsPrec sp' sl') (liftShowList sp' sl'))+        "ScopeH" d x+      where+        sp' = liftShowsPrec sp sl+        sl' = liftShowList sp sl++instance (Read b, Read1 f, Read1 m) => Read1 (ScopeH b f m) where+    liftReadsPrec f g = readsData $ readsUnaryWith+        (liftReadsPrec (liftReadsPrec f' g') (liftReadList f' g'))+        "ScopeH" ScopeH+      where+        f' = liftReadsPrec f g+        g' = liftReadList f g++-------------------------------------------------------------------------------+-- Abstraction+-------------------------------------------------------------------------------++-- | Capture some free variables in an expression to yield a 'ScopeH' with bound variables in @b@.+abstractH :: (Functor f, Monad m) => (a -> Maybe b) -> f a -> ScopeH b f m a+abstractH f e = ScopeH (fmap k e) where+    k y = case f y of+        Just z  -> B z+        Nothing -> F (return y)+{-# INLINE abstractH #-}++-- | Abstract over a single variable.+abstract1H :: (Functor f, Monad m, Eq a) => a -> f a -> ScopeH () f m a+abstract1H a = abstractH (\b -> if a == b then Just () else Nothing)+{-# INLINE abstract1H #-}++-- | Capture some free variables in an expression to yield a 'ScopeH' with bound variables in @b@. Optionally change the types of the remaining free variables.+abstractHEither :: (Functor f,  Monad m) => (a -> Either b c) -> f a -> ScopeH b f m c+abstractHEither f e = ScopeH (fmap k e) where+    k y = case f y of+        Left z -> B z+        Right y' -> F (return y')+{-# INLINE abstractHEither #-}++-------------------------------------------------------------------------------+-- Abstraction with Name+-------------------------------------------------------------------------------++-- | Abstraction, capturing named bound variables.+abstractHName :: (Functor f, Monad m) => (a -> Maybe b) -> f a -> ScopeH (Name a b) f m a+abstractHName f t = ScopeH (fmap k t) where+    k a = case f a of+        Just b  -> B (Name a b)+        Nothing -> F (return a)+{-# INLINE abstractHName #-}++-- | Abstract over a single variable+abstract1HName :: (Functor f, Monad m, Eq a) => a -> f a -> ScopeH (Name a ()) f m a+abstract1HName a = abstractHName (\b -> if a == b then Just () else Nothing)+{-# INLINE abstract1HName #-}++-------------------------------------------------------------------------------+-- Instantiation+-------------------------------------------------------------------------------++-- | Enter a 'ScopeH', instantiating all bound variables+instantiateH :: Module f m => (b -> m a) -> ScopeH b f m a -> f a+instantiateH k (ScopeH e) = e >>== \v -> case v of+    B b -> k b+    F a -> a+{-# INLINE instantiateH #-}++-- | Enter a 'ScopeH' that binds one variable, instantiating it+instantiate1H :: Module f m => m a -> ScopeH b f m a -> f a+instantiate1H e = instantiateH (const e)+{-# INLINE instantiate1H #-}++-- | Enter a 'ScopeH', and instantiate all bound and free variables in one go.+instantiateHEither :: Module f m => (Either b a -> m c) -> ScopeH b f m a -> f c+instantiateHEither f (ScopeH e) = e >>== \v -> case v of+    B b -> f (Left b)+    F ea -> ea >>= f . Right+{-# INLINE instantiateHEither #-}++-------------------------------------------------------------------------------+-- Traditional de Bruijn+-------------------------------------------------------------------------------++-- | Convert to traditional de Bruijn.+fromScopeH :: Module f m => ScopeH b f m a -> f (Var b a)+fromScopeH (ScopeH s) = s >>== \v -> case v of+    F e -> fmap F e+    B b -> return (B b)++-- | Convert from traditional de Bruijn to generalized de Bruijn indices.+toScopeH :: (Functor f, Monad m) => f (Var b a) -> ScopeH b f m a+toScopeH e = ScopeH (fmap (fmap return) e)++-- | Convert to 'Scope'.+lowerScopeH+    :: (Functor f, Functor f)+    => (forall x. f x -> h x)+    -> (forall x. m x -> h x)+    -> ScopeH b f m a -> Scope b h a+lowerScopeH f m (ScopeH x) = Scope (f (fmap (fmap m) x))++convertFromScope :: Scope b f a -> ScopeH b f f a+convertFromScope (Scope x) = ScopeH x++-------------------------------------------------------------------------------+-- Extras+-------------------------------------------------------------------------------++-- | Perform substitution on both bound and free variables in a 'ScopeH'.+splatH :: Module f m => (a -> m c) -> (b -> m c) -> ScopeH b f m a -> f c+splatH f unbind (ScopeH e) = e >>== \v -> case v of+    B b -> unbind b+    F ea -> ea >>= f+{-# INLINE splatH #-}++-- | Return a list of occurences of the variables bound by this 'ScopeH'.+bindingsH :: Foldable f => ScopeH b f m a -> [b]+bindingsH (ScopeH s) = foldr f [] s where+    f (B v) vs = v : vs+    f _ vs     = vs+{-# INLINE bindingsH #-}++-- | Perform a change of variables on bound variables.+mapBoundH :: Functor f => (b -> b') -> ScopeH b f m a -> ScopeH b' f m a+mapBoundH f (ScopeH s) = ScopeH (fmap f' s) where+    f' (B b) = B (f b)+    f' (F a) = F a+{-# INLINE mapBoundH #-}++-- | Perform a change of variables, reassigning both bound and free variables.+mapScopeH+    :: (Functor f, Functor m)+    => (b -> d) -> (a -> c)+    -> ScopeH b f m a -> ScopeH d f m c+mapScopeH f g (ScopeH s) = ScopeH $ fmap (bimap f (fmap g)) s+{-# INLINE mapScopeH #-}++-- | Obtain a result by collecting information from bound variables+foldMapBoundH :: (Foldable f, Monoid r) => (b -> r) -> ScopeH b f m a -> r+foldMapBoundH f (ScopeH s) = foldMap f' s where+    f' (B a) = f a+    f' _     = mempty+{-# INLINE foldMapBoundH #-}++-- | Obtain a result by collecting information from both bound and free+-- variables+foldMapScopeH+    :: (Foldable f, Foldable m, Monoid r)+    => (b -> r) -> (a -> r)+    -> ScopeH b f m a -> r+foldMapScopeH f g (ScopeH s) = foldMap (bifoldMap f (foldMap g)) s+{-# INLINE foldMapScopeH #-}++-- | 'traverse_' the bound variables in a 'Scope'.+traverseBoundH_ :: (Applicative g, Foldable f) => (b -> g d) -> ScopeH b f m a -> g ()+traverseBoundH_ f (ScopeH s) = traverse_ f' s where+    f' (B a) = () <$ f a+    f' _     = pure ()+{-# INLINE traverseBoundH_ #-}++-- | 'traverse_' both the variables bound by this scope and any free variables.+traverseScopeH_+    :: (Applicative g, Foldable f, Foldable m)+    => (b -> g d) -> (a -> g c)+    -> ScopeH b f m a -> g ()+traverseScopeH_ f g (ScopeH s) = traverse_ (bitraverse_ f (traverse_ g)) s+{-# INLINE traverseScopeH_ #-}++-- | 'traverse' the bound variables in a 'Scope'.+traverseBoundH+    :: (Applicative g, Traversable f)+    => (b -> g c) -> ScopeH b f m a -> g (ScopeH c f m a)+traverseBoundH f (ScopeH s) = ScopeH <$> traverse f' s where+    f' (B b) = B <$> f b+    f' (F a) = pure (F a)+{-# INLINE traverseBoundH #-}++-- | 'traverse' both bound and free variables+traverseScopeH+    :: (Applicative g, Traversable f, Traversable m)+    => (b -> g d) -> (a -> g c)+    -> ScopeH b f m a -> g (ScopeH d f m c)+traverseScopeH f g (ScopeH s) = ScopeH <$> traverse (bitraverse f (traverse g)) s+{-# INLINE traverseScopeH #-}++bitraverseScopeH+    :: (Applicative g, Bitraversable f, Bitraversable m)+    => (k -> g k')+    -> (l -> g l')+    -> (a -> g a')+    -> ScopeH b (f k) (m l) a+    -> g (ScopeH b (f k') (m l') a')+bitraverseScopeH k l = bitransverseScopeH (bitraverse k) (bitraverse l)+{-# INLINE bitraverseScopeH #-}++bitransverseScopeH+    :: Applicative g+    => (forall x x'. (x -> g x') -> f x -> g (f' x'))  -- ^ 'traverse'-like for @f@+    -> (forall x x'. (x -> g x') -> m x -> g (m' x'))  -- ^ 'traverse'-like for @m@+    -> (a -> g a')+    -> ScopeH b f m a+    -> g (ScopeH b f' m' a')+bitransverseScopeH tauF tauM f = fmap ScopeH . tauF (traverse (tauM f)) . unscopeH+{-# INLINE bitransverseScopeH #-}
+ src/Bound/ScopeT.hs view
@@ -0,0 +1,316 @@+{-# LANGUAGE CPP                   #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes            #-}+#if __GLASGOW_HASKELL__ >= 805+{-# LANGUAGE QuantifiedConstraints #-}+#endif+-- For NFData instance+{-# LANGUAGE UndecidableInstances  #-}+-- | 'ScopeT' scope, which allows substitute 'f' into 't f' to get new 't f'.+--+-- Consider using 'Bound.ScopeH.ScopeH', it might be clearer.+module Bound.ScopeT (+    ScopeT (..),+    (>>>>=),+    -- * Abstraction+    abstractT, abstract1T, abstractTEither,+    -- ** Name+    abstractTName, abstract1TName,+    -- * Instantiation+    instantiateT, instantiate1T, instantiateTEither,+    -- * Traditional de Bruijn+    fromScopeT,+    toScopeT,+    -- * Bound variable manipulation+    lowerScopeT,+    splatT,+    bindingsT,+    mapBoundT,+    mapScopeT,+    foldMapBoundT,+    foldMapScopeT,+    traverseBoundT_,+    traverseScopeT_,+    traverseBoundT,+    traverseScopeT,+    bitransverseScopeT,+    ) where++import Bound                (Bound (..), Scope (..), Var (..))+import Bound.Name           (Name (..))+import Control.DeepSeq      (NFData (..))+import Control.Monad.Module (Module (..))+import Data.Bifoldable      (bifoldMap, bitraverse_)+import Data.Bifunctor       (bimap)+import Data.Bitraversable   (Bitraversable (..))+import Data.Foldable        (traverse_)+import Data.Functor.Classes+import Data.Hashable        (Hashable (..))+import Data.Hashable.Lifted (Hashable1 (..), hashWithSalt1)++-- | @'Scope' b f a@ is a @t f@ expression abstracted over @f@,+-- with bound variables in @b@, and free variables in @a@.+--+-- @+-- 'Scope' n f a ~ 'ScopeT' n 'IdentityT' f a+-- 'ScopeT' n t f a ~ t ('Scope' n f) a+-- @+--+newtype ScopeT b t f a = ScopeT { unscopeT :: t f (Var b (f a)) }++-------------------------------------------------------------------------------+-- Instances+-------------------------------------------------------------------------------++instance (Functor (t f), Functor f) => Functor (ScopeT b t f) where+   fmap f (ScopeT a) = ScopeT $ fmap (fmap (fmap f)) a++instance (Foldable (t f), Foldable f) => Foldable (ScopeT b t f) where+    foldMap f (ScopeT a) = foldMap (foldMap (foldMap f)) a+    foldr f z (ScopeT a) = foldr (flip (foldr (flip (foldr f))))  z a++instance (Traversable (t f), Traversable f) => Traversable (ScopeT b t f) where+    traverse f (ScopeT a) = ScopeT <$> traverse (traverse (traverse f)) a++-- | We cannot write @'Bound' ('ScopeT' n t)@ pre-GHC-8.6 (without an auxiliary type class).+(>>>>=) :: (Monad f, Functor (t f)) => ScopeT b t f a -> (a -> f c) -> ScopeT b t f c+ScopeT m >>>>= k = ScopeT $ fmap (fmap (>>= k)) m+{-# INLINE (>>>>=) #-}++#if __GLASGOW_HASKELL__ >= 805+-- | @(>>>=) :: ... => 'ScopeT' n t f a -> (a -> f b) -> 'ScopeT' n t f b@+instance (forall f. Functor f => Functor (t f)) => Bound (ScopeT n t) where+    (>>>=) = (>>>>=)+#endif++instance (Monad f, Functor (t f)) => Module (ScopeT b t f) f where+    (>>==) = (>>>>=)++instance (Hashable b, Bound t, Monad f, Hashable1 f, Hashable1 (t f)) => Hashable1 (ScopeT b t f) where+    liftHashWithSalt h s m = liftHashWithSalt (liftHashWithSalt h) s (fromScopeT m)+    {-# INLINE liftHashWithSalt #-}++instance (Hashable b, Bound t, Monad f, Hashable1 f, Hashable1 (t f), Hashable a) => Hashable (ScopeT b t f a) where+    hashWithSalt n m = hashWithSalt1 n (fromScopeT m)+    {-# INLINE hashWithSalt #-}++instance NFData (t f (Var b (f a))) => NFData (ScopeT b t f a) where+  rnf scope = rnf (unscopeT scope)++instance (Monad f, Bound t, Eq b, Eq1 (t f), Eq1 f, Eq a) => Eq  (ScopeT b t f a) where (==) = eq1+instance (Monad f, Bound t, Ord b, Ord1 (t f), Ord1 f, Ord a) => Ord  (ScopeT b t f a) where compare = compare1+instance (Show b, Show1 (t f), Show1 f, Show a) => Show (ScopeT b t f a) where showsPrec = showsPrec1+instance (Read b, Read1 (t f), Read1 f, Read a) => Read (ScopeT b t f a) where readsPrec = readsPrec1++-------------------------------------------------------------------------------+-- * transformers 0.5 Data.Functor.Classes+-------------------------------------------------------------------------------++instance (Monad f, Bound t, Eq b, Eq1 (t f), Eq1 f) => Eq1 (ScopeT b t f) where+  liftEq f m n = liftEq (liftEq f) (fromScopeT m) (fromScopeT n)++instance (Monad f, Bound t, Ord b, Ord1 (t f), Ord1 f) => Ord1 (ScopeT b t f) where+  liftCompare f m n = liftCompare (liftCompare f) (fromScopeT m) (fromScopeT n)++instance (Show b, Show1 (t f), Show1 f) => Show1 (ScopeT b t f) where+    liftShowsPrec sp sl d (ScopeT x) = showsUnaryWith+        (liftShowsPrec (liftShowsPrec sp' sl') (liftShowList sp' sl'))+        "ScopeT" d x+      where+        sp' = liftShowsPrec sp sl+        sl' = liftShowList sp sl++instance (Read b, Read1 (t f), Read1 f) => Read1 (ScopeT b t f) where+    liftReadsPrec f g = readsData $ readsUnaryWith+        (liftReadsPrec (liftReadsPrec f' g') (liftReadList f' g'))+        "ScopeT" ScopeT+      where+        f' = liftReadsPrec f g+        g' = liftReadList f g++-------------------------------------------------------------------------------+-- Abstraction+-------------------------------------------------------------------------------++-- | Capture some free variables in an expression to yield a 'ScopeT' with bound variables in @b@.+abstractT :: (Functor (t f), Monad f) => (a -> Maybe b) -> t f a -> ScopeT b t f a+abstractT f e = ScopeT (fmap k e) where+    k y = case f y of+        Just z  -> B z+        Nothing -> F (return y)+{-# INLINE abstractT #-}++-- | Abstract over a single variable.+--+-- >>> abstract1T 'x' (MaybeT (Nothing : map Just "xyz"))+-- ScopeT (MaybeT [Nothing,Just (B ()),Just (F "y"),Just (F "z")])+abstract1T :: (Functor (t f), Monad f, Eq a) => a -> t f a -> ScopeT () t f a+abstract1T a = abstractT (\b -> if a == b then Just () else Nothing)+{-# INLINE abstract1T #-}++-- | Capture some free variables in an expression to yield a 'ScopeT' with bound variables in @b@. Optionally change the types of the remaining free variables.+abstractTEither :: (Functor (t f),  Monad f) => (a -> Either b c) -> t f a -> ScopeT b t f c+abstractTEither f e = ScopeT (fmap k e) where+    k y = case f y of+        Left z -> B z+        Right y' -> F (return y')+{-# INLINE abstractTEither #-}++-------------------------------------------------------------------------------+-- Abstraction with Name+-------------------------------------------------------------------------------++-- | Abstraction, capturing named bound variables.+abstractTName :: (Functor (t f), Monad f) => (a -> Maybe b) -> t f a -> ScopeT (Name a b) t f a+abstractTName f t = ScopeT (fmap k t) where+    k a = case f a of+        Just b  -> B (Name a b)+        Nothing -> F (return a)+{-# INLINE abstractTName #-}++-- | Abstract over a single variable+abstract1TName :: (Functor (t f), Monad f, Eq a) => a -> t f a -> ScopeT (Name a ()) t f a+abstract1TName a = abstractTName (\b -> if a == b then Just () else Nothing)+{-# INLINE abstract1TName #-}++-------------------------------------------------------------------------------+-- Instantiation+-------------------------------------------------------------------------------++-- | Enter a 'ScopeT', instantiating all bound variables+instantiateT :: (Bound t, Monad f) => (b -> f a) -> ScopeT b t f a -> t f a+instantiateT k (ScopeT e) = e >>>= \v -> case v of+    B b -> k b+    F a -> a+{-# INLINE instantiateT #-}++-- | Enter a 'ScopeT' that binds one variable, instantiating it+instantiate1T :: (Bound t, Monad f) => f a -> ScopeT b t f a -> t f a+instantiate1T e = instantiateT (const e)+{-# INLINE instantiate1T #-}++-- | Enter a 'ScopeT', and instantiate all bound and free variables in one go.+instantiateTEither :: (Bound t, Monad f) => (Either b a -> f c) -> ScopeT b t f a -> t f c+instantiateTEither f (ScopeT e) = e >>>= \v -> case v of+    B b -> f (Left b)+    F ea -> ea >>= f . Right+{-# INLINE instantiateTEither #-}++-------------------------------------------------------------------------------+-- Traditional de Bruijn+-------------------------------------------------------------------------------++-- | Convert to traditional de Bruijn.+fromScopeT :: (Bound t, Monad f) => ScopeT b t f a -> t f (Var b a)+fromScopeT (ScopeT s) = s >>>= \v -> case v of+    F e -> fmap F e+    B b -> return (B b)++-- | Convert from traditional de Bruijn to generalized de Bruijn indices.+toScopeT :: (Functor (t f), Monad f) => t f (Var b a) -> ScopeT b t f a+toScopeT e = ScopeT (fmap (fmap return) e)++-- | Convert to 'Scope'.+lowerScopeT+    :: (Functor (t f), Functor f)+    => (forall x. t f x -> g x)+    -> (forall x. f x -> g x)+    -> ScopeT b t f a -> Scope b g a+lowerScopeT tf f (ScopeT x) = Scope (tf (fmap (fmap f) x))++-------------------------------------------------------------------------------+-- Extras+-------------------------------------------------------------------------------++-- | Perform substitution on both bound and free variables in a 'ScopeT'.+splatT :: (Bound t, Monad f) => (a -> f c) -> (b -> f c) -> ScopeT b t f a -> t f c+splatT f unbind (ScopeT e) = e >>>= \v -> case v of+    B b -> unbind b+    F ea -> ea >>= f+{-# INLINE splatT #-}++-- | Return a list of occurences of the variables bound by this 'ScopeT'.+bindingsT :: Foldable (t f) => ScopeT b t f a -> [b]+bindingsT (ScopeT s) = foldr f [] s where+    f (B v) vs = v : vs+    f _ vs     = vs+{-# INLINE bindingsT #-}++-- | Perform a change of variables on bound variables.+mapBoundT :: Functor (t f) => (b -> b') -> ScopeT b t f a -> ScopeT b' t f a+mapBoundT f (ScopeT s) = ScopeT (fmap f' s) where+    f' (B b) = B (f b)+    f' (F a) = F a+{-# INLINE mapBoundT #-}++-- | Perform a change of variables, reassigning both bound and free variables.+mapScopeT+    :: (Functor (t f), Functor f)+    => (b -> d) -> (a -> c)+    -> ScopeT b t f a -> ScopeT d t f c+mapScopeT f g (ScopeT s) = ScopeT $ fmap (bimap f (fmap g)) s+{-# INLINE mapScopeT #-}++-- | Obtain a result by collecting information from bound variables+foldMapBoundT :: (Foldable (t f), Monoid r) => (b -> r) -> ScopeT b t f a -> r+foldMapBoundT f (ScopeT s) = foldMap f' s where+    f' (B a) = f a+    f' _     = mempty+{-# INLINE foldMapBoundT #-}++-- | Obtain a result by collecting information from both bound and free+-- variables+foldMapScopeT+    :: (Foldable f, Foldable (t f), Monoid r)+    => (b -> r) -> (a -> r)+    -> ScopeT b t f a -> r+foldMapScopeT f g (ScopeT s) = foldMap (bifoldMap f (foldMap g)) s+{-# INLINE foldMapScopeT #-}++-- | 'traverse_' the bound variables in a 'Scope'.+traverseBoundT_ :: (Applicative g, Foldable (t f)) => (b -> g d) -> ScopeT b t f a -> g ()+traverseBoundT_ f (ScopeT s) = traverse_ f' s where+    f' (B a) = () <$ f a+    f' _     = pure ()+{-# INLINE traverseBoundT_ #-}++-- | 'traverse_' both the variables bound by this scope and any free variables.+traverseScopeT_+    :: (Applicative g, Foldable f, Foldable (t f))+    => (b -> g d) -> (a -> g c)+    -> ScopeT b t f a -> g ()+traverseScopeT_ f g (ScopeT s) = traverse_ (bitraverse_ f (traverse_ g)) s+{-# INLINE traverseScopeT_ #-}++-- | 'traverse' the bound variables in a 'Scope'.+traverseBoundT+    :: (Applicative g, Traversable (t f))+    => (b -> g c) -> ScopeT b t f a -> g (ScopeT c t f a)+traverseBoundT f (ScopeT s) = ScopeT <$> traverse f' s where+    f' (B b) = B <$> f b+    f' (F a) = pure (F a)+{-# INLINE traverseBoundT #-}++-- | 'traverse' both bound and free variables+traverseScopeT+    :: (Applicative g, Traversable f, Traversable (t f))+    => (b -> g d) -> (a -> g c)+    -> ScopeT b t f a -> g (ScopeT d t f c)+traverseScopeT f g (ScopeT s) = ScopeT <$> traverse (bitraverse f (traverse g)) s+{-# INLINE traverseScopeT #-}++-- | If you are looking for 'bitraverseScopeT', this is the monster you need.+bitransverseScopeT+    :: Applicative f+    => (forall x x'. (x -> f x') -> t s x -> f (t' s' x'))  -- ^ 'traverse'-like for @t@+    -> (forall x x'. (x -> f x') -> s x -> f (s' x'))       -- ^ 'traverse'-like for @s@+    -> (a -> f a')+    -> ScopeT b t s a+    -> f (ScopeT b t' s' a')+bitransverseScopeT tauT tauS f = fmap ScopeT . tauT (traverse (tauS f)) . unscopeT+{-# INLINE bitransverseScopeT #-}++-- $setup+-- >>> import Control.Monad.Trans.Maybe
+ src/Control/Monad/Module.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+-- | Right monad 'Module' type-class.+--+-- Most possible instances are omitted.+-- The primary use-case for 'Module' is to power 'Bound.ScopeH.ScopeH'.+module Control.Monad.Module where++import Bound                     (Scope (..), (>>>=))+import Control.Monad.Trans.Class (MonadTrans (..))+import Data.Functor.Compose      (Compose (..))+import Data.Functor.Identity     (Identity (..))++-- | @f@ is right @m@-module. (according to https://ncatlab.org/nlab/show/module+over+a+monad#modules definitions).+-- We have @'Compose' f m ~> f@ natural transformation.+--+-- === Laws+--+-- @+-- fma '>>==' return    = fma+-- fma '>>==' (f 'Control.Monad.>=>' g) = (fma '>>==' f) '>>==' g+-- @+--+-- === Properties+--+-- For all @'Monad' m@ we can write associated @instance 'Module' m m where ('>>==') = ('>>=')@.+--+-- 'mjoin' and '>>==' are equivalent in power:+--+-- @+-- fa '>>==' amb = 'mjoin' ('fmap' amb fa)+-- @+class (Functor f, Monad m) => Module f m where++    -- | Called 'action'.+    (>>==) :: f a -> (a -> m b) -> f b++infixl 1 >>==++-- | 'Module''s 'join' variant.+mjoin :: Module f m => f (m a) -> f a+mjoin fma = fma >>== id++-- | @'Module' m (t m)@ action's implementation.+transAction :: (MonadTrans t, Monad m, Monad (t m)) => t m a -> (a -> m b) -> t m b+transAction tma amb = tma >>= lift . amb++-- | @'Module' m ('Compose' f m)@ action's implementation.+composeAction :: (Functor f, Monad m) => Compose f m a -> (a -> m b) -> Compose f m b+composeAction (Compose fma) amb = Compose (fmap (>>= amb) fma)++instance Monad m => Module m Identity where+    fa >>== k = fmap (runIdentity . k) fa++instance Monad m => Module (Scope b m) m where+    (>>==) = (>>>=)