Syd*_*ove 8 monads haskell monad-transformers
我得到这样的错误:
假设我有一个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’
Run Code Online (Sandbox Code Playgroud)
为什么Haskell无法确定使用哪个实例?另外,我该如何解决这个问题?假设put A和B相同的数据类型不是一个选项,因为我需要一个MonadReader A m实例.
bhe*_*ilr 13
所述MonadReader类被使用定义的FunctionalDependencies扩展,它允许声明等
class Monad m => MonadReader r m | m -> r where
...
Run Code Online (Sandbox Code Playgroud)
这意味着对于任何monad m,r它都由它唯一确定.因此,您不能拥有一个m确定两种不同r类型的monad .如果不将此作为限制,编译器将无法键入该类的检查用法.
解决这个问题的方法就是编写你的函数
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
Run Code Online (Sandbox Code Playgroud)
然后(A, B)在实际实现中使用元组:
baz :: Reader (A, B) (Int, String)
baz = do
a <- withReader fst foo
b <- withReader snd bar
return (a, b)
Run Code Online (Sandbox Code Playgroud)
还有一个withReaderT更复杂的案例.
作为不允许堆叠ReaderT的原因的一个例子,请考虑这种情况
type App = ReaderT Int (Reader Int)
Run Code Online (Sandbox Code Playgroud)
你打电话的时候ask,Int你指的是哪个?对于像这样的案件,似乎很明显
type App = ReaderT A (Reader B)
Run Code Online (Sandbox Code Playgroud)
编译器应该能够找出使用哪个,但问题是ask这里的函数会有类型
ask :: App ???
Run Code Online (Sandbox Code Playgroud)
哪里???可以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)
Run Code Online (Sandbox Code Playgroud)
但你只能拥有MonadReader A App,你也不能拥有MonadReader B App.这种方法可以称为"显式提升",它使这些功能特定于App类型,因此不太可组合.
它可以使用一些扩展来完成,但是重叠实例可能不是一个好主意.
{-# 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
Run Code Online (Sandbox Code Playgroud)
不用说,只要有可能,使用类似的东西会好得多ReaderT (A, B) IO.