读者莫纳德 - 琐碎案例的解释

时间:2018-03-26 16:02:30

标签: haskell reader-monad

我一直试图与读者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 (++ ".")

2 个答案:

答案 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"

......只是令人困惑。

<强>脚注

  1. 例如,as Carl points out in a comment,它和相关实例在......中很有用。

      

    [...]您在类型构造函数中具有多态性的代码,并且您的用例正在传入参数。例如,当使用透镜提供的多态类型时,可能会出现这种情况。 / p>