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

时间:2016-09-02 06:45:53

标签: haskell type-inference typeclass type-families

考虑代码段

{-# 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,在我的情况下修复所有问题。但我不知道使用该扩展的后果,而且我宁愿知道为什么我的例子没有进行类型检查。

我觉得这与统一者首先尝试统一Monadify m sig有关,从而推断它无法证明sigm s是相同的。虽然统一者只需要查看传递的参数就知道它们是相同的,所以这可能是AllowAmbiguousTypes帮助的地方。

1 个答案:

答案 0 :(得分:4)

问题在于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 不是 injective:

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

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