首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Haskell二级多态性编译错误

Haskell二级多态性编译错误
EN

Stack Overflow用户
提问于 2012-04-27 14:29:52
回答 2查看 464关注 0票数 17

给定以下定义:

代码语言:javascript
复制
import Control.Monad.ST
import Data.STRef

fourty_two = do
  x <- newSTRef (42::Int)
  readSTRef x

以下代码在GHC下编译:

代码语言:javascript
复制
main = (print . runST) fourty_two -- (1)

但这不是:

代码语言:javascript
复制
main = (print . runST) $ fourty_two -- (2)

但正如bdonlan在评论中指出的那样,这确实是编译的:

代码语言:javascript
复制
main = ((print . runST) $) fourty_two -- (3)

但是,这不能编译

代码语言:javascript
复制
main = (($) (print . runST)) fourty_two -- (4)

这似乎表明(3)只有由于对中缀$的特殊处理才能编译,然而,它仍然不能解释为什么(1)可以编译。

问题:

1)我已经阅读了以下两个问题(firstsecond),并且我相信$只能用单态类型实例化。但我同样假设.只能用单态类型实例化,因此也同样会失败。为什么第一个代码成功了,而第二个代码失败了?(例如,对于第一种情况,GHC是否有一个特殊的规则,它不能应用于第二种情况?)

2)当前是否有编译第二个代码的GHC扩展?(也许ImpredicativePolymorphism在某种程度上做到了这一点,但它似乎已被弃用,有什么东西可以取代它吗?)

3)有没有办法使用GHC扩展来定义`my_dollar`来做$所做的事情,但又能够处理多态类型,所以(print . runST) `my_dollar` fourty_two可以编译?

编辑:建议答案:

此外,以下代码无法编译:

代码语言:javascript
复制
main = ((.) print runST) fourty_two -- (5)

这与(1)相同,不同之处在于不使用.的中缀版本。

因此,GHC似乎对$.都有特殊的规则,但只有它们的中缀版本。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-04-28 02:32:06

  1. ,我不太明白为什么第二个不起作用。我们可以查看print . runST的类型,并观察到它具有足够的多态性,因此责任不在(.)。我怀疑GHC针对infix ($)的特殊规则是不够的。如果你在他们的追踪器上提出这个片段作为bug,SPJ和朋友们可能会开放重新检查它。

至于第三个示例为什么有效,这只是因为((print . runST) $)的类型是足够多态的;实际上,它等同于print . runST.

  • Nothing替换了ImpredicativePolymorphism的类型,因为GHC人员还没有看到任何额外的程序员便利性超过了额外的编译器错误的潜在可能性的用例。(我认为他们也不会认为这是令人信服的,尽管我当然不是authority.)

  • We可以定义一个稍微不那么多态的($$)

{-# LANGUAGE RankNTypes #-} infixl 0 $$ ( $$ ) ::((forall s. f s a) -> b) -> ((forall s. f s a) -> b) f$$x=f x

然后,您的示例可以使用这个新操作符进行类型检查:

*Main> (打印runST) $$ fourty_two 42

票数 6
EN

Stack Overflow用户

发布于 2012-04-27 14:46:41

在这个问题上,我不能说得太权威,但我认为可能会发生以下情况:

考虑在每种情况下类型检查器都要做些什么。(print . runST)的类型为Show b => (forall s. ST s t) -> IO ()fourty_two的类型为ST x Int

这里的forall是一个存在类型限定符-在这里它意味着传入的参数在s上必须是通用的。也就是说,您必须传入一个支持任何s值的多态类型。如果您没有显式地声明forall,Haskell会将其放在类型定义的最外层。这意味着fourty_two :: forall x. ST x Int(print . runST) :: forall t. Show t => (forall s. ST s t) -> IO ()

现在,我们可以将forall x. ST x Intforall s. ST s t匹配,方法是让t = Int, x = s。因此,直接调用的情况是可行的。但是,如果我们使用$,会发生什么呢?

$的类型为($) :: forall a b. (a -> b) -> a -> b。当我们解析ab时,由于$的类型没有任何这样的显式类型作用域,因此fourty_twox参数被提升到($)的类型中的最外层作用域-所以($) :: forall x t. (a = forall s. ST s t -> b = IO ()) -> (a = ST x t) -> IO ()。此时,它尝试匹配ab,但失败了。

如果改为编写((print . runST) $) fourty_two,则编译器首先解析((print . runST $)的类型。它将($)的类型解析为forall t. (a = forall s. ST s t -> b = IO ()) -> a -> b;请注意,因为a的第二次出现是无约束的,所以我们不会让那个讨厌的类型变量泄漏到最外层的作用域!所以匹配成功了,函数被部分应用了,表达式的整体类型是forall t. (forall s. ST s t) -> IO (),它正好回到了我们开始的地方,所以它成功了。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10345809

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档