我收到这样的错误:
假设我有一个monadStack ReaderT A (ReaderT B m)
,每当我使用ask
或asks
时,都会出现如下错误:
Types.hs:21:10:
Couldn't match type ‘A’ with ‘B’
arising from a functional dependency between:
constraint ‘MonadReader B m’
arising from the instance declaration
instance ‘MonadReader A m2’ at Types.hs:21:10-63
In the instance declaration for ‘MonadReader A m’
Haskell为何不知道要使用哪个实例?另外,我该如何解决呢?
假设不能将
A
和B
放在相同的数据类型中,因为我需要MonadReader A m
实例。 最佳答案
MonadReader
类是使用FunctionalDependencies
扩展名定义的,该扩展名允许以下声明:
class Monad m => MonadReader r m | m -> r where
...
这意味着对于任何monad
m
,r
由它唯一确定。因此,您不能使用一个monad m
来确定两种不同的r
类型。没有这个限制,编译器将无法键入该类的检查用法。解决方案是将您的函数编写为
getA'sInt :: A -> Int
getA'sInt = undefined
getB'sString :: B -> String
getB'sString = undefined
foo :: (MonadReader A m) => m Int
foo = do
a <- asks getA'sInt
return $ a + 1
bar :: (MonadReader B m) => m String
bar = do
b <- asks getB'sString
return $ map toUpper b
然后只需在实际实现中使用元组
(A, B)
即可:baz :: Reader (A, B) (Int, String)
baz = do
a <- withReader fst foo
b <- withReader snd bar
return (a, b)
对于更复杂的情况,还有一个
withReaderT
。作为为什么不允许堆栈
ReaderT
的示例,请考虑以下情况type App = ReaderT Int (Reader Int)
当您调用
ask
时,您指的是哪个Int
?对于像这样的情况,似乎很明显type App = ReaderT A (Reader B)
编译器应该能够弄清楚使用哪个,但是问题是这里的
ask
函数将具有以下类型:ask :: App ???
其中
???
可以是A
或B
。您可以通过不直接使用MonadReader
并定义特定的askA
和askB
函数来解决此问题:type App = ReaderT A (Reader B)
askA :: App A
askA = ask
askB :: App B
askB = lift ask
baz :: App (Int, String)
baz = do
a <- askA
b <- askB
return (getA'sInt a, getB'sString b)
但是您只能拥有
MonadReader A App
,也不能拥有MonadReader B App
。这种方法可以称为“显式提升”,它使这些函数特定于App
类型,因此组合性较差。