.
Tek yön compensating transactions'u kullanmaktır.
Bu yaklaşımda, Success
kasası "geri alma" işlevlerinin bir listesini içerir. Geri alınamayan her adım bu listeye bir işlev ekler. Herhangi bir adım başarısız olduğunda, listedeki her bir geri alma işlevi yürütülür (ters sırada).
Elbette bunu yapmak için daha karmaşık yollar vardır (ör. Çökmeler durumunda geri alma işlevlerini kalıcı olarak saklamak, veya this kind of thing). İşte
bu yaklaşımı gösteren bazı kod:
/// ROP design with compensating transactions
module RopWithUndo =
type Undo = unit -> unit
type Result<'success> =
| Success of 'success * Undo list
| Failure of string
let bind f x =
match x with
| Failure e -> Failure e
| Success (s1,undoList1) ->
match f s1 with
| Failure e ->
// undo everything in reverse order
undoList1 |> List.rev |> List.iter (fun undo -> undo())
// return the error
Failure e
| Success (s2,undoList2) ->
// concatenate the undo lists
Success (s2, undoList1 @ undoList2)
/// Example
module LaunchWithUndo =
open RopWithUndo
let undo_refuel() =
printfn "undoing refuel"
let refuel ok =
if ok then
printfn "doing refuel"
Success ("refuel", [undo_refuel])
else
Failure "refuel failed"
let undo_enterLaunchCodes() =
printfn "undoing enterLaunchCodes"
let enterLaunchCodes ok refuelInfo =
if ok then
printfn "doing enterLaunchCodes"
Success ("enterLaunchCodes", [undo_enterLaunchCodes])
else
Failure "enterLaunchCodes failed"
let fireMissile ok launchCodesInfo =
if ok then
printfn "doing fireMissile "
Success ("fireMissile ", [])
else
Failure "fireMissile failed"
// test with failure at refuel
refuel false
|> bind (enterLaunchCodes true)
|> bind (fireMissile true)
(*
val it : Result<string> = Failure "refuel failed"
*)
// test with failure at enterLaunchCodes
refuel true
|> bind (enterLaunchCodes false)
|> bind (fireMissile true)
(*
doing refuel
undoing refuel
val it : Result<string> = Failure "enterLaunchCodes failed"
*)
// test with failure at fireMissile
refuel true
|> bind (enterLaunchCodes true)
|> bind (fireMissile false)
(*
doing refuel
doing enterLaunchCodes
undoing enterLaunchCodes
undoing refuel
val it : Result<string> = Failure "fireMissile failed"
*)
// test with no failure
refuel true
|> bind (enterLaunchCodes true)
|> bind (fireMissile true)
(*
doing refuel
doing enterLaunchCodes
doing fireMissile
val it : Result<string> =
Success ("fireMissile ",[..functions..])
*)
her sonuçları geri alınamaz, ikinci bir seçenek, tüm her adımda geri dönüşü olmayan şeyleri yapmaya fakat geciktirmek için değil tüm adımlar tamamlanana kadar geri dönüşü olmayan bitler.
Bu yaklaşımda, Success
kasası "execute" işlevlerinin bir listesini içerir. Başarılı olan her adım bu listeye bir işlev ekler. En sonunda, tüm işlevler listesi yürütülür.
olumsuz
(! Ayrıca monadik çok olanlar zincirlemek rağmen) işlenen bir kez, tüm fonksiyonlar çalıştırılır olmasıdır Bu temelde tercüman desen çok kaba bir versiyonu.
İşte bu yaklaşımı gösteren bazı kod:
/// ROP design with delayed executions
module RopWithExec =
type Execute = unit -> unit
type Result<'success> =
| Success of 'success * Execute list
| Failure of string
let bind f x =
match x with
| Failure e -> Failure e
| Success (s1,execList1) ->
match f s1 with
| Failure e ->
// return the error
Failure e
| Success (s2,execList2) ->
// concatenate the exec lists
Success (s2, execList1 @ execList2)
let execute x =
match x with
| Failure e ->
Failure e
| Success (s,execList) ->
execList |> List.iter (fun exec -> exec())
Success (s,[])
/// Example
module LaunchWithExec =
open RopWithExec
let exec_refuel() =
printfn "refuel"
let refuel ok =
if ok then
printfn "checking if refuelling can be done"
Success ("refuel", [exec_refuel])
else
Failure "refuel failed"
let exec_enterLaunchCodes() =
printfn "entering launch codes"
let enterLaunchCodes ok refuelInfo =
if ok then
printfn "checking if launch codes can be entered"
Success ("enterLaunchCodes", [exec_enterLaunchCodes])
else
Failure "enterLaunchCodes failed"
let exec_fireMissile() =
printfn "firing missile"
let fireMissile ok launchCodesInfo =
if ok then
printfn "checking if missile can be fired"
Success ("fireMissile ", [exec_fireMissile])
else
Failure "fireMissile failed"
// test with failure at refuel
refuel false
|> bind (enterLaunchCodes true)
|> bind (fireMissile true)
|> execute
(*
val it : Result<string> = Failure "refuel failed"
*)
// test with failure at enterLaunchCodes
refuel true
|> bind (enterLaunchCodes false)
|> bind (fireMissile true)
|> execute
(*
checking if refuelling can be done
val it : Result<string> = Failure "enterLaunchCodes failed"
*)
// test with failure at fireMissile
refuel true
|> bind (enterLaunchCodes true)
|> bind (fireMissile false)
|> execute
(*
checking if refuelling can be done
checking if launch codes can be entered
val it : Result<string> = Failure "fireMissile failed"
*)
// test with no failure
refuel true
|> bind (enterLaunchCodes true)
|> bind (fireMissile true)
|> execute
(*
checking if refuelling can be done
checking if launch codes can be entered
checking if missile can be fired
refuel
entering launch codes
firing missile
val it : Result<string> = Success ("fireMissile ",[])
*)
Sen fikir, umarım. Eminim başka yaklaşımlar da vardır - bunlar açık ve basit olan ikisi. :)
Sonuçlar yine de geri dönmek için yeterli olur mu? Muhtemelen gerçek geri alma işlemlerini bir araya getiren bir sonuç türü için giderim, bu nedenle herhangi bir adımda değer bir başarı/başarısızlık (şimdi olduğu gibi) ve bir geri alma işlevidir() ->() 'dir. –
Kötü için bunu işlevsel bir soru olarak sordun. Prolog'u kullanarak veya [inferencing] (https://en.wikipedia.org/wiki/Inference_engine) anında akla geliyor. F # ile çıkarımı uygulayabilirsiniz ve harika çalışıyor. –
Ganesh: Hmm bu ilginç bir fikir. Etrafta bir oyun oynayacağım ve nasıl çalıştığını göreceğim! – Oenotria