如何从需要基础结构功能各种子集的组件中集成应用程序?
有些会非常简单,只需要配置读取器(我只想为每个业务功能公开一个相关的子集),也许还需要一个记录器。有些还需要与外部服务(具有缓存)的连接性,在这里我还想公开与外部世界进行有限范围的可能交互。
我不想处理将多个参数显式传递给此类函数,也不希望将它们包装在能够执行所有操作的某些MonadIO
中。
与在Java应用程序容器中注入多个依赖关系最接近的是什么?
最佳答案
mtl库具有类型类,用于表示从环境MonadReader
读取配置并将数据写入记录器MonadWriter
之类的计算。我们将这些作为示例。
我们将使用的MonadReader
部分是
class Monad m => MonadReader r m | m -> r where
ask :: m r
我们将使用的
MonadWriter
部分是class (Monoid w, Monad m) => MonadWriter w m | m -> w where Source
tell :: w -> m ()
要要求“多个依赖项”,我们将需要一个
m
,它为多个类型类提供实例。样板
最终,我们将使用transformers中的
ReaderT
和WriterT
和Identity
来运行示例。{-# LANGUAGE FlexibleContexts #-}
--mtl
import Control.Monad.Reader.Class
import Control.Monad.Writer.Class
--transformers
import Control.Monad.Trans.Reader hiding (ask)
import Control.Monad.Trans.Writer.Strict hiding (tell)
import Data.Functor.Identity
使用多个依赖项:读取配置和日志记录
我们的读者将在以下环境中阅读;它将提供一个
MonadReader Configuration
。data Configuration = Config { site :: String }
deriving Show
我们的记录器将累积一条消息列表,每条消息都是一个字符串。它将提供一个
MonadWriter [String]
。您可以通过要求多个类型类的实例来要求多种功能。为此,您可能需要一个具有
m
和MonadReader
实例的单个MonadWriter
。这是一个组件,需要一个环境来读取配置以及一种写入日志消息的方式。logConfig :: (MonadWriter String m, MonadReader Configuration m) => m ()
logConfig = do
config <- ask
tell [show config]
提供与转换器的多个依赖关系
我们可以提供必要的
m
而无需触摸IO
。转换器的ReaderT
增加了从环境读取Monad
的能力;我们将使用它来提供MonadReader Configuration
。变压器的WriterT
增加了将输出累加到Monad
的能力;我们将使用它来提供MonadWriter [String]
。对于底层的Monad
,我们将仅使用Identity
来表明我们没有弄乱IO
。以下提供了MonadReader Configuration
和MonadWriter [String]
,并在不使用IO
的情况下运行计算。type DepsIdentity = ReaderT Configuration (WriterT [String] Identity)
runDepsIdentity :: DepsIdentity a -> Configuration -> (a, [String])
runDepsIdentity ma = runIdentity . runWriterT . runReaderT ma
运行一个例子
我们将在另一个更大的示例中使用之前的
logConfig
:example :: (MonadWriter [String] m, MonadReader Configuration m) => m ()
example = do
tell ["Starting", "Logging Config"]
logConfig
tell ["Done Logging Config", "Done"]
最后,我们将使用
example
运行Configuration
并查看其作用。请注意,此处的IO
仅用于输出最终结果,以运行示例。main :: IO ()
main = print . runDepsIdentity example $ Config {site = "StackOverflow"}
这将产生以下输出
((),["Starting","Logging Config","Config {site = \"StackOverflow\"}","Done Logging Config","Done"])