设计 – Haskell中的依赖注入:习惯性地解决任务

依赖注入的习惯Haskell解决方案是什么?

例如,假设你有一个接口f​​robby,你需要传递一个符合frobby的实例(可能有这些实例的多个变种,说,foo和bar)。

典型操作是:

>取一些值X并返回一些值Y的函数。例如,这可能是一个数据库访问器,采用SQL查询&连接器并返回数据集。你可能需要实现postgres,MysqL一个模拟测试系统。
>获取一些值Z并返回与Z相关的闭包的函数,专用于在运行时选择的给定foo或bar样式。

一个解决了这个问题,如下:

http://mikehadlow.blogspot.com/2011/05/dependency-injection-haskell-style.html

但我不知道这是否是管理这项任务的规范方式。

我认为这里的正确答案是,我可能会收到几个downVote只是这样说:忘记术语依赖注入。把它忘了吧。这是一个来自OO世界的时尚流行词,但没有更多。

让我们来解决真正的问题。请记住,你正在解决一个问题,这个问题是手头的特定编程任务。不要让你的问题“实现依赖注入”。

我们将以一个记录器为例,因为这是许多程序想要拥有的基本功能,并且有很多不同类型的记录器:一个记录到stderr,一个记录到一个文件一个数据库,和一个根本不做任何事情。要统一它们所有你想要的类型:

type Logger m = String -> m ()

您还可以选择一个鸽友类型来保存一些击键:

class PrettyPrint a where
    pretty :: a -> String

type Logger m = forall a. (PrettyPrint a) => a -> m ()

现在让我们使用后者的变体定义几个记录器:

noLogger :: (Monad m) => Logger m
noLogger _ = return ()

stderrLogger :: (Monadio m) => Logger m
stderrLogger x = liftIO . hPutStrLn stderr $ pretty x

fileLogger :: (Monadio m) => FilePath -> Logger m
fileLogger logF x =
    liftIO . withFile logF AppendMode $ \h ->
        hPutStrLn h (pretty x)

acidLogger :: (Monadio m) => AcidState MyDB -> Logger m
acidLogger db x = update' db . AddLogLine $ pretty x

您可以看到这是如何构建依赖关系的图。 acidLogger依赖于MyDB数据库布局的数据库连接。将参数传递给函数是关于在程序中表达依赖性的最自然的方式。毕竟一个函数只是一个取决于另一个值的值。行动也是如此。如果你的行为取决于一个记录器,那么自然它是记录器的一个功能

printFile :: (Monadio m) => Logger m -> FilePath -> m ()
printFile log fp = do
    log ("Printing file: " ++ fp)
    liftIO (readFile fp >>= putStr)
    log "Done printing."

看看这是多么容易?在某些时候,这让你意识到你的生活会多么容易,当你只是忘记了OO已经教给你的所有废话。

相关文章

迭代器模式(Iterator)迭代器模式(Iterator)[Cursor]意图...
高性能IO模型浅析服务器端编程经常需要构造高性能的IO模型,...
策略模式(Strategy)策略模式(Strategy)[Policy]意图:定...
访问者模式(Visitor)访问者模式(Visitor)意图:表示一个...
命令模式(Command)命令模式(Command)[Action/Transactio...
生成器模式(Builder)生成器模式(Builder)意图:将一个对...