0

我试图通过比较其他函数式编程语言和概念中的类似功能来了解递归集如何在内部运行。

我可以在wiki中找到它。在那,我需要知道 Y 组合子,不动点。我可以在wiki中简要了解它。

然后,现在我开始在 Haskell 中应用它。

哈斯克尔

这很容易。但我想知道幕后。

*Main> let x = y; y = 10; in x
10
4

2 回答 2

1

当您a = f b使用 Haskell 或 Nix 之类的惰性函数式语言编写时,其意义不仅仅是赋值。a并且f b将是同样的事情。这通常称为绑定

我将专注于一个 Nix 示例,因为您正在专门询问递归集。

一个简单的属性集

我们先来看一个属性集的初始化。当要求 Nix 解释器评估此文件时

{ a = 1 + 1; b = true; }

它解析它并返回一个像这样的数据结构

{ a = <thunk 1>; b = <thunk 2>; }

其中 thunk对相关语法树节点的引用和对“环境”的引用,它的行为类似于从标识符到它们的值的字典,尽管实现更有效。

也许我们评估这个文件的原因是因为你请求nix-build了,它不仅会询问文件的值,还会在它看到它是一个时遍历属性集。所以nix-build会要求 的值a,这将根据它的 thunk 计算。计算完成后,为保存 thunk 的内存分配实际值type = tInt, value.integer = 2

递归属性集

Nix 有一种特殊的语法,它结合了属性集构造语法 ( { }) 和let-binding 语法的功能。当您使用一些共享值构建属性集时,这可以避免一些重复。

例如

let b = 1 + 1;
in { b = b; a = b + 5; }

可以表示为

rec { b = 1 + 1; a = b + 5; }

评估以类似的方式工作。

起初,求值器返回带有所有 thunk 的属性集的表示,但这次 thunk 引用了一个新环境,该环境包括所有属性,位于现有词法范围之上。

请注意,所有这些表示都可以在执行最少的工作时构建。

nix-build按字母顺序遍历 attrsets,因此它将a首先评估。它是一个引用+语法节点和其中的环境的 thunk b。评估它需要评估b语法节点 (an ExprVar),它引用环境,我们在其中找到1 + 1thunk,它像以前一样更改为 a tIntof 2

正如你所看到的,这个创建 thunk 但只在需要时才评估它们的过程确实是懒惰的,并且允许我们拥有各种具有自己范围规则的语言结构。

Haskell 实现通常遵循类似的模式,但可以编译代码而不是解释语法树,并将所有变量引用完全解析为常量内存偏移量。Nix 尝试在某种程度上做到这一点,但它必须能够依赖字符串,因为不建议使用的with关键字会使范围动态化。

于 2020-12-05T23:54:00.017 回答
0

我自己了几件事。

  • 在 eagar 评估语言中,我必须在使用它之前声明。所以声明的顺序很简单。
int x = 10;
int y = x;
  • 仅适用于 Nix 语言

wiki中,虽然与 Haskell 进行了比较,但没有与 Haskell 进行任何概念比较let ... in

  • 词汇范围

所有变量都是词法范围的

  • 相互递归

https://en.wikipedia.org/wiki/Let_expression#Mutually_recursive_let_expression

于 2020-12-05T13:49:01.443 回答