给定以下定义:
import Control.Monad.ST
import Data.STRef
fourty_two = do
x <- newSTRef (42::Int)
readSTRef x
以下代码在GHC下编译:
main = (print . runST) fourty_two -- (1)
但这不是:
main = (print . runST) $ fourty_two -- (2)
但正如bdonlan在评论中指出的那样,这确实是编译的:
main = ((print . runST) $) fourty_two -- (3)
但是,这不能编译
main = (($) (print . runST)) fourty_two -- (4)
这似乎表明(3)只有由于对中缀$
的特殊处理才能编译,然而,它仍然不能解释为什么(1)可以编译。
问题:
1)我已经阅读了以下两个问题(first,second),并且我相信$
只能用单态类型实例化。但我同样假设.
只能用单态类型实例化,因此也同样会失败。为什么第一个代码成功了,而第二个代码失败了?(例如,对于第一种情况,GHC是否有一个特殊的规则,它不能应用于第二种情况?)
2)当前是否有编译第二个代码的GHC扩展?(也许ImpredicativePolymorphism在某种程度上做到了这一点,但它似乎已被弃用,有什么东西可以取代它吗?)
3)有没有办法使用GHC扩展来定义`my_dollar`
来做$
所做的事情,但又能够处理多态类型,所以(print . runST) `my_dollar` fourty_two
可以编译?
编辑:建议答案:
此外,以下代码无法编译:
main = ((.) print runST) fourty_two -- (5)
这与(1)相同,不同之处在于不使用.
的中缀版本。
因此,GHC似乎对$
和.
都有特殊的规则,但只有它们的中缀版本。
发布于 2012-04-28 02:32:06
,
print . runST
的类型,并观察到它具有足够的多态性,因此责任不在(.)
。我怀疑GHC针对infix ($)
的特殊规则是不够的。如果你在他们的追踪器上提出这个片段作为bug,SPJ和朋友们可能会开放重新检查它。至于第三个示例为什么有效,这只是因为((print . runST) $)
的类型是足够多态的;实际上,它等同于print . runST
.
ImpredicativePolymorphism
的类型,因为GHC人员还没有看到任何额外的程序员便利性超过了额外的编译器错误的潜在可能性的用例。(我认为他们也不会认为这是令人信服的,尽管我当然不是authority.)
($$)
:{-# 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
发布于 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 Int
与forall s. ST s t
匹配,方法是让t = Int, x = s
。因此,直接调用的情况是可行的。但是,如果我们使用$
,会发生什么呢?
$
的类型为($) :: forall a b. (a -> b) -> a -> b
。当我们解析a
和b
时,由于$
的类型没有任何这样的显式类型作用域,因此fourty_two
的x
参数被提升到($)
的类型中的最外层作用域-所以($) :: forall x t. (a = forall s. ST s t -> b = IO ()) -> (a = ST x t) -> IO ()
。此时,它尝试匹配a
和b
,但失败了。
如果改为编写((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 ()
,它正好回到了我们开始的地方,所以它成功了。
https://stackoverflow.com/questions/10345809
复制相似问题