haskell - 存在构造函数的模式绑定
问题描述
作为一个接触过 Lisp 的程序员,在编写 Haskell 时,我注意到了一些奇怪的东西,我没能理解。
这编译得很好:
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ExistentialQuantification #-}
data Foo = forall a. Show a => Foo { getFoo :: a }
showfoo :: Foo -> String
showfoo Foo{getFoo} = do
show getFoo
而这失败了:
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ExistentialQuantification #-}
data Foo = forall a. Show a => Foo { getFoo :: a }
showfoo :: Foo -> String
showfoo foo = do
let Foo{getFoo} = foo
show getFoo
对我来说,第二个片段失败的原因并不明显。
问题是:
我是否错过了某些东西或由于 haskell 不是同音异形这一事实而导致了这种行为?
我的理由是,鉴于:
Haskell 需要将记录模式匹配实现为编译器扩展,因为它选择使用语法而不是数据。
函数头或 let 子句中的匹配是两种特殊情况。
很难理解这些特殊情况,因为它们既不能实现,也不能直接在语言本身中查找。
因此,无法保证整个语言的一致行为。尤其是与其他编译器扩展一起,例如。
ps:编译错误:
error:
• My brain just exploded
I can't handle pattern bindings for existential or GADT data constructors.
Instead, use a case-expression, or do-notation, to unpack the constructor.
• In the pattern: Foo {getFoo}
In a pattern binding: Foo {getFoo} = foo
In the expression:
do { let Foo {getFoo} = foo;
show getFoo }
编辑:不同的编译器版本会针对相同的问题给出此错误
* Couldn't match expected type `p' with actual type `a'
because type variable `a' would escape its scope
This (rigid, skolem) type variable is bound by
a pattern with constructor: Foo :: forall a. Show a => a -> Foo
解决方案
我是否错过了某些东西或由于 haskell 不是同音异形这一事实而导致了这种行为?
不。同音性是一条红鲱鱼:每种语言都与它的源文本和它的 AST 1同音,事实上,Haskell在内部实现为各种中间语言之间的一系列脱糖传递。
真正的问题是,let...in
只是case...of
有根本不同的语义,这是有意的。模式匹配case...of
是严格的,从某种意义上说,它强制审查员评估以选择要评估的 RHS,但let...in
表单中的模式绑定是惰性的。从这个意义上说,let p = e1 in e2
实际上最类似于case e1 of ~p -> e2
(注意使用 ! 的惰性模式匹配~
),它会产生类似但不同的错误消息:
ghci> case undefined of { ~Foo{getFoo} -> show getFoo }
<interactive>:5:22: error:
• An existential or GADT data constructor cannot be used
inside a lazy (~) pattern
• In the pattern: Foo {getFoo}
In the pattern: ~Foo {getFoo}
In a case alternative: ~Foo {getFoo} -> show getFoo
这在对Odd ghc 错误消息“我的大脑刚刚爆炸”的回答中有更详细的解释?.
1如果这不能满足您的要求,请注意 Haskell在大多数 Lispers 使用该词的意义上是谐音的,因为它支持引号quote
形式的 Lisp 运算符的模拟[| ... |]
,这是模板 Haskell 的一部分。
推荐阅读
- excel - Excel VBA - 如何从工作簿 B 传输形状并替换工作簿 A 中特定的现有形状?
- ubuntu - 如何静态链接库以构建共享库
- .htaccess - apache上的转发代理,带有.htaccess到jupyter笔记本
- javascript - 仅在 for 循环中执行所有 Async 函数之后执行函数
- typescript - Typescript 和 WebRTC,“RTCPeerConnection”类型上不存在“createDataChannel”
- ios - 使用 ObjectMapper iOS 高效解析 JSON 数据
- javascript - 未执行 Jsstore 添加
- ruby-on-rails - 在 Rails 应用程序上的 127.0.0.1:6379(ECONNREFUSED) 上连接到 Redis 时出错
- python - 如何使用我从另一个请求中获得的 html 表单发送 POST 请求?
- c# - 如何首先在实体框架代码中设置身份主键的起始值?