currying与partial application威力巨大,假设要解决一个领域设计里的问题:
module rec WeatherDemo =
type GenerateWeatherInfo = Location -> WeatherInfo
type Location = string
type LocationCode = int
type Temperature = float
type HtmlString = string
type WeatherInfo = HtmlString // Html doc
实际流程的设计可以是:
type GenerateWeatherInfoFlow =
GetLocationCode
-> GetWeather
-> RenderWeatherInfo
-> GenerateWeatherInfo
type GetLocationCode = Location -> LocationCode option
type GetWeather = LocationCode -> Temperature option
type RenderWeatherInfo = Location -> Temperature option -> WeatherInfo
GenerateWeatherInfoFlow其实展开就是:
type GenerateWeatherInfoFlow =
(Location -> LocationCode option)
-> (LocationCode -> Temperature option)
-> (Location -> Temperature option -> WeatherInfo)
-> Location -> WeatherInfo
但是抽象成不展开的样子有很多好处,设计领域的时候简洁清楚,代码类似设计文档,而且用领域里的语言描述,对于新进工程师也方便上手,一看就知道整个流程是什么样。
另外测试也方便,以前为了测试,很多依赖注入的东西都需要繁杂的mock,但是现在测试可以如下:
[<Fact>]
let ``should generate weather info success`` () =
generateWeatherInfo
(fun _ -> Some 1)
(fun _ -> Some -1.)
(fun x y -> sprintf "%s %f" x (y |> Option.get))
"shanghai"
|> should equal "shanghai -1"
主流程的实现可以是:
let generateWeatherInfo: GenerateWeatherInfoFlow =
fun getLocationCode
getWeather
renderWeatherInfo
location ->
location
|> getLocationCode
|> Option.bind getWeather
|> renderWeatherInfo location
一切按照定义好的图纸来,所以简单得像乐高积木一样,比如最后的组装可以是:
let generateWeatherInfoApi db mojiKey location =
generateWeatherInfo
(getLocationFromDb db)
(getWeatherFromMojitianqi mojiKey)
(simpleRenderWeatherInfo)
location
另外有人可能会疑惑(getLocationFromDb db),不是前面的定义是
type GetLocationCode = Location -> LocationCode option
么,没有db啊,妙的地方就在此:
let getLocationFromDb db: GetLocationCode =
fun location ->
(query {
for lc in db.LocationCodes do
where (lc.Location.Contains location)
select lc.Code
}).FirstOrDefault()
|> mapToOption
各个功能的实现可以是有副作用的如访问数据库getLocationFromDb,发http请求 getWeatherFromMojitianqi等,也可以是纯函数simpleRenderWeatherInfo,拼接简单的html字符串。我们可以组织好这些功能的实现,实现各种不同的组合形式来满足项目经理各种无理的需求变更。
当然如果你的流程很复杂,你可以设计很多子流程然后嵌入到主流程里面,这样可以减少一个函数需要的参数。
总之,个人经验感觉currying与partial application是functional programming里非常炫酷有用的东西,定义简单,方便测试,单一职责,灵活面对需求的更改。。更多的可以参见一本书“Domain Modeling Made Functional - Tackle Software Complexity with Domain-Driven Design and F#”。