2013-07-14 9 views
5

W F # wiele funkcji, które pobierają sekwencje, ma sekwencję jako ostatni parametr wspierający potokowanie.Pipelining a częściowa aplikacja do projektowania interfejsu API

Przy projektowaniu API, mogę śledzić ten trend, jak w tym prostym przykładzie machiny państwowej:

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 

Tu getNewState ma podpis:

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

który obsługuje przetwarzanie potokowe:

myTransitions |> getNewState "A" "one" 

Ale w niektórych przypadkach sekwencja jest stała, podczas gdy inne argumenty są różne. W przykładzie maszyny stanów, tabela przejściowa (transitions) zostanie ustalona dla danego komputera stanu. getNewState zostanie wywołany wiele razy z różnymi stanami i zdarzeniami. Jeśli sekwencja były parametr pierwszy dzwoniący może użyć częściowe zastosowanie:

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 

Teraz getNewState ma podpis:

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

i stateMachine ma podpis:

(string -> string -> string option) 

Jak mogę zaprojektować API do obsługi zarówno potokowej jak i częściowej aplikacji, w opcji dzwoniącego?

+0

Twój 'getNewState' jest funkcja zawód związany, więc nie sądzę, nie jest łatwym rozwiązaniem, kim jesteś szukanie. (Zobacz http://msdn.microsoft.com/en-us/library/dd233213.aspx.) Jednym z rozwiązań może być zdefiniowanie nowego typu - powiedzmy rekordu - dla argumentu 'getNewState', aby niezależnie od tego, co jest zmienne, możesz zbudować nowy rekord i przesłać go do 'getNewState'. Możesz też poczekać, aż Petricek lub Pappas udzielą autorytatywnej odpowiedzi. – Shredderroy

Odpowiedz

3

Pipelining wykorzystuje częściową aplikację, jest to po prostu inny sposób wywołania funkcji, określając najpierw parametr, a następnie funkcję.

myTransitions |> getNewState "A" "one"

Tutaj getNewState jest najpierw częściowo zastosowane, aby uzyskać funkcję z jednej param, a następnie, że funkcja jest wywoływana z myTransitions.

Sposób na zachowanie funkcji, która może mieć inną kolejność parametrów, ale nazwa funkcji pozostaje taka sama, polega na przeciążeniu metody, tzn. Na typie ze statycznymi metodami, ale wówczas tracimy niejawne częściowe zastosowanie, ponieważ metody przyjmują parametry jako pojedynczą krotkę .

Byłoby lepiej trzymać się jednego podpisu, a osoba dzwoniąca może łatwo utworzyć inną funkcję, która ma inny porządek według potrzeb.Na przykład w drugim przykładzie kodu można użyć pierwszym przykładem na getNewState jak:

let stateMachine a b = getNewState a b myTransitions

2

Dlaczego przejścia muszą się różnić, czy nie stanowią one samej definicji Państwa automatu?

W każdym razie, jeśli czujesz się pokusie, aby przejść się na ostatnim miejscu, ale nadal chcesz częściowo stosuje się, zawsze można zrobić funkcję, która właśnie to robi:

let getNewState currentState triggeringEvent transitions = 
    // your definition from above 
let createStateMachine transitions currentState triggeringEvent = 
    getNewState currentState triggeringEvent transitions) 

lub można zrobić ogólny rotateArgs funkcja i używać go zdefiniować specjalny podpis coś takiego:

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

(lub rozmówca zawsze może to zrobić dla siebie, w przypadku, gdy brakuje jakiś podpis, czego potrzebują)

+0

Przejścia nie powinny się różnić; jak mówisz, definiują automat stanów. – TrueWill

+1

W takim przypadku, polecam * nie *, aby zdefiniować "transistions" jako ostatni parametr, tak jak powiedział Daniel. Funkcja, która, biorąc pod uwagę tabelę przejściową, generuje automat stanów wydaje się mieć więcej sensu (np. "CreateStateMachine" Daniela). – Frank

Powiązane problemy