为什么这个在 where 子句中使用作用域类型变量的函数不进行类型检查?

Why does this function that uses a scoped type variable in a where clause not typecheck?

我有一个函数,其值在 where 子句中定义,我想给它一个显式类型注释。注释需要使用顶级函数中的类型变量,因此我的理解是我需要使用 ScopedTypeVariables。这是问题的最小化:

{-# LANGUAGE ScopedTypeVariables #-}

import Control.Monad.Trans.Except
import Data.Functor.Identity

f :: ExceptT String Identity a -> Maybe a
f m = Nothing
  where x :: Identity (Either String a)
        x = runExceptT m

此代码类型检查。它失败并显示以下错误消息:

Couldn't match type ‘a’ with ‘a1’
  ‘a’ is a rigid type variable bound by
      the type signature for f :: ExceptT String Identity a -> Maybe a
      at src/Lib.hs:20:6
  ‘a1’ is a rigid type variable bound by
       the type signature for x :: Identity (Either String a1)
       at src/Lib.hs:22:14
Expected type: ExceptT String Identity a1
  Actual type: ExceptT String Identity a
Relevant bindings include
  x :: Identity (Either String a1) (bound at src/Lib.hs:23:9)
  m :: ExceptT String Identity a (bound at src/Lib.hs:21:3)
  f :: ExceptT String Identity a -> Maybe a
    (bound at src/Lib.hs:21:1)
In the first argument of ‘runExceptT’, namely ‘m’
In the expression: runExceptT m

为什么会失败?我不明白为什么这会导致问题——这似乎是教科书对作用域类型变量的使用。作为参考,我使用的是 GHC 7.10.3.

您需要 explicit forall:

{-# LANGUAGE ScopedTypeVariables #-}

import Control.Monad.Trans.Except
import Data.Functor.Identity

f :: forall a. ExceptT String Identity a -> Maybe a
f m = Nothing
  where x :: Identity (Either String a)
        x = runExceptT m

但是为什么

这是一个很好的问题。这似乎是ScopedTypeVariables的规则。我们知道在 GHC Haskell 中所有顶级变量都是隐式 forall 的,如记录 here。有人会怀疑 GHC 开发人员添加此行为是为了向后兼容:如果没有此规则,未打开扩展名的文件可能会在扩展名打开后停止类型检查。我们可以很容易地想象这样一种场景,在 where 块中声明的辅助函数无意中重用了公共类型变量名称 a, b, c, t,依此类推。如果有人能在 GHC 源代码中找到显式和隐式 forall 变量之间的区别出现的确切位置,那就太好了。

更新

我们开始吧(虽然这都是来自评论和 grepping 的猜测):