Haskell 存在构造函数的模式绑定
在编写Haskell时,我曾接触过Lisp,但我注意到了一些奇怪的事情,但我没有理解 这很好:Haskell 存在构造函数的模式绑定,haskell,homoiconicity,Haskell,Homoiconicity,在编写Haskell时,我曾接触过Lisp,但我注意到了一些奇怪的事情,但我没有理解 这很好: {-# 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{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不是同性恋
我的理由是:
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不是同性恋
不。同象似性是一个危险的问题:每种语言都有其源文本和AST1,并且Haskell在内部实现为各种中间语言之间的一系列去修饰过程
真正的问题是,让
中的…和的案例…具有根本不同的语义,这是有意的。与case…of
的模式匹配是严格的,因为它强制对scrutinee进行评估,以便选择要评估的RH,但是let…in
形式中的模式绑定是惰性的。从这个意义上讲,让e2中的p=e1
实际上与~p->e2的案例e1最为相似(请注意使用~
!)的惰性模式匹配),这会产生类似但不同的错误消息:
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
ghci>case未定义的{~Foo{getFoo}->show getFoo}
:5:22:错误:
•不能使用存在或GADT数据构造函数
在惰性(~)模式中
•在模式中:Foo{getFoo}
在模式中:~Foo{getFoo}
在另一种情况下:~Foo{getFoo}->show getFoo
这在对的答复中有更详细的解释
1如果您对此不满意,请注意,Haskell在大多数Lisper使用该词的意义上是同源的,因为它支持模拟Lisp的
引号
运算符,其形式为[|…|]
引号括号,这些都是模板Haskell的一部分。我对此进行了一些思考,虽然起初这种行为似乎有些奇怪,但经过一些思考后,我想人们可以这样来证明:
假设我以你的第二个(失败的)例子为例,经过一些按摩和价值置换后,我将其简化为:
data Foo = forall a. Show a => Foo { getFoo :: a }
main::IO()
main = do
let Foo x = Foo (5::Int)
putStrLn $ show x
这会产生错误:
无法将预期类型“p”与实际类型“a”匹配,因为类型变量“a”将脱离其作用域
如果允许模式匹配,x的类型是什么?好。。类型当然是Int
。但是,Foo
的定义指出,getFoo
字段的类型是属于Show
实例的任何类型。Int
是Show
的一个实例,但它不是任何类型。。这是一个具体的问题。。在这方面,Foo
中包含的实际特定类型的值将变为“可见”(即escape),因此违反了我们的明确保证,即对于所有a。显示a=>…
如果我们现在查看通过在函数声明中使用模式匹配来工作的代码版本:
data Foo = forall a . Show a => Foo { getFoo :: !a }
unfoo :: Foo -> String
unfoo Foo{..} = show getFoo
main :: IO ()
main = do
putStrLn . unfoo $ Foo (5::Int)
查看unfoo
函数,我们发现没有任何东西表明Foo
中的类型是任何特定类型。。(一个Int
或其他)。。在该函数的范围内,我们所拥有的只是原始保证,即getFoo
可以是Show
的实例的任何类型。包装值的实际类型仍然是隐藏的和不可知的,因此没有违反任何类型的保证,幸福随之发生
PS:我忘了提到,
Int
位当然是一个例子。。在您的情况下,foo
值内的getFoo
字段的类型是a
类型,但这是GHC类型推断所指的特定(非存在)类型(而不是类型声明中的存在a
)。。我刚刚提出了一个具体的Int
类型的例子,以便更容易理解和更直观。这个问题是关于Haskell的,而不是Lisp。破坏者:“同音异象”实际上没有任何意义。你对同音异象的定义没有解决我的问题。。mk,更紧迫的是:可能是我在这里的理解有限:如果我将模式绑定设置为严格的,为什么它不编译?@zabeltech可能是因为它只是一个非常不常见的用例而没有实现。您当然可以在请求实现时提交一个问题。@zabeltech在任何情况下都是“同象似性”,即使它实现了,它也可以。即使在Lisp中,您也不希望涉及let
的错误以大小写
的形式报告。两种语言特征具有相似但不同的语义并保持分离,而不是将一种语言特征分解为另一种语言特征,这是一种与同源性完全正交的选择,GHC中的许多特征都是通过分解来定义的(例如do
符号和LambdaCase
)。嗯,是的,可能是没人打扰……我还是哈斯克尔的忠实粉丝。但是评估策略似乎会影响类型检查器,尽管您已经证明它确实会影响。。。你认为这有一个共同的感官原因吗?@zabeltech评估策略必须这样做