【问题标题】:name capture in Haskell `let` expressionHaskell`let`表达式中的名称捕获
【发布时间】:2018-02-02 06:47:39
【问题描述】:

我正在编写一个类似这样的函数:

f x = let
  x = ...
  in
    e

由于 Haskell 中的作用域规则,任何在 e 中使用 x 都将解析为 let 构造中的 x 定义。

为什么在 Haskell 中允许这样的事情? 编译器不应该拒绝这样的程序,告诉我们不能绑定与函数参数同名的值。

(这个例子可能很简单,但在现实世界的上下文中,变量具有与之相关的语义意义,很容易犯这样的错误)

【问题讨论】:

  • 名称/标识符/变量阴影在许多编程语言中通常是允许的。如果被禁止,f x = x+1 将停止工作,因为有人在某处添加了全局x=42,或者只是因为某些导入库的更新版本现在导出了x。即使在 lambda 演算中 \x. \x. ... 也是允许的(这稍微简化了语义)。 ML 甚至允许let x = x+1 in ...,这在 ML 中不是递归的,这已成为惯用的。现在,我并不是说阴影是好的,但 IMO 对此的编译器警告总比错误好。
  • 在像 Haskell 这样高度递归的语言中,可能很难将变量名的意外重用与有意递归区分开来......
  • @MathematicalOrchid 确实如此。我刚刚修复了我的代码中的一个错误,该错误导致程序中出现无限循环。

标签: haskell let


【解决方案1】:

您可以使用编译器标志为这种类型的名称隐藏启用警告

-fwarn-name-shadowing

每当内部范围值与外部范围值同名时,此选项都会发出警告,即内部值会遮盖外部值。这可以捕获印刷错误,这些错误会变成难以发现的错误,例如,无意中捕获了 f = ... let f = id in ... f ... 中的递归调用。

但是,使用-Wall 进行编译更为常见,其中包含许多其他警告,可帮助您避免不良做法。

【讨论】:

  • 好建议!但是您没有回答“为什么在 haskell 中允许这样的事情?”的问题?
  • @LudvigH 那是在征求意见,真的。我认为乍得的回答字里行间。如果这是一个警告,那么 Haskell/GHC 的创建者已经意识到它有潜在的危险,但认为没有必要完全禁止它。
  • 很公平。但我经常对 SO 的人感到惊讶,他们设法找到设计文档和规范,这些文档和规范解释了导致诸如阴影等决策的推理路线,所以我认为这样的答案是有效的,而不是意见问题。无论哪种情况 - 我认为乍得提供了如何处理它的实际问题的答案。
  • @LudvigH,Haskell 几乎可以肯定地允许它,因为它在 lambda 演算和导致 Haskell 的 LC 后代链中是允许的。 LC 本身旨在作为一种正式的逻辑系统,而不是一种编程语言。 Alonzo Church 可能甚至没有考虑到人为错误的可能性。
  • @LudvigH 也许是另一种方式,没有人设计任何明确反对名称阴影的东西,因为这需要额外的步骤(或努力去做)。例如,使用 BNF 排除名称阴影可能很难(可能不可能),因此 let 表达式的正常定义允许它(直到有人抱怨并将其添加为警告)。
猜你喜欢
  • 2019-01-29
  • 2020-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-06
  • 2010-11-25
  • 2020-02-17
  • 1970-01-01
相关资源
最近更新 更多