类型不统一使用类型 class 和类型族

Types don't unify using a type class and a type family

考虑片段

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}

import Data.Proxy

monadify' :: forall m sig. (Monad m, Sig sig) => Proxy sig -> Monadify m sig
monadify' p = monadify p (return () :: m ())

type family Monadify f a where
  Monadify f (a -> r) = a -> Monadify f r
  Monadify f a = f a

class Sig sig where
    monadify :: Monad m => Proxy sig -> m () -> Monadify m sig

我没有给出任何实例,但示例用法是 f :: Int -> String -> Bool, monadify' f :: Int -> String -> IO Bool

类型检查失败并显示以下错误消息:

Couldn't match expected type ‘Monadify m sig’
            with actual type ‘Monadify m0 sig0’
NB: ‘Monadify’ is a type function, and may not be injective
The type variables ‘m0’, ‘sig0’ are ambiguous
In the ambiguity check for the type signature for ‘monadify'’:
  monadify' :: forall (m :: * -> *) sig.
               (Monad m, Sig sig) =>
               Monadify m sig
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature for ‘monadify'’:
  monadify' :: forall m sig. (Monad m, Sig sig) => Monadify m sig

凭直觉我会说它应该进行类型检查,但是 GHC 被类型族搞糊涂了,它没有被注释为单射(我宁愿不向后兼容)。它可以从 m ()Proxy 恢复原像,所以我真的不知道这里有什么问题。

编辑:

如错误消息所示,我可以输入 AllowAmbiguousTypes,这在我的案例中解决了所有问题。但我不知道使用该扩展的后果,而且我更想知道为什么我的示例没有进行类型检查。

感觉和unifier有关,首先试图统一Monadify m sigs,从而推断它不能证明sigs和m 是相同的。虽然统一器只需要查看传递的参数就知道它们是相同的,所以这可能是 AllowAmbiguousTypes 帮助的地方。

问题出在 monadify',而不是 monadify

假设你正在打电话

monadify' :: forall m sig. (Monad m, Sig sig) => Monadify m sig

这里没有代理,所以,如果不假设 Monadify 单射,编译器不可能知道 m,sig 应该实例化成什么。这也是了解应该使用哪些实例 (Monad m, Sig sig) 所必需的。

尝试使用

monadify' :: forall m sig. (Monad m, Sig sig) 
          => Proxy m -> Proxy sig -> Monadify m sig

还要注意 Monadify 不是 单射的:

Monadify ((->) Bool) (IO Char)      ~ Bool -> IO Char
Monadify IO          (Bool -> Char) ~ Bool -> IO Char

如果您使用 AllowAmbiguousTypes,则以下内容不会进行类型检查:

test :: forall m sig. (Monad m, Sig sig)
     => Proxy sig -> Proxy m -> Monadify m sig
test t _ = monadify' t
-- Type variable m0 is ambiguous

但是我们可以通过传递显式类型参数来修复它 m:

test :: forall m sig. (Monad m, Sig sig)
     => Proxy sig -> Proxy m -> Monadify m sig
test t _ = monadify' @ m t

就个人而言,我会尝试删除所有代理并改用类型参数,因为我发现它更简洁,即使这需要不明确的类型。