【问题标题】:runST and function compositionrunST 和函数组合
【发布时间】:2012-02-27 16:59:42
【问题描述】:

为什么要进行这种类型检查:

runST $ return $ True

虽然以下没有:

runST . return $ True

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

【问题讨论】:

  • 如果($) 可以被赋予一个依赖类型的签名($) : forall (a : *) (b : a -> *) . ((x : a) -> b x) -> (x : a) -> b x 它可以在没有GHC 技巧的情况下工作,(.) 也是如此。

标签: haskell function-composition


【解决方案1】:

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

> :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

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

> 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)'

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

> :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

这里发生的情况是 GHC 7 中有一个特殊的键入规则,它仅适用于标准的 ($) 运算符。 Simon Peyton-Jones 在a reply on the GHC users mailing list 中解释了这种行为:

这是一个可以处理类型推断的激励示例 谓语类型。考虑($)的类型:

($) :: forall p q. (p -> q) -> p -> q

在示例中,我们需要用(forall s. ST s a) 实例化p,这就是 指示性多态性意味着:实例化一个类型变量 多态类型。

遗憾的是,我知道没有可以进行类型检查的合理复杂系统 [这] 没有帮助。有很多复杂的系统,我有 是至少两篇论文的合著者,但他们都是太 Jolly 住在 GHC 很复杂。我们确实有一个实施 四四方方的类型,但我在实现新的类型检查器时把它拿出来了。 没人明白。

然而,人们经常写作

runST $ do ... 

在 GHC 7 中我实现了一个特殊的类型规则,仅用于 ($) 的中缀使用。只需将(f $ x) 视为一个新的 句法形式,带有明显的打字规则,然后就可以了。

您的第二个示例失败,因为(.) 没有这样的规则。

【讨论】:

  • 谢谢,现在开始有意义了。
【解决方案2】:

runST $ do { ... } 模式非常常见,而且它通常不会进行类型检查的事实非常烦人,以至于 GHC 包含了一些 ST 特定的类型检查技巧以使其工作。这些 hack 可能是针对 ($) 版本,而不是 (.) 版本。

【讨论】:

  • 有趣的地方。只要看到它应用于 2 个参数,只需删除 ($) 可能就足够了。应该很容易通过将其替换为与 ($) 相同的自定义函数来验证,然后查看类型检查器是否会抱怨。
  • @Ingo:是的,let app f x = f x in runST `app` (return `app` True) 无法进行类型检查。很有趣。
  • @Hammar:这意味着,GHC 显然会降低 $,尽管这对于更高级别的类型并不完全正确。
  • 这是正确答案。第一个 sn-p 通常不会进行类型检查,但是,GHC 对 runST $ whatever 有一个特殊规则,但对 runST . foo 没有。
  • 而不是($) runST (return True),它也不会进行类型检查
【解决方案3】:

这些消息有点令人困惑(或者我觉得)。 让我重写你的代码:

runST (return True)   -- return True is ST s Bool
(runST . return) True  -- cannot work

另一种说法是单态m0 a0(返回的结果,如果它会得到a0)不能与(forall s.ST s a)统一。

【讨论】:

  • 这会进行类型检查:unsafePerformIO . return $ True
  • @Ingo:您对这两个示例的解析错误。 runST $ return $ True($) runST (($) return True)runST . return $ True($) ((.) runST return) True。他们会在没有 rank-2 类型的情况下做同样的事情。
  • @ehird - 你说得对,我忘了点。 (嘿,那押韵……)
猜你喜欢
  • 2012-07-24
  • 1970-01-01
  • 1970-01-01
  • 2013-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-04
相关资源
最近更新 更多