我有一个用lambda演算表示的术语,参数化于名称类型上:
假设有以下两个表示之间的转换函数:
但这似乎非常复杂。是否有更简单的方法,或者我遗漏了什么重要的东西?
{-# LANGUAGE DeriveFunctor #-}
data Lambda a = Var a | App (Lambda a) (Lambda a) | Lam a (Lambda a)
deriving Functor
我在想Lambda
是否可以成为单子的实例?我认为以下内容可能适用于join
的实现:
joinT :: Lambda (Lambda a) -> Lambda a
joinT (Var a) = a
joinT (fun `App` arg) = joinT fun `App` joinT arg
joinT (Lam n body) = ?
对于第三种情况,我完全不知道... 但是应该是可能的 - 这种无名的λ项表示法来自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 a
和 name :: Expr a -> Lambda a
。通过利用这两种类型构造函数之间的同构,我们可以实现Lambda的join
。joinL :: Lambda (Lambda a) -> Lambda a
joinL = name . joinT . uname . fmap uname
但这似乎非常复杂。是否有更简单的方法,或者我遗漏了什么重要的东西?
编辑:这里是我认为会完成工作的name
和uname
函数。正如评论和答案所指出的那样,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'] ]
name
和unname
是如何工作的。你如何将例如Lam (\x -> x+1) (Var (\y -> 2*y)) :: Lambda (Int->Int)
转换成Expr (Int->Int)
?这些类型真的是同构的吗?这种转换是否需要函数的Eq
(我只是猜测...)? - chia
需要一个Eq
约束。这会破坏同构吗? - julesconstraints
和Monads
搭配得不好。例如,Set
应该是一个monad
,但涉及的Ord
约束阻止了这种情况。另外,Lam fresh ...
看起来像是一个Lambda String
值,而不是一个Lambda a
。它真的通过类型检查吗? - chi