参数化的lambda术语是Monad吗?

时间:2016-03-10 20:44:08

标签: haskell lambda monads

我在lambda演算中对这个术语的表达形式进行了参数化:

{-# LANGUAGE DeriveFunctor #-}

data Lambda a = Var a | App (Lambda a) (Lambda a) | Lam a (Lambda a) 
    deriving Functor

我想知道Lambda是否可以成为monad的实例?我认为以下内容可能适用于join

的实现
joinT :: Lambda (Lambda a) -> Lambda a
joinT (Var a) = a
joinT (fun `App` arg) = joinT fun `App` joinT arg
joinT (Lam n body) = ?

对于第三种情况,我完全没有线索......但它应该是可能的 - 这个无名的lambda术语表示来自De Bruijn Notation as a Nested Datatype,是Monad的一个实例(Maybe用于区分此表示中的绑定变量和自由变量):

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE DeriveFunctor #-}

data Expr a 
    = V a
    | A (Expr a) (Expr a)
    | L (Expr (Maybe a))
    deriving (Show, Eq, Functor)

gfoldT :: forall m n b.
    (forall a. m a -> n a) ->
    (forall a. n a -> n a -> n a) ->
    (forall a. n (Maybe a) -> n a) ->
    (forall a. (Maybe (m a)) ->  m (Maybe a)) ->
    Expr (m b) -> n b
gfoldT v _ _ _ (V x) = v x
gfoldT v a l t (fun `A` arg) = a (gfoldT v a l t fun) (gfoldT v a l t arg)
gfoldT v a l t (L body) = l (gfoldT v a l t (fmap t body))

joinT :: Expr (Expr a) -> Expr a
joinT = gfoldT id (:@) Lam distT

distT :: Maybe (Expr a) -> Expr (Maybe a)
distT Nothing = Var Nothing
distT (Just x) = fmap Just x

joinT足以满足instance Monad Expr

instance Applicative Expr where
    pure = Var
    ef <*> ea = do
        f <- ef
        a <- ea
        return $ f a

instance Monad Expr where
    return = Var
    t >>= f = (joinT . fmap f) t

进一步假设表示之间有以下两个转换函数:
unname :: Lamba a -> Expr aname :: Expr a -> Lambda a。有了这些,我们可以通过利用两个类型构造函数之间的同构来为Lambda实现join

joinL :: Lambda (Lambda a) -> Lambda a
joinL = name . joinT . uname . fmap uname

但这似乎很复杂。有没有更简单的方法,还是我错过了一些重要的东西?

编辑:以下是我认为可以完成工作的nameuname函数。正如评论和答案中所指出的那样,a确实需要一个Eq约束来打破同构。

foldT :: forall n b.
    (forall a. a -> n a) ->
    (forall a. n a -> n a -> n a) ->
    (forall a. n (Maybe a) -> n a) ->
    Expr b -> n b
foldT v _ _ (V x) = v x
foldT v a l (A fun arg) = a (foldT v a l fun) (foldT v a l arg)
foldT v a l (L body) = l (foldT v a l body)

abstract :: Eq a => a -> Expr a -> Expr a
abstract x = L . fmap (match x)

match :: Eq a => a -> a -> Maybe a
match x y = if x == y then Nothing else Just y

apply :: Expr a -> Expr (Maybe a) -> Expr a
apply t = joinT . fmap (subst t . fmap V)

uname :: Eq a => Lambda a -> Expr a
uname = foldL V A abstract

name :: Eq a => Expr a -> Lambda a
name e = nm [] e where
    nm vars (V n) = Var n
    nm vars (A fun arg) = nm vars fun `App` nm vars arg
    nm vars (L body) =
        Lam fresh $ nm (fresh:vars) (apply (V fresh) body) where
        fresh = head (names \\ vars)

names :: [String]
names = [ [i] | i <- ['a'..'z']] ++ [i : show j | j <- [1..], i <- ['a'..'z'] ]

1 个答案:

答案 0 :(得分:12)

你的直觉是正确的:在绑定网站上具有显式名称的术语不构成monad。

>>=的签名提供了一些值得思考的东西:

(>>=) :: Lambda a -> (a -> Lambda b) -> Lambda b

绑定lambda术语执行替换。您绑定的函数是环境映射名称a到术语Lambda b; >>=找到名称a的所有出现,并将每个名称交换为其引用的环境中的值。 (将a -> Lambda b与更传统的环境类型[(a, Lambda b)]进行比较。

但在绑定网站上替换是没有意义的。 lambda术语的参数在语法上可以是lambda。 (\(\x -> y) -> y在语法上没有效力。)在a构造函数中加Lam意味着Lambda无法成为monad。

您违反的特定法律是正确的身份,其中列出x >>= return = x所有x。 (要查看违规行为,请尝试将x设置为Lam字词。)

从另一个角度来看,请考虑如何实施>>=在Paterson和Bird的论文中提供的捕获避免替换。当您不使用de Bruijn指数时,捕获避免替换是棘手的:您需要新名称的来源以及识别重合名称的能力(以确定何时需要使用新名称)。这种函数的类型如下所示:

subst :: (MonadFresh a m, Eq a) => Lambda a -> (a -> Lambda a) -> m (Lambda a)

类约束和monadic上下文使得这个签名与>>=的签名非常不同!如果您确实尝试实施nameunname,您会发现您假设的类型不正确,joinL需要这些类。

Bird和Paterson对lambda术语的表示是一个monad,因为它是本地无名。他们的a构造函数中没有L;相反,只要变量的值很长,就可以通过缩小找到变量的绑定站点。正如论文所解释的,这是表示de Bruijn指数的一种方式(将Just (Just Nothing)与自然数S (S Z)进行比较)。

有关更多内容,请参阅Kmett的detailed article描述他的bound图书馆的设计,该图书馆使用Bird和Paterson的方法作为灵感来源。