【问题标题】:Implementing Iota in Haskell在 Haskell 中实现 Iota
【发布时间】:2012-08-16 03:40:31
【问题描述】:

Iota 是一种非常小的“编程语言”,只使用一个组合子。我有兴趣了解它的工作原理,但以我熟悉的语言查看实现会很有帮助。

我找到了一个用 Scheme 编写的 Iota 编程语言的实现。不过,我在将其翻译为 Haskell 时遇到了一些麻烦。这很简单,但我对 Haskell 和 Scheme 都比较陌生。

您将如何在 Haskell 中编写等效的 Iota 实现?

(let iota ()
  (if (eq? #\* (read-char)) ((iota)(iota))
      (lambda (c) ((c (lambda (x) (lambda (y) (lambda (z) ((x z)(y z))))))
           (lambda (x) (lambda (y) x))))))

【问题讨论】:

  • Haskell 中没有等效的实现。这样的实现不会进行类型检查。当然,可以使用不同的策略编写实现。
  • 是的,我知道它不会输入检查。我想我被绊倒的部分是理解 ((iota)(iota)) 在这个实现中做了什么。

标签: haskell scheme implementation iota


【解决方案1】:

我一直在自学这些东西,所以我当然希望我能做对……

作为 n.m.提到,Haskell 是打字的事实对这个问题非常重要;类型系统限制了可以形成的表达式,特别是 lambda 演算的最基本类型系统禁止自我应用,这最终为您提供了一种非图灵完备的语言。图灵完备性被添加在基本类型系统的顶部作为语言的额外功能(fix :: (a -> a) -> a 运算符或递归类型)。

这并不意味着你不能在 Haskell 中实现它,而是这样的实现不会只有一个运算符。

方法#1:实现second example one-point combinatory logic basis from here,并添加fix函数:

iota' :: ((t1 -> t2 -> t1)
          -> ((t5 -> t4 -> t3) -> (t5 -> t4) -> t5 -> t3)
          -> (t6 -> t7 -> t6)
          -> t)
         -> t
iota' x = x k s k 
    where k x y = x
          s x y z = x z (y z)

fix :: (a -> a) -> a
fix f = let result = f result in result

现在你可以用iota'fix 编写任何程序。解释这是如何工作的有点复杂。 (编辑:请注意,这个iota' 与原始问题中的λx.x S K 不同;它是λx.x K S K,这也是图灵完备的。iota' 就是这种情况程序将不同于 iota 程序。我已经在 Haskell 中尝试了 iota = λx.x S K 定义;它会进行类型检查,但是当您尝试 k = iota (iota (iota iota))s = iota (iota (iota (iota iota))) 时会出现类型错误。)

方法 #2: 可以使用这种递归类型将无类型 lambda 演算表示法嵌入到 Haskell 中:

newtype D = In { out :: D -> D }

D 基本上是一个类型,其元素是从DD 的函数。我们有In :: (D -> D) -> DD -> D 函数转换为普通的D,而out :: D -> (D -> D) 则相反。所以如果我们有x :: D,我们可以通过out x x :: D自行申请。

给定,现在我们可以写了:

iota :: D
iota = In $ \x -> out (out x s) k
    where k = In $ \x -> In $ \y -> x
          s = In $ \x -> In $ \y -> In $ \z -> out (out x z) (out y z)

这需要来自Inout 的一些“噪音”; Haskell 仍然禁止您将D 应用于D,但我们可以使用Inout 来解决这个问题。你实际上不能对D 类型的值做任何有用的事情,但你可以围绕相同的模式设计一个有用的类型。


编辑: iota 基本上是λx.x S K,其中K = λx.λy.xS = λx.λy.λz.x z (y z)。即,iota 采用两个参数的函数并将其应用于 S 和 K;因此,通过传递一个返回其第一个参数的函数,您将获得 S,并通过传递一个返回其第二个参数的函数,您将获得 K。因此,如果您可以使用 iota 编写“返回第一个参数”和“返回第二个参数”,您可以用iota写S和K。但是S and K are enough to get Turing completeness,因此您还可以在讨价还价中获得图灵完整性。事实证明,您可以使用 iota 编写必要的选择器函数,因此 iota 足以实现图灵完备。

因此,这将理解 iota 的问题简化为理解 SK 演算。

【讨论】:

  • 方法 #2 超出了我的理解范围,但我开始掌握方法 #1 你愿意更详细地解释一下吗?该修复操作符究竟是如何工作的?
  • fix 是一个fixed-point combinator,它基本上将无限递归引入到一种缺乏它的语言中。如果您听说过 Y 组合器,那么 fix 是 Haskell 中的等价物。简短的解释是fix f = f (f (f (f ...)))f 的无限应用塔);由于 f 在 Haskell 中可以是惰性的,f 可以选择不使用 f(基本情况)或使用 f (f (f (f ...))) 堆栈(递归情况)返回值。
  • 关于fix 的小说明:通常你可以通过添加一个表示递归大小写的额外参数和fixing 结果:f = fix (λrec x. ... rec y ...),将递归函数f = λx. ... f y ... 转换为其匿名版本。
  • 在你写iota x = x k s k 的第一种方法中,在第二种方法中-“iota基本上是λx.x S K”。是不是打错字了?
  • @WillNess:不,不是错字。 Wikipedia link 显示了两种可能的定义方式,这导致了 sk 的不同基于 iota 的定义。但是,出于某种原因,如果您在 Haskell 中使用 λx.x S K 版本,而该版本会进行类型检查,则相关的 sk 定义不会。
猜你喜欢
  • 2012-08-26
  • 2021-12-04
  • 1970-01-01
  • 2014-03-14
  • 2014-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多