将 Bound 与多个类型变量一起用于抽象
Using Bound with multiple type variables for abstractions
我一直在试用 bound 包 - 您可以尝试使用的一个玩具示例是 System F。与包文档中的示例不同,包文档中的示例具有一个类型参数,用于变量被绑定lambda,System F 将有两个类型参数,一个用于普通变量(由普通 lambda 抽象绑定),一个用于类型变量(由类型抽象绑定)。
我不太明白如何使用这个包,但是看了示例,我的印象是我应该从为表达式类型编写一个 Monad
实例开始。但是,我 运行 遇到了麻烦,因为我无法想出可以进行类型检查并且也是 "obviously correct" 的东西(即通过检查看起来直觉上是正确的)。到目前为止我有
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE LambdaCase #-}
module SystemF where
import Bound
import Control.Monad
import Data.Bifunctor
-- e ::= x | λx : τ. e | e1 e2 | ΛX. e | e [τ]
-- t denotes type variables, e denotes expression variables
data Exp t e
= Var e
| Lam (Scope () (Exp t) e)
| App (Exp t e) (Exp t e)
| TyLam (Scope () (FlipExp e) t) -- Is this correct?
| TyApp (Exp t e) (Type t)
newtype FlipExp e t = FlipExp { getExp :: Exp t e }
instance Functor (Exp t) where
fmap = second
instance Bifunctor Exp where
bimap f g = \case
Var e -> Var (g e)
Lam s -> Lam (bimapInScope f g s)
App e1 e2 -> App (bimap f g e1) (bimap f g e2)
TyLam s' -> TyLam (bimapInScope g f s')
TyApp e t -> TyApp (bimap f g e) (fmap f t)
where
bimapInScope f g = Scope . bimap f (second (bimap f g)) . unscope
instance Applicative (Exp t) where
pure = Var
(<*>) = ap
instance Monad (Exp t) where
x >>= f = case x of
Var v -> f v
Lam s -> Lam (s >>>= f)
App e1 e2 -> App (e1 >>= f) (e2 >>= f)
TyLam s ->
-- tmp :: Exp (Var () (Exp t a) a
-- but we need Exp (Var () (Exp t b)) b
-- just applying >>= inside the first argument
-- is insufficient as the outer 'a' won't change
let tmp = first (second getExp) $ getExp (unscope s)
in error "How do I implement this?"
TyApp e t -> TyApp (e >>= f) t
instance Functor (FlipExp e) where
fmap = second
instance Bifunctor FlipExp where
bimap f g = FlipExp . bimap g f . getExp
-- τ ::= X | τ -> τ | ∀ X . τ
data Type t
= TVar t
| TFun (Type t) (Type t)
| TForall (Scope () Type t)
deriving (Functor, Foldable, Traversable)
instance Applicative Type where
pure = TVar
(<*>) = ap
instance Monad Type where
x >>= f = case x of
TVar v -> f v
TFun t1 t2 -> TFun (t1 >>= f) (t2 >>= f)
TForall s -> TForall (s >>>= f)
- 是否可以为
Exp t
提供一个 monad 实例?如果是,如何?
- Monad 实例背后的直觉是什么?对于 State/Maybe monad,我发现将它们视为链接计算(在绑定方面)很有用,而对于像列表这样的结构,我发现将它们视为扁平化(在术语中)很有用加入)。但是,我无法对 Exp 的 Monad 实例提出任何正确的直觉。 bind 是否精确地进行了避免捕获的替换?我通读了这个 post 但读完普通的 "De Bruijn indices" 部分就迷路了。
查看讨论 here and @phadej's bound-extras
package here。
要点是类型抽象是术语级别的东西(因此是 Expr
的变体),需要对 Type
进行抽象。普通的 Scope b f a
不适合处理这个问题,因为它的扩展 f (Either b (f a))
已经为这两种情况修复了 f
。您希望外部 f
是 Expr
,而内部应该是 Type
。这导致 Scope
的以下概括:
newtype ScopeH b f g a = ScopeH (g (Either b (f a)))
newtype ScopeT b t f a = ScopeT (t f (Either b (f a)))
newtype Expr' a b = Expr' (Expr b a)
data Expr b a
= V a
...
| TyApp (Expr b a) (Ty b)
| Forall (ScopeH () (Expr' a) Ty b)
...
Expr' a
修复了术语变量的 de Bruijn 索引,以便 ScopeH
构造函数可以引入一个额外的类型变量以放入 b
孔中。
我一直在试用 bound 包 - 您可以尝试使用的一个玩具示例是 System F。与包文档中的示例不同,包文档中的示例具有一个类型参数,用于变量被绑定lambda,System F 将有两个类型参数,一个用于普通变量(由普通 lambda 抽象绑定),一个用于类型变量(由类型抽象绑定)。
我不太明白如何使用这个包,但是看了示例,我的印象是我应该从为表达式类型编写一个 Monad
实例开始。但是,我 运行 遇到了麻烦,因为我无法想出可以进行类型检查并且也是 "obviously correct" 的东西(即通过检查看起来直觉上是正确的)。到目前为止我有
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE LambdaCase #-}
module SystemF where
import Bound
import Control.Monad
import Data.Bifunctor
-- e ::= x | λx : τ. e | e1 e2 | ΛX. e | e [τ]
-- t denotes type variables, e denotes expression variables
data Exp t e
= Var e
| Lam (Scope () (Exp t) e)
| App (Exp t e) (Exp t e)
| TyLam (Scope () (FlipExp e) t) -- Is this correct?
| TyApp (Exp t e) (Type t)
newtype FlipExp e t = FlipExp { getExp :: Exp t e }
instance Functor (Exp t) where
fmap = second
instance Bifunctor Exp where
bimap f g = \case
Var e -> Var (g e)
Lam s -> Lam (bimapInScope f g s)
App e1 e2 -> App (bimap f g e1) (bimap f g e2)
TyLam s' -> TyLam (bimapInScope g f s')
TyApp e t -> TyApp (bimap f g e) (fmap f t)
where
bimapInScope f g = Scope . bimap f (second (bimap f g)) . unscope
instance Applicative (Exp t) where
pure = Var
(<*>) = ap
instance Monad (Exp t) where
x >>= f = case x of
Var v -> f v
Lam s -> Lam (s >>>= f)
App e1 e2 -> App (e1 >>= f) (e2 >>= f)
TyLam s ->
-- tmp :: Exp (Var () (Exp t a) a
-- but we need Exp (Var () (Exp t b)) b
-- just applying >>= inside the first argument
-- is insufficient as the outer 'a' won't change
let tmp = first (second getExp) $ getExp (unscope s)
in error "How do I implement this?"
TyApp e t -> TyApp (e >>= f) t
instance Functor (FlipExp e) where
fmap = second
instance Bifunctor FlipExp where
bimap f g = FlipExp . bimap g f . getExp
-- τ ::= X | τ -> τ | ∀ X . τ
data Type t
= TVar t
| TFun (Type t) (Type t)
| TForall (Scope () Type t)
deriving (Functor, Foldable, Traversable)
instance Applicative Type where
pure = TVar
(<*>) = ap
instance Monad Type where
x >>= f = case x of
TVar v -> f v
TFun t1 t2 -> TFun (t1 >>= f) (t2 >>= f)
TForall s -> TForall (s >>>= f)
- 是否可以为
Exp t
提供一个 monad 实例?如果是,如何? - Monad 实例背后的直觉是什么?对于 State/Maybe monad,我发现将它们视为链接计算(在绑定方面)很有用,而对于像列表这样的结构,我发现将它们视为扁平化(在术语中)很有用加入)。但是,我无法对 Exp 的 Monad 实例提出任何正确的直觉。 bind 是否精确地进行了避免捕获的替换?我通读了这个 post 但读完普通的 "De Bruijn indices" 部分就迷路了。
查看讨论 here and @phadej's bound-extras
package here。
要点是类型抽象是术语级别的东西(因此是 Expr
的变体),需要对 Type
进行抽象。普通的 Scope b f a
不适合处理这个问题,因为它的扩展 f (Either b (f a))
已经为这两种情况修复了 f
。您希望外部 f
是 Expr
,而内部应该是 Type
。这导致 Scope
的以下概括:
newtype ScopeH b f g a = ScopeH (g (Either b (f a)))
newtype ScopeT b t f a = ScopeT (t f (Either b (f a)))
newtype Expr' a b = Expr' (Expr b a)
data Expr b a
= V a
...
| TyApp (Expr b a) (Ty b)
| Forall (ScopeH () (Expr' a) Ty b)
...
Expr' a
修复了术语变量的 de Bruijn 索引,以便 ScopeH
构造函数可以引入一个额外的类型变量以放入 b
孔中。