Читатель Monad - объяснение тривиального случая
Я пытался справиться с монадой читателя и наткнулся на этот учебник. В нем автор представляет этот пример:
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 ответа
Reader
не часто используется сам по себе в реальном коде. Как вы заметили, на самом деле это не лучше, чем просто передать дополнительный аргумент вашим функциям. Однако, как часть монадного преобразователя, это отличный способ передать параметры конфигурации через ваше приложение. Обычно это делается путем добавления 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
dfeuer, chi и user2297560 правы в этом Reader
не часто используется сам по себе в реальном коде ". Однако стоит отметить, что между тем, что вы делаете во втором фрагменте вопроса, и тем, Reader
как монада: функтор функции просто Reader
без обертки, а Monad
а также Applicative
экземпляры для них обоих эквивалентны. Кстати, вне высокополиморфного кода1, типичная мотивация для использования функции Applicative
делает код более бессмысленным. В этом случае умеренность очень рекомендуется. Например, что касается моего собственного вкуса, это...
(&&) <$> isFoo <*> isBar
... хорошо (а иногда это может даже читать лучше, чем многозначительное написание), в то время как это...
end <*> greet "James"
... просто сбивает с толку.
Сноски
Например, как указывает Карл в комментарии, он и связанные с ним экземпляры могут быть полезны в...
[...] места, где у вас есть код, который полиморфен в конструкторе типов, и ваш вариант использования передает аргумент. Это может возникнуть, например, при использовании полиморфных типов, предлагаемых линзами.