我对 Haskell 的重击感到困惑

2024-01-22

The wikibook https://en.wikibooks.org/wiki/Haskell/Laziness说:在这个表达中

let z = (length [1..5], reverse "olleh") in ...

z是一声重击。

但是这个堆栈溢出 https://stackoverflow.com/questions/6872898/what-is-weak-head-normal-form帖子说的是最外层z是数据构造函数(,),在 WHNF 中,因此z不是重击。

那么哪一个是正确的呢?我在 GHCi 上尝试过这个

ghci> z = (length [1..5], reverse "olleh")
ghci> :sprint z
z = (_,_)

看到那个z不是 thunk,只有它的组件才是。

编辑:维基百科说值是按层评估的。z是外层(,)这是一声重击。通过模式匹配解构该层后,您将获得两个子组件,它们本身就是 thunk。这对我来说是有道理的,因为它符合这样的原则:如果你不参考z后来z不需要评估。但 GHCi 的做法有所不同。那么这是否意味着原则和实践之间存在差距,或者维基百科根本就是错误的?


延迟求值让我们可以在调用实际求值之前处理函数返回的值。我们可以将其传递给其他调用者,并且仅在稍后需要详细查看该值时才触发评估。

为此,我们创建一个 thunk。 thunk 只不过是内存中的一个小结构,其中包含用于存储参数的字段。

但是应用数据构造函数(例如(,)) also只是在内存中生成一个小结构,用于存储它所应用的参数的字段。所以事实证明,为数据构造函数的应用程序创建 thunk 确实没有任何意义。

如果我们创建一个 thunk,我们只会将参数存储在 thunk 中而不查看它们,并且稍后当某些模式与值匹配时(触发它被评估),它们将被访问。如果我们跳过 thunk 并真正应用数据构造函数,我们将只存储参数而不查看它们,并且稍后当某些模式与值匹配时将访问它们。无论我们是否使用 thunk 并延迟应用程序,系统的行为基本相同。

因此,在试图帮助学习者建立 Haskell 如何工作的概念模型时,这样说是有道理的:any当变量绑定到应用程序的结果时,它将创建一个 thunk。作为一个概念模型来说这很好;它与该语言的所有通常可观察的行为一致,并且当所应用的东西是数据构造函数时,它比担心特殊异常更简单。我想这就是维基教科书文章所采取的观点。

But remember that thunks aren't actually the goal. There's no hard specification for exactly when GHC must use a thunk to delay evaluation of a value. They're just a means to an end. In cases like this when the observable behaviour will be the same either way, GHC is free to create a thunk or not. It's always safe to skip the thunk for a data constructor application1, but GHC can also do this when strictness analysis reveals that the value was going to be forced soon anyway. GHC simply doesn't always do exactly what textbooks say; it does whatever it thinks will produce the "best" code while still behaving the same as the simpler model you'll get from textbooks.

重要的是要记住这一点:sprint不是检查 Haskell 良好理论特性的工具。它是一个调试工具,用于查看 GHC 使用的实现。它具体来说让你看到不应该从 Haskell 本身观察到的细节,这就是为什么它必须是一个特殊的 GHCi 命令而不是实际语言的功能。有时:sprint只会揭示像这样的小细节,这些细节更多地与 GHC 的内部实现细节有关,而不是与 Haskell 作为一种语言有关。


1 Unless it has strict fields, but then you can think about it as syntactic sugar for a function that does examine its arguments before storing them. The "real" underlying constructor still doesn't need a thunk, but that is never actually applied in the user's code, only the wrapper function that does the extra evaluation.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我对 Haskell 的重击感到困惑 的相关文章

  • 不同 hs 文件中的函数分离时堆栈空间溢出

    我有一个巨大的 haskell 文件 它编译和运行没有任何问题 我想将一些函数和类型定义放在通用 hs 文件中的单独模块中 然后将其导入我的主模块中 虽然主程序编译时没有任何错误 它还编译导入的模块 但当我尝试运行它时 出现堆栈空间溢出 I
  • 可以通过Data.Function.fix来表达变形吗?

    我有这个可爱的fixana这里的函数执行速度比她的姐妹快 5 倍左右ana 我有一个criterion报告支持我这一点 ana alg Fix fmap ana alg alg fixana alg fix f gt Fix fmap f
  • 如何在 Haskell 中枚举递归数据类型?

    这篇博文 http lukepalmer wordpress com 2008 05 02 enumerating a context free language 对于如何使用 Omega monad 对角枚举任意语法有一个有趣的解释 他提
  • Haskell 二进制解析

    我一直在尝试在 haskell 中实现一个协议解析器 而且我对这门语言还很陌生 特别是当涉及到 monad 时 我一直在使用binary 0 5 0 2 并描述了协议的标头和所有有效负载 我想要解析的消息如下所示 header payloa
  • 如何、为什么以及何时使用“.Internal”模块模式?

    我在上面看到了几个包裹hackage http hackage haskell org packages archive pkg list html其中包含模块名称 Internal作为他们的姓氏组成部分 例如Data ByteString
  • 'lens' 的阴谋集团依赖性解析失败

    我刚刚做了一个阴谋更新并尝试从 hackage 安装 lens 这给了我以下错误 cabal install j lens Resolving dependencies Configuring dlist 0 7 0 1
  • 为什么haskell中的递归列表这么慢?

    我对 Haskell 很陌生 我在 Haskell 中定义了一个函数 febs Integral a gt a gt a febs n n lt 0 0 n 1 1 n 2 1 otherwise febs n 1 febs n 2 但是
  • 如何将只缓存某些内容的字段添加到ADT?

    我经常需要向 ADT 添加字段 仅记住一些冗余信息 但我还没有完全弄清楚如何又好又高效地做到这一点 说明问题的最好方法是举个例子 假设我们正在使用无类型 lambda 项 type VSym String data Lambda Var V
  • 我应该使用镜头中的什么来按索引构建只读吸气剂?

    我有一个内部细节被隐藏的类型 我想提供某种镜头 可以在特定索引处读取所述类型的元素 但是not修改它们 一个Ixed我的类型的实例似乎没有做我想要的事情 因为它明确允许修改 尽管不允许插入或删除 如果我想允许只读索引 我不确定我使用什么 如
  • Haskell 中的相互递归求值器

    Update 我已经添加一个答案 https stackoverflow com questions 3524485 mutually recursive evaluator in haskell 4504200 4504200这描述了我的
  • 在 haskell 中处理 IO 与纯代码

    我正在编写一个shell脚本 我在haskell中的第一个非示例 它应该列出一个目录 获取每个文件大小 进行一些字符串操作 纯代码 然后重命名一些文件 我不确定我做错了什么 所以有两个问题 我应该如何安排这样的程序中的代码 我有一个具体问题
  • 对元组列表进行排序的函数 - Haskell

    抱歉 这个简单的问题只是我对 haskell 非常陌生 我正在尝试编写一个函数 order 它将对另一个函数 Frequency 生成的元组列表进行排序 频率计算列表中不同元素的数量 a给出一个这样的结果 比如 gt 频率 aabbbccc
  • 带有查询参数的渲染 url

    无法找到简单问题的解决方案 答案应该是显而易见的 如何在 hamlet 模板中使用查询参数渲染 url I e ItemsR 将生成http localhost 3000 items我如何生成类似的东西http localhost 3000
  • 什么是阴谋地狱?

    在阅读有关 阴谋地狱 的内容时 我有点困惑 因为这个词的含义太多了 我猜最初 Cabal Hell 指的是钻石依赖问题 该问题是通过限制构建计划在每个构建计划中只有任何包的单个版本来解决的 一个包的两个不同版本不能存在于单个构建计划中 正如
  • 使用 Haskell 将函数注入到 Java .class 文件中

    我使用 Haskell 编写了一个 Java 字节码解析器 它工作得很好 然而下一步让我完全难住了 我的 Haskell 程序需要修改 class 文件 以便在执行时 Java 程序打印 输入 此处的方法名称 在执行方法之前 并且 退出 此
  • Haskell:GHC 无法推断类型。由类型签名错误绑定的刚性类型变量

    我看过几篇主题相似的帖子 但它们并不能真正帮助我解决我的问题 所以我才敢重复 现在我有一个带有签名的函数 run Expr query gt RethinkDBHandle gt query gt IO JSON 这是一个数据库查询运行函数
  • 如何处理或避免BlockedIndefinitelyOnSTM异常?

    我花了很多时间来解决我正在处理的应用程序中遇到的问题 该应用程序是一个 Web 应用程序 使用 scotty 公开 REST 端点 它使用一个TVar保持其更新的状态STM a由前端层触发的动作 由于该应用程序基于事件溯源原则 因此业务层生
  • Haskell cabal:我刚刚安装了软件包,但现在找不到软件包

    在这里 http haskell org haskellwiki Cabal Install I just installed packages 2C but now the packages are not found这是我可以找到我正在
  • Haskell 中实例声明的参数顺序切换

    我想进行实例声明 但自由类型变量不是最后一个变量 例如 我有一个类声明 class Poppable m where tryPop m a gt Maybe a m a 现在我想让 Q PSQ 优先级队列 成为 Poppable 的实例 具
  • 为什么我不能将 Int 类型与 a 类型匹配

    哈斯克尔新手在这里 我在这里尝试做的事情的一个过于简单的例子 test Int gt a test i i Couldn t match expected type a with actual type Int a is a rigid t

随机推荐