依赖注入的习惯Haskell解决方案是什么?
例如,假设你有一个接口frobby,你需要传递一个符合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已经教给你的所有废话。