我有一个函数,它在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。
答案 0 :(得分:7)
您需要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
' d,如文档here所示。有人会怀疑GHC开发人员为了向后兼容性而添加了这种行为:如果没有这个规则,没有启用扩展名的文件可能会在扩展程序打开后停止进行类型检查。我们可以很容易地想象一个场景,其中在where
块中声明的辅助函数无意中重用了公共类型变量名a, b, c, t
,依此类推。如果有人能够在GHC源代码中找到显式和隐式forall
变量之间的区别,那就太棒了。
我们走了(尽管这些都是评论和评论的猜测):
在对用户签名进行类型检查时,函数tcUserTypeSig
会调用findScopedTyVars
。 TcBinds.hs:ef44606:L1786
findScopedTyVars
,TcRnTypes
过滤了forall
个tcSplitForAllTys
中的{li> splitForAllTys
。 TcRnTypes.hs:ef44606:L1221
这是forall
的包装器,它折叠了一个类型的子类型,以构建{{1}}引入的类型变量列表。 Types/Type.hs:ef44606:L1361