2013-07-14 27 views
5

在F#中,許多采用序列的函數都將序列作爲支持流水線的最後一個參數。流水線與API設計的部分應用程序

當設計一個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) 

支持流水線:

myTransitions |> getNewState "A" "one" 

但是在某些情況下,序列是恆定的,而其他參數不同。在狀態機示例中,對於給定的狀態機,轉換表(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 

現在getNewState有簽名:

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

stateMachine有簽名:

(string -> string -> string option) 

我怎樣才能設計一個API來支持流水線和部分應用程序,在調用者的選擇?

+0

你'getNewState'是讓綁定功能,所以我不認爲有一個簡單的解決方案,你是什麼求。 (請參閱http://msdn.microsoft.com/en-us/library/dd233213.aspx。)一種解決方案可能是爲'getNewState'的參數定義一個新類型(例如記錄),以便無論變化如何,您都可以創建一條新記錄並將其傳遞給'getNewState'。或者您可以等待Petricek或Pappas提供權威答案。 – Shredderroy

回答

3

流水線使用部分應用程序,它只是通過先指定參數然後再指定函數來調用函數的另一種方法。

myTransitions |> getNewState "A" "one"

這裏getNewState首先部分地施加到得到一個函數與一個PARAM,然後將該函數調用myTransitions。

有一個函數可以有不同的參數順序,但函數名仍然保持不變的方法是使用方法重載,即具有靜態方法的類型,但然後你鬆開隱式的部分應用程序,因爲方法將參數作爲單個元組。

堅持一個簽名會更好,調用者可以根據需要輕鬆創建另一個具有不同參數順序的函數。例如,在你的第二個代碼示例,您可以使用第一個例子中的getNewState爲:

let stateMachine a b = getNewState a b myTransitions

2

爲什麼轉換需要改變它們不是形成狀態機的定義?

在任何情況下,如果你覺得敦促過渡是在最後的地方,但你仍然希望部分應用,你總是可以做,不只是一個函數:

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 

(或主叫方總是可以做到這一點對自己的情況下,你缺少一些簽名,他們需要)

+0

轉換不需要改變;正如你所說,他們定義了狀態機。 – TrueWill

+1

在這種情況下,我會推薦* not *來定義'transistions'是最後一個參數,就像Daniel說的那樣。一個給定轉換表的函數看起來更有意義(例如Daniel的'createStateMachine')。 – Frank