繁体   English   中英

在这种情况下,GADT 如何影响类型推断?

[英]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'多态。

请注意,您可以同时拥有GADTsNoMonoLocalBinds ,但是因为使用 GADT 进行类型推断很棘手,当您实际使用 GADT 时,这可能只会导致更多问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM