我得到这样的错误:
假设我有一个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
实例。
答案 0 :(得分:13)
MonadReader
类是使用FunctionalDependencies
扩展名定义的,它允许声明如
class Monad m => MonadReader r m | m -> r where
...
这意味着对于任何monad m
,r
由它唯一确定。因此,您不能拥有一个确定两种不同m
类型的monad 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
类型,因此组合性较差。
答案 1 :(得分:5)
可以使用一些扩展来完成,但重叠实例可能不是一个好主意。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverlappingInstances #-}
import Control.Monad.Reader
import Data.Functor.Identity
data A = A deriving Show
data B = B deriving Show
type SomeMonad a = ReaderT A (ReaderT B Identity) a
instance MonadReader a m => MonadReader a (ReaderT b m) where
ask = lift ask
local f mx = do
b <- ask
lift $ local f $ runReaderT mx b
main :: IO ()
main = do
let res = runIdentity $ flip runReaderT B $ flip runReaderT A $ do
a <- ask :: SomeMonad A
b <- ask :: SomeMonad B
return (a, b)
print res
毋庸置疑,只要有可能,使用ReaderT (A, B) IO
之类的东西会更好。