Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 管道与API设计的部分应用_F#_Functional Programming_Pipeline - Fatal编程技术网

F# 管道与API设计的部分应用

F# 管道与API设计的部分应用,f#,functional-programming,pipeline,F#,Functional Programming,Pipeline,在F#中,许多采用序列的函数都将序列作为支持流水线的最后一个参数 在设计API时,我可以遵循这一趋势,如下面这个简单的状态机示例: type Transition = { CurrentState : string; TriggeringEvent : string; NewState : string } let getNewState currentState triggeringEvent transitions = let isMatch t = t.C

在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
此处
getNewState
有签名:

(string -> string -> seq<Transition> -> string option)
(seq<Transition> -> string -> string -> string option)
(string -> string -> string option)
但在某些情况下,序列是恒定的,而其他参数则不同。在状态机示例中,给定状态机的转换表(
转换
)是固定的
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
现在
getNewState
具有签名:

(string -> string -> seq<Transition> -> string option)
(seq<Transition> -> string -> string -> string option)
(string -> string -> string option)

如何根据调用方的选择设计一个既支持流水线又支持部分应用程序的API?

为什么转换需要变化?它们不构成状态机的定义吗

在任何情况下,如果您觉得过渡必须在最后一个位置,但您仍然希望部分应用,那么您始终可以创建一个函数来实现这一点:

let getNewState currentState triggeringEvent transitions = 
    // your definition from above
let createStateMachine transitions currentState triggeringEvent =
    getNewState currentState triggeringEvent transitions)
或者,您可以创建一个通用的
rotateArgs
函数,并使用它定义您的特殊签名,如下所示:

let rotateArgs f z x y = f x y z
let createStateMachine = rotateArgs getNewState

(或者调用者可以自己做,以防您丢失他们需要的签名)

管道使用部分应用程序,这只是通过先指定参数然后指定函数来调用函数的另一种方法

myTransitions>getNewState“A”“one”

这里首先部分应用
getNewState
来获取一个带有一个参数的函数,然后使用myTransitions调用该函数

使函数具有不同的参数顺序但函数名仍然保持不变的方法是使用方法重载,即使用具有静态方法的类型,但由于方法将参数作为一个元组,因此会松开隐式部分应用程序

最好坚持使用一个签名,调用方可以根据需要轻松创建另一个具有不同参数顺序的函数。例如,在第二个代码示例中,您可以使用第一个示例的
getNewState
作为:


let stateMachine a b=getNewState a b myTransitions

您的
getNewState
是一个let绑定函数,因此我认为没有一个简单的解决方案可以满足您的要求。(请参阅。)一种解决方案可能是为
getNewState
的参数定义一个新类型(例如,记录),这样无论发生什么变化,您都可以构建一个新记录并将其通过管道传输到
getNewState
。或者你可以等待Petricek或Pappas提供权威的答案;正如你所说,它们定义了状态机。在这种情况下,我建议不要像Daniel所说的那样,将
传输定义为最后一个参数。给定一个转换表,生成状态机的函数似乎更有意义(例如Daniel的
createStateMachine
)。