[英]How does GADTs affect type inference in this case?
假设我有一个通过 Writer 发出软故障的 monad。 一个简化的版本是这样的(为了记录,我使用的是 GHC 9.0.2):
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE FlexibleContexts #-}
-- {-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Lib where
import Control.Monad.Writer.CPS
import Data.Maybe
verify :: MonadWriter [String] m => some -> args -> m ()
verify _ _ = fix \(_ :: m ()) ->
do
let warn = tell . (: [])
a = Just 1 :: Maybe Int
b = Just [1, 23] :: Maybe [Int]
c = Just (id :: forall a. a -> a)
-- isJust' :: String -> Maybe a -> m ()
isJust' tag v = unless (isJust v) do
warn $ tag <> " is Nothing"
isJust' "a" a
isJust' "b" b
isJust' "c" c
一切都很好,直到我添加GADTs
扩展,然后 GHC 无法找到isJust'
的最通用类型:
• Couldn't match type ‘[Int]’ with ‘Int’
Expected: Maybe Int
Actual: Maybe [Int]
• In the second argument of ‘isJust'’, namely ‘b’
In a stmt of a 'do' block: isJust' "b" b
In the expression:
do let warn = tell . (: [])
a = ...
....
isJust' "a" a
isJust' "b" b
isJust' "c" c
|
22 | isJust' "b" b
| ^
/var/tmp/demo/src/Lib.hs:23:17: error:
• Couldn't match type ‘a0 -> a0’ with ‘Int’
Expected: Maybe Int
Actual: Maybe (a0 -> a0)
• In the second argument of ‘isJust'’, namely ‘c’
In a stmt of a 'do' block: isJust' "c" c
In the expression:
do let warn = tell . (: [])
a = ...
....
isJust' "a" a
isJust' "b" b
isJust' "c" c
|
23 | isJust' "c" c
| ^
此时我必须取消注释isJust'
类型注释才能进行类型检查 - 我想知道这里有什么作用,我认为我没有使用任何 GADTs 功能?
旁注 1:我通常只是猛烈NoMonomorphismRestriction
看看它是否有帮助,但它没有。
旁注 2:使用fix
只是为了在存在ScopedTypeVariables
的情况下控制那个m
- 一个附带问题是是否有更好的方法来做到这一点而不依赖于verify
具有显式类型签名(比如定义了这个 function在一个实例内)。
问题不是GADTs
而是MonoLocalBinds
,它是由GADTs
自动启用的。 问题在于isJust':: String -> Maybe a -> m ()
具有多态类型签名,这意味着您每次调用时都可以选择a
是什么。 至少,如果MonoLocalBinds
未启用,它将是多态的。 根据文档:
使用 MonoLocalBinds,绑定组将被泛化当且仅当
- 它是一个顶级绑定组,或者
- 它的每个自由变量(不包括由组本身绑定的变量)都是封闭的(参见下一个项目符号),或者
- 它的任何绑定器都有一个部分类型签名(请参阅部分类型签名)。
如果一个变量不满足三个要点之一,它的类型就不是泛化的,即它不会是多态的。
什么是“封闭变量”有一个严格的定义,但重要的部分是:
如果一个变量是封闭的,那么它的类型肯定没有自由类型变量。
isJust'
的第二个参数的类型为Maybe a
,表示某个未知a
,这意味着它没有关闭。 因此,项目符号 1 不满足,因为isJust'
在verify
中定义,项目符号 2 不满足,因为isJust'
的第二个参数没有关闭,项目符号 3 不满足,因为isJust'
的类型中没有漏洞。 因此, isJust'
毕竟不是多态的。
这一切意味着String -> Maybe a -> m ()
中的a
实际上是 GHC 必须推断的固定类型。 它看到你调用isJust' "a" (a::Maybe Int)
并假设a ~ Int
,但随后它看到isJust' "b" (b::Maybe [Int])
并假设a ~ [Int]
。 显然这是一个问题,所以 GHC 抱怨。 添加类型签名的唯一作用是告诉 GHC 使isJust'
多态。
请注意,您可以同时拥有GADTs
和NoMonoLocalBinds
,但是因为使用 GADT 进行类型推断很棘手,当您实际使用 GADT 时,这可能只会导致更多问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.