Tru*_*ill 5 f# functional-programming pipeline
在F#中,许多采用序列的函数都将序列作为支持流水线操作的最后一个参数.
在设计API时,我可以遵循这种趋势,就像在这个简单的状态机示例中一样:
type Transition =
{ CurrentState : string; TriggeringEvent : string; NewState : string }
let getNewState currentState triggeringEvent transitions =
let isMatch t =
t.CurrentState = currentState
&& t.TriggeringEvent = triggeringEvent
match transitions |> Seq.tryFind isMatch with
| Some transition -> Some(transition.NewState)
| None -> None
let myTransitions =
[ { CurrentState = "A"; TriggeringEvent = "one"; NewState = "B" };
{ CurrentState = "B"; TriggeringEvent = "two"; NewState = "A" } ]
let result = myTransitions |> getNewState "A" "one"
printfn "%A" result
Run Code Online (Sandbox Code Playgroud)
这里getNewState有签名:
(string -> string -> seq<Transition> -> string option)
Run Code Online (Sandbox Code Playgroud)
它支持流水线操作:
myTransitions |> getNewState "A" "one"
Run Code Online (Sandbox Code Playgroud)
但在某些情况下,序列是不变的,而其他参数则不同.在状态机示例中,转换表(transitions)将针对给定的状态机进行修复.getNewState将使用不同的状态和事件多次调用.如果序列是第一个参数,则调用者可以使用部分应用程序:
let getNewState transitions currentState triggeringEvent =
// body same as before
let stateMachine = getNewState myTransitions
let result1 = stateMachine "A" "one"
let result2 = stateMachine "B" "two"
printfn "%A" result1
printfn "%A" result2
Run Code Online (Sandbox Code Playgroud)
现在getNewState签名:
(seq<Transition> -> string -> string -> string option)
Run Code Online (Sandbox Code Playgroud)
并stateMachine签名:
(string -> string -> string option)
Run Code Online (Sandbox Code Playgroud)
如何在调用者选项中设计API以支持流水线操作和部分应用程序?
流水线使用部分应用,它只是先指定参数,然后指定函数来调用函数的另一种方式。
myTransitions |> getNewState "A" "one"
这里getNewState首先部分应用以获取具有一个参数的函数,然后使用 myTransitions 调用该函数。
拥有一个可以具有不同参数顺序但函数名称仍然保持不变的函数的方法是使用方法重载,即具有带有静态方法的类型,但随后您会失去隐式部分应用程序,因为方法将参数作为单个元组。
最好坚持使用一个签名,调用者可以轻松创建另一个根据需要具有不同参数顺序的函数。例如,在第二个代码示例中,您可以将第一个示例用作getNewState:
let stateMachine a b = getNewState a b myTransitions