这里的其他答案也许会教您如何在 F# 中实现 IO monad,这当然是一个选择。不过,在 F# 中,我经常只是将函数与其他函数组合。您不必定义“接口”或任何特定类型来执行此操作。
开发您的系统由外而内,并通过关注它们需要实现的行为来定义您的高级函数。使他们高阶函数通过传入依赖项作为参数。
需要查询数据存储?传入一个loadUser
争论。需要保存用户吗?传入一个saveUser
争论:
let myHighLevelFunction loadUser saveUser (userId) =
let user = loadUser (UserId userId)
match user with
| Some u ->
let u' = doSomethingInterestingWith u
saveUser u'
| None -> ()
The loadUser
参数被推断为类型User -> User option
, and saveUser
as User -> unit
, 因为doSomethingInterestingWith
是类型的函数User -> User
.
您现在可以“实施”loadUser
and saveUser
通过编写调用较低级别库的函数。
我对这种方法的典型反应是:这将要求我向我的函数传递太多参数!
事实上,如果发生这种情况,请考虑这是否是该函数试图做太多事情的味道。
自从依赖倒置原则 https://en.wikipedia.org/wiki/Dependency_inversion_principle这个问题的标题中提到了,我想指出的是坚实的原则 https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)如果所有这些都协同应用,效果最佳。这接口隔离原则 https://en.wikipedia.org/wiki/Interface_segregation_principle说接口应该尽可能小,并且你不会让它们比每个“接口”都是单个函数时更小。
有关描述此技术的更详细文章,您可以阅读我的类型驱动开发文章 http://blog.ploeh.dk/2015/08/10/type-driven-development.