我收到这样的错误:

假设我有一个monadStack ReaderT A (ReaderT B m),每当我使用askasks时,都会出现如下错误:

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为何不知道要使用哪个实例?另外,我该如何解决呢?
假设不能将AB放在相同的数据类型中,因为我需要MonadReader A m实例。

最佳答案

MonadReader类是使用FunctionalDependencies扩展名定义的,该扩展名允许以下声明:

class Monad m => MonadReader r m | m -> r where
    ...

这意味着对于任何monad mr由它唯一确定。因此,您不能使用一个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 ???

其中???可以是AB。您可以通过不直接使用MonadReader并定义特定的askAaskB函数来解决此问题:
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类型,因此组合性较差。

10-06 02:45