为什么我不能将两个阅读器堆叠在一起?
Why can't I stack two readers ontop of eachother?
我收到这样的错误:
假设我有一个 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
class 是使用 FunctionalDependencies
扩展定义的,它允许像
这样的声明
class Monad m => MonadReader r m | m -> r where
...
这意味着对于任何 monad m
,r
由它唯一确定。因此,您不能用一个 monad m
来确定两种不同的 r
类型。如果没有这个限制,编译器将无法对 class.
的使用进行类型检查
解决这个问题的方法是像这样编写你的函数
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
。这种方法可以称为 "explicit lifting",它使这些函数特定于 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
不用说,只要有可能,最好使用 ReaderT (A, B) IO
.
我收到这样的错误:
假设我有一个 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
class 是使用 FunctionalDependencies
扩展定义的,它允许像
class Monad m => MonadReader r m | m -> r where
...
这意味着对于任何 monad m
,r
由它唯一确定。因此,您不能用一个 monad m
来确定两种不同的 r
类型。如果没有这个限制,编译器将无法对 class.
解决这个问题的方法是像这样编写你的函数
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
。这种方法可以称为 "explicit lifting",它使这些函数特定于 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
不用说,只要有可能,最好使用 ReaderT (A, B) IO
.