使用Bound与多个类型变量进行抽象

时间:2018-12-09 04:37:45

标签: haskell monads

我一直在尝试bound软件包-一个可以尝试使用的玩具示例是SystemF。与软件包文档中的示例不同,该示例中有一个类型参数用于由lambda绑定的变量,系统F将具有两个类型参数,一个用于普通变量(由普通lambda抽象绑定),一个用于类型变量(由类型抽象绑定)。

我不太了解如何使用该软件包,但是看一下示例,我得到的印象是,我应该从为表达式类型编写一个Monad实例开始。但是,我遇到了麻烦,因为我无法提出可以进行类型检查并且也“显然正确”的东西(即通过检查似乎直观上正确)。到目前为止,我有

{-# 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)
  1. 是否可以为Exp t使用monad实例?如果是,怎么办?
  2. Monad实例背后的直觉是什么?对于State /也许是Monad,我发现将它们视为链接计算(就绑定而言)很有用,而对于List之类的结构,我发现就展平(就联接而言)很有用)。但是,我无法为Exp的Monad实例提出任何适当的直觉。绑定是否精确执行避免捕获的替换?我通读了post,但在普通的“ De Bruijn索引”部分迷路了。

1 个答案:

答案 0 :(得分:1)

请参阅讨论here和@phadej的bound-extras package here

要点是类型抽象是术语级的东西(因此是Expr的变体),需要对Type上的东西进行抽象。平原Scope b f a不适合处理此问题,因为它的扩展f (Either b (f a))已针对两种情况固定了f。您希望外部fExpr,而内部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修复了术语vars的de Bruijn索引,因此ScopeH构造函数可以引入一个附加类型var放入b孔中。