我有数据结构(此处为B
和T
),其中包含monad作为类型参数(此处为m
)(它是Data.Binding.Simple
的简化形式)它在类(此处为Variables3
)中使用,其函数具有相同的monad类型。在使用数据的类的实例中,monad的类型参数(比如m)出现两次(这里是Variable3 (T m) m a
)。这个编译,但当我使用代码中的函数时,对于某些类型的参数(这里是test3
),我得到一个错误(could not deduce ... m ..m1
),表明编译器看到两次出现的类型变量不同的。
我找到了一个解决方案:使用不同的类型参数(比如m
和m1
)命名两个匹配项,并添加等价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!
这背后的规则是什么?这是编译器中的错误吗?
答案 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
的另一个实例!它不能继续尝试统一类型变量,因为这会改变类型检查器的状态(在那里没有回溯)。因此,我相信其他事情必须让它知道这种平等。
您发现的解决方案在任何情况下都是非常标准的,通常建议使用。