In F#, many functions that take sequences have the sequence as the last parameter to support pipelining.
When designing an API, I can follow that trend, as in this simple state machine example:
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
Here getNewState
has signature:
(string -> string -> seq<Transition> -> string option)
which supports pipelining:
myTransitions |> getNewState "A" "one"
But in some cases, the sequence is constant while the other arguments vary. In the state machine example, the transition table (transitions
) will be fixed for a given state machine. getNewState
will be called multiple times with different states and events. If the sequence were the first parameter, callers could use partial application:
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
Now getNewState
has signature:
(seq<Transition> -> string -> string -> string option)
and stateMachine
has signature:
(string -> string -> string option)
How can I design an API to support both pipelining and partial application, at the caller's option?
Pipelining uses partial application, it is just another way to call a function by specifying the parameter first and then the function.
myTransitions |> getNewState "A" "one"
Here getNewState
is first partially applied to get a function with one param and then that function is called with myTransitions.
The way to have a function that can have different parameter order but the function name still remains same is using method overloading i.e have a type with static methods but then you loose the implicit partial application because methods takes parameters as a single tuple.
It would be better to stick with one signature and the caller can easily create another function which has different parameter order as required. For example in your second code example you can use the first example's getNewState
as:
let stateMachine a b = getNewState a b myTransitions
Why would the transitions need to vary don't they form the very definition of your state machine?
In any case, if you feel the urge that the transitions be at the last place but you still want partially apply, you could always make a function that does just that:
let getNewState currentState triggeringEvent transitions =
// your definition from above
let createStateMachine transitions currentState triggeringEvent =
getNewState currentState triggeringEvent transitions)
or you could make a general rotateArgs
function and use it to define your special signature something like this:
let rotateArgs f z x y = f x y z
let createStateMachine = rotateArgs getNewState
(Or the caller can always do it for himself in case you are missing some signature they need)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With