runST和函数组合

Grz*_*ała 53 haskell function-composition

为什么这个类型检查:

runST $ return $ True
Run Code Online (Sandbox Code Playgroud)

虽然以下不是:

runST . return $ True
Run Code Online (Sandbox Code Playgroud)

GHCI抱怨:

Couldn't match expected type `forall s. ST s c0'
            with actual type `m0 a0'
Expected type: a0 -> forall s. ST s c0
  Actual type: a0 -> m0 a0
In the second argument of `(.)', namely `return'
In the expression: runST . return
Run Code Online (Sandbox Code Playgroud)

ham*_*mar 48

简短的回答是类型推断并不总是适用于更高级别的类型.在这种情况下,它无法推断类型(.),但它会检查我们是否添加了显式类型注释:

> :m + Control.Monad.ST
> :set -XRankNTypes
> :t (((.) :: ((forall s0. ST s0 a) -> a) -> (a -> forall s1. ST s1 a) -> a -> a) runST return) $ True
(((.) :: ((forall s0. ST s0 a) -> a) -> (a -> forall s1. ST s1 a) -> a -> a) runST return) $ True :: Bool
Run Code Online (Sandbox Code Playgroud)

如果我们($)用自己的版本替换,第一个示例也会出现同样的问题:

> let app f x = f x
> :t runST `app` (return `app` True)
<interactive>:1:14:
    Couldn't match expected type `forall s. ST s t0'
                with actual type `m0 t10'
    Expected type: t10 -> forall s. ST s t0
      Actual type: t10 -> m0 t10
    In the first argument of `app', namely `return'
    In the second argument of `app', namely `(return `app` True)'
Run Code Online (Sandbox Code Playgroud)

同样,这可以通过添加类型注释来解决:

> :t (app :: ((forall s0. ST s0 a) -> a) -> (forall s1. ST s1 a) -> a) runST (return `app` True)
(app :: ((forall s0. ST s0 a) -> a) -> (forall s1. ST s1 a) -> a) runST (return `app` True) :: Bool
Run Code Online (Sandbox Code Playgroud)

这里发生的是GHC 7中有一个特殊的输入规则,它只适用于标准($)运算符.Simon Peyton-Jones 在GHC用户邮件列表的回复中解释了这种行为:

这是可以处理不可预测类型的类型推断的激励示例.考虑以下类型($):

($) :: forall p q. (p -> q) -> p -> q
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我们需要实例p(forall s. ST s a),这就是impredicative多态是指:用实例化一个多态类型的类型变量.

可悲的是,我知道没有任何合理复杂的系统可以在没有帮助的情况下进行检查.有很多复杂的系统,我至少有两篇关于论文的共同作者,但他们都太过于复杂地生活在GHC中.我们确实有一个boxy类型的实现,但是在实现新的类型检查器时我把它拿出来了.没有人理解它.

但是,人们经常这么写

runST $ do ... 
Run Code Online (Sandbox Code Playgroud)

在GHC 7中我实现了一个特殊的输入规则,仅用于中缀的使用($).只需将其(f $ x)视为一种新的语法形式,具有明显的输入规则,然后就可以了.

你的第二个例子失败了,因为没有这样的规则(.).


Dan*_*ner 34

这种runST $ do { ... }模式是如此常见,而且它通常不会进行类型检查的事实是如此令人烦恼,GHC包含了一些ST特定的类型检查黑客以使其工作.那些黑客可能会在这里为($)版本而不是(.)版本.

  • 而不是`($)runST(返回True)`,它也没有进行类型检查 (2认同)