我一直试图与读者monad握手并遇到this tutorial。在其中,作者提出了这个例子:
example2 :: String -> String
example2 context = runReader (greet "James" >>= end) context
where
greet :: String -> Reader String String
greet name = do
greeting <- ask
return $ greeting ++ ", " ++ name
end :: String -> Reader String String
end input = do
isHello <- asks (== "Hello")
return $ input ++ if isHello then "!" else "."
我知道这是一个显示机制的简单例子,但我试图找出为什么它比做类似的事情更好:
example3 :: String -> String
example3 = end <*> (greet "James")
where
greet name input = input ++ ", " ++ name
end input = if input == "Hello" then (++ "!") else (++ ".")
答案 0 :(得分:6)
Reader
在实际代码中并不经常使用。正如您所观察到的,它并不比仅仅为您的函数传递额外的参数更好。但是,作为monad变换器的一部分,它是通过应用程序传递配置参数的绝佳方法。通常,这是通过向需要访问配置的任何函数添加MonadReader
约束来完成的。
这是尝试一个更现实世界的例子:
data Config = Config
{ databaseConnection :: Connection
, ... other configuration stuff
}
getUser :: (MonadReader Config m, MonadIO m) => UserKey -> m User
getUser x = do
db <- asks databaseConnection
.... fetch user from database using the connection
然后你的main
看起来像:
main :: IO ()
main = do
config <- .... create the configuration
user <- runReaderT (getUser (UserKey 42)) config
print user
答案 1 :(得分:1)
dfeuer,chi和user2297560就在"Reader
isn't often used by itself in real code"中。但值得注意的是,在问题的第二个片段和实际使用Reader
作为monad的内容之间几乎没有本质区别:函数函子只是Reader
而没有包装器和the Monad
and Applicative
instances for both of them are equivalent。顺便说一下,在高度多态代码 1 之外,使用函数Applicative
的典型动机是使代码更加无点。在这种情况下,温和是非常可取的。例如,就我自己的口味而言,这......
(&&) <$> isFoo <*> isBar
...很好(有时甚至可能比有点拼写读得更好),而这......
end <*> greet "James"
......只是令人困惑。
<强>脚注强>
例如,as Carl points out in a comment,它和相关实例在......中很有用。
[...]您在类型构造函数中具有多态性的代码,并且您的用例正在传入参数。例如,当使用透镜提供的多态类型时,可能会出现这种情况。 / p>