Haskell 非类型lambda演算冒险

Haskell 非类型lambda演算冒险,haskell,types,lambda-calculus,Haskell,Types,Lambda Calculus,我们偶尔会有人询问如何在Haskell中实现非类型lambda演算。[当然,我现在找不到这些问题中的任何一个,但我确信我已经看到了它们!]只是为了咯咯地笑,我想我应该花一些时间来玩这个 做这样的事情已经够琐碎的了 i = \ x -> x k = \ x y -> x s = \ f g x -> (f x) (g x) s i i decode :: Term Int -> Int decode ti = out $ ti ! R (\ tx -> 1 + o

我们偶尔会有人询问如何在Haskell中实现非类型lambda演算。[当然,我现在找不到这些问题中的任何一个,但我确信我已经看到了它们!]只是为了咯咯地笑,我想我应该花一些时间来玩这个

做这样的事情已经够琐碎的了

i = \ x -> x
k = \ x y -> x
s = \ f g x -> (f x) (g x)
s i i
decode :: Term Int -> Int
decode ti = out $ ti ! R (\ tx -> 1 + out tx) ! R (\ tx -> 0)
这很好用。然而,一旦你尝试做类似的事情

i = \ x -> x
k = \ x y -> x
s = \ f g x -> (f x) (g x)
s i i
decode :: Term Int -> Int
decode ti = out $ ti ! R (\ tx -> 1 + out tx) ! R (\ tx -> 0)
类型检查器正确地抱怨无限类型。基本上,非类型lambda演算中的所有东西都是一个函数,这意味着所有函数都有无穷多。但是Haskell只允许有限算术的函数。(因为,真的,你为什么想要无穷多?)

事实证明,我们可以很容易地避开这个限制:

data Term = T (Term -> Term)

T f ! x = f x

i = T $ \ x -> x
k = T $ \ x -> T $ \ y -> x
s = T $ \ f -> T $ \ g -> T $ \ x -> (f ! x) ! (g ! x)
这可以完美地工作,并允许构造和执行任意lambda表达式。例如,我们可以轻松构建一个函数,将
Int
转换为教堂数字:

zero = k ! i
succ = s ! (s ! (k ! s) ! k)

encode 0 = zero
encode n = succ ! (encode $ n-1)
同样,这是完美的

现在编写一个解码函数

…是的,祝你好运!问题是,我们可以创建任意的lambda术语,但我们不能以任何方式检查它们。所以我们需要添加一些方法来实现这一点


到目前为止,我想到的最好的主意是:

data Term x = F (Term x -> Term x) | R (Term x -> x)

F f ! x =            f x
R f ! x = R $ \ _ -> f x

out :: Term x -> x
out (R f) = f (error "mu")
out (F _) =   (error "nu")

i = F $ \ x -> x
k = F $ \ x -> F $ \ y -> x
s = F $ \ f -> F $ \ g -> F $ \ x -> (f ! x) ! (g ! x)
我现在可以做类似的事情了

i = \ x -> x
k = \ x y -> x
s = \ f g x -> (f x) (g x)
s i i
decode :: Term Int -> Int
decode ti = out $ ti ! R (\ tx -> 1 + out tx) ! R (\ tx -> 0)
这对教堂的布隆和教堂的数字非常有效


当我开始尝试做任何高阶的事情时,事情开始变得非常糟糕。为了实现非类型化的lambda演算,我扔掉了所有类型信息,现在正努力说服类型检查器让我做我想做的事情

这项工作:

something = F $ \ x -> F $ \ n -> F $ \ s -> s ! x
nothing   =            F $ \ n -> F $ \ s -> n

encode :: Maybe x -> Term x
encode (Nothing) = nothing
encode (Just  x) = something ! x
这并不是:

decode :: Term x -> Maybe (Term x)
decode tmx = out $ tmx ! R (\ tx -> Nothing) ! R (\ tx -> Just tx)
我试过十几种轻微的变化;他们没有一个进行过打字检查。并不是我不明白它为什么会失败,而是我想不出任何成功的方法。(最特别的是,
R只是
显然输入错误。)

这几乎就像我想要一个函数
用于所有xy。术语x->术语y
。因为,对于纯非类型术语,这应该总是可能的。只有那些涉及
R
的术语是行不通的。但我不知道如何在Haskell类型的系统中表达它

(例如,尝试将
F
的类型更改为
用于所有x.Term x->Term x
。现在
k
的定义类型不正确,因为内部
F$\y->x
实际上不能返回任何类型,而只能返回
x
的[Now fixed]类型)

有比我聪明的人有更好的主意吗?

好的,我找到了一个解决方案:

上面的代码有
项x
,由
R
的结果类型参数化。与其这样做(并破坏类型检查器),不如构造一些类型
Value
,它可以表示您想要返回的每个结果类型。现在我们有了

data Term = F (Term -> Term) | R (Term -> Value)
将所有可能的结果类型折叠成一个不透明的
类型后,我们就可以开始工作了

具体来说,我选择的类型是

data Value = V Int [Term]
因此,
Value
是一个表示ADT值构造函数的
Int
,然后是该构造函数的每个字段的一个
术语。有了这个定义,我们终于可以做到了

decode :: Term -> Maybe Term
decode tmx =
  case tmx ! R (\ _ -> V 0 []) ! R (\ tx -> V 1 [tx]) of
    V 0 []   -> Nothing
    V 1 [tx] -> Just tx
    _        -> error "not a Maybe"
类似地,您可以对列表进行编码和解码,如下所示:

null =                        F $ \ n -> F $ \ c -> n
cons = F $ \ x -> n $ \ xs -> F $ \ n -> F $ \ c -> c ! x ! xs

encode :: [Term] -> Term
encode (  []) = null
encode (x:xs) = cons ! x ! encode xs

decode :: Term -> [Term]
decode txs =
  case out $ txs ! R (\ txs -> V 0 []) ! F (\ tx -> R $ \ txs -> V 1 [tx, txs]) of
    V 0 []        -> []
    V 1 [tx, txs] -> tx : decode txs
    _             -> error "not a list"

当然,您必须猜测需要应用哪个解码函数。但这就是你的非类型lambda演算

这不是一个答案,但评论限制太大了

R Just
是类型错误的,因为它的类型是递归的,但我们总是可以将此类型级别的递归封装在数据类型中:

data Fix2 g f = Fix2 { run :: g (f (Fix2 g f)) }
Fix2
可以用
Fix
和类型构造函数的组合来表示,但我不想让事情复杂化

然后我们可以将
解码
定义为

decode :: Term (Fix2 Maybe Term) -> Maybe (Term (Fix2 Maybe Term))
decode tmx = run $ out $ tmx ! R (Fix2 . const Nothing) ! R (Fix2 . Just)
一些测试:

isSomething :: Term (Fix2 Maybe Term) -> Bool
isSomething = isJust . decode

i = F id

main = do
    print $ isSomething (something ! i) -- True
    print $ isSomething  nothing        -- False

但显然
Term(Fix2可能是Term)
远不是
Term a

我认为
data Term=Lam(Term->Term)| App Term Term | Var Int
将是一个更简单的解决方案。@András Kovács,你的意思是
Lam Term
?@user3237465不,我的意思是
Term->Term
。我们可以做如上所述的事情,但我想是这样的。@András Kovács,啊,现在看。这非常类似于中的
quote
(其他作者称此函数为
readback
)。