为什么在实例中出现两次参数会在以后使用类中的函数时产生错误?

时间:2016-03-28 20:26:50

标签: haskell

我有数据结构(此处为BT),其中包含monad作为类型参数(此处为m)(它是Data.Binding.Simple的简化形式)它在类(此处为Variables3)中使用,其函数具有相同的monad类型。在使用数据的类的实例中,monad的类型参数(比如m)出现两次(这里是Variable3 (T m) m a)。这个编译,但当我使用代码中的函数时,对于某些类型的参数(这里是test3),我得到一个错误(could not deduce ... m ..m1),表明编译器看到两次出现的类型变量不同的。

我找到了一个解决方案:使用不同的类型参数(比如mm1)命名两个匹配项,并添加等价m ~ m1(使用TypeFamilies扩展名)。编译并运行。

这里有一些非常简化的代码,它会产生test3

的错误
class (Monad m) => Variable3 v m a where  
 newVar3     :: a -> m (v a)
 readVar3    :: v a -> m a
 writeVar3   :: v a -> a -> m ()

data B a m = B {f1 :: a
            ,  f2 :: a -> m () }

data T m a = T {unT :: TVar (B a m)}   

instance (Variable3 TVar m (B a m)
       , MonadIO m
       ) => Variable3 (T m) m a  where
   newVar3 a = do 
                n <- newVar3 (B {f1 = a, f2 = \a -> return () })
                return (T n) 

   readVar3 a        = do 
                        v <- liftIO $ readTVarIO . unT $ a 
                        return . f1 $ v 

test3 :: ( MonadIO m 
    , Variable3 TVar m (B a m) 
        , Eq a) =>  [a] ->   m Bool
test3   [v1, v2]  = do
    n1 :: (T m1 a) <- newVar3  v1
    r1 <- readVar3 n1
    let b1 = r1 == v1
   return True `

将实例头替换为:

instance (Variable3 TVar m (B a m1)
        , MonadIO m
        , m ~ m1
        ) => Variable3 (T m1 ) m a  where

允许编译并运行test3!

这背后的规则是什么?这是编译器中的错误吗?

1 个答案:

答案 0 :(得分:5)

我没有给你一个完整的答案,但我知道的很多。

当GHC解析Variable3实例并看到

instance (Variable3 TVar m (B a m1)
         , MonadIO m
         , m ~ m1
         ) => Variable3 (T m1 ) m a

它检查第一个参数的格式是T m1。然后它提交到该实例并专门用于解决上下文。

当它看到

instance (Variable3 TVar m (B a m)
         , MonadIO m
         ) => Variable3 (T m) m a

它不会提交实例,除非它可以看到第一个参数是T应用于第二个参数。毕竟,你可以拥有Variable3 (T (MaybeT m)) m a的另一个实例!它不能继续尝试统一类型变量,因为这会改变类型检查器的状态(在那里没有回溯)。因此,我相信其他事情必须让它知道这种平等。

您发现的解决方案在任何情况下都是非常标准的,通常建议使用。