2015-04-24 16 views
7

Tôi quen thuộc với thực tế rằng trong F # không có từ khóa "trả lại" tương đương.Cách triển khai logic "trả về sớm" trong F #

Tuy nhiên, chúng tôi đã gặp một vấn đề gần đây, nơi chúng tôi cần một quy trình làm việc bao gồm nhiều bước, trong đó mỗi bước có thể trả lại kết quả tốt hoặc xấu. Nếu có kết quả xấu trong bất kỳ bước nào, chúng tôi muốn thoát khỏi luồng công việc - và thoát sớm!

Chúng tôi đã giải quyết vấn đề này bằng cách kiểm tra có hiệu quả Lỗi trong mỗi bước (tức là chức năng) nhưng tôi không cảm thấy đây là cách chính xác để thực hiện - không hiệu quả và chúng tôi không thoát sớm.

Một chức năng mẫu trong công việc là như sau:

let StepB stepAResult someParameters = 
    match stepAResult with 
    | Good(validResourceData) -> 
     // Do current step processing 
     // Return current step result 
    | Error(error) -> error |> Result.Error 

Các công việc chính nó là như sau:

let Workflow someParameters = 
    let stepAResult = StepA someParameters 
    let stepBResult = StepB stepAResult someParameters 
    let stepCResult = StepC stepBResult someParameters 
    let stepDResult = StepD stepCResult someParameters 
    stepDResult 

Vì vậy, mỗi chức năng mẫu sẽ mất trong kết quả của hàm trước và chỉ thực hiện bước hiện tại nếu không có Lỗi!

Vấn đề tôi gặp phải là nếu StepA không thành công với Lỗi, mọi bước khác vẫn được gọi.

Có cách "hoạt động" có chức năng "trả lại sớm" thay vì gọi mọi chức năng trong quy trình làm việc, tại đó chúng tôi phải kiểm tra từng lần cho Lỗi không?

+5

Xin vui lòng đọc này: http://fsharpforfunandprofit.com/posts/recipe-part2/ –

Trả lời

9

Bạn viết các hàm của mình theo giả định, tất cả đều diễn ra tốt đẹp như bạn đã làm. Sau đó, bạn unwrap trường hợp hạnh phúc và tiếp tục với trường hợp hạnh phúc.

Và cuối cùng, bạn có thể sử dụng trình tạo để làm cho cú pháp đẹp.

type Result<'TSuccess, 'TError> = 
    | Success of 'TSuccess 
    | Error of 'TError 

type ResultBuilder() = 
    member this.Bind(v, f) = 
     match v with 
     | Success v -> f v 
     | Error e -> Error e 

    member this.Return value = Success value 

let result = ResultBuilder() 

let bla<'a> = result { 
    let! successOne = Success 1 
    let! successTwo = Success 2 
    let! failure = Error "after this, the computation exited" 
    failwith "Boom, won't occurr" 
    return successOne + successTwo } 
+0

Đề xuất này là nhiều hơn hoặc ít hơn những gì chúng tôi đã đi với, tuy nhiên nó có nghĩa là bạn không "trở lại sớm". Mỗi chức năng trong quy trình làm việc phải "kiểm tra" hiệu quả đối với trường hợp lỗi. Cảm ơn bạn đã làm rõ điều này! – bstack

3

Đây là biểu thức tính toán.

Biểu thức tính toán cung cấp đường cú pháp đẹp để làm những gì được gọi là thành phần monadic, trong đó giá trị kết quả trước đó được kiểm tra tự động trước khi bước tiếp theo được thực thi.

Gần đây tôi đã nói về khái niệm này - nó được đăng trên youtube tại https://www.youtube.com/watch?v=gNNTuN6wWVc; và @scottwlaschin có phần giới thiệu chi tiết về nó tại http://fsharpforfunandprofit.com/series/computation-expressions.html

Ping tôi trên twitter nếu bạn muốn được trợ giúp thêm!

+0

Nhưng điều này không có nghĩa là chúng ta phải kiểm tra tại mỗi bước? Nếu bước A thất bại với một lỗi, tôi muốn thoát ra, tôi không muốn phải kiểm tra điều này trong bước B, C và D? – bstack

+1

Bạn có ý nghĩa gì với "thoát"? * Nếu điều đó có nghĩa là bạn không thực hiện bất kỳ chức năng nào trong số các chức năng tiếp theo trong luồng công việc, đó chính xác là những gì xảy ra trong quy trình làm việc. * Nếu điều đó có nghĩa là bạn không thực hiện thêm bất kỳ kiểm tra giá trị kết quả nào, nó thực sự không thể được thực hiện theo cách thông thường .. Khái niệm "thoát" là một nôn nao từ một phong cách suy nghĩ rất bắt buộc - bạn 'd được phục vụ tốt hơn bằng cách sử dụng khái niệm về giá trị bao gồm chuỗi nhiều chức năng một cách an toàn ... –

+1

Ngoài ra, bạn không phải tự viết mã kiểm tra - bạn chỉ cung cấp các chức năng tham gia vào quy trình làm việc trong " thành công "trường hợp, và cơ chế chức năng nâng những chức năng để áp dụng các mã kiểm tra cho bạn ... vì vậy bạn không thực sự phải lo lắng về code-bloat hoặc các vấn đề bảo trì bạn nhận được trong mã mệnh lệnh! –

3

Các câu trả lời khác là tuyệt vời, biểu thức tính toán sẽ là hoàn hảo cho việc này.

Chỉ cần cung cấp một tùy chọn khác, cần lưu ý rằng có một vài trường hợp đặc biệt trong cấu trúc mã F # cho phép câu chuyện "trở về sớm" ít đau đớn hơn.

Định dạng thụt lề dựa trên kinh điển có thể là đống lộn xộn này:

let step1 = ... 
if failed step1 then 
    () // bail out 
else 
    let step2 = ... 
    if failed step2 then 
     () 
    else 
     let step3 = ... 
     if failed step3 then 
      () 
     else 
      let step4 = ... 
      ... 

Hai formattings thay thế dưới đây. Họ trông kỳ lạ, nhưng thực sự khá tiện dụng.

let step1 = ... 
if failed step1 then 
    () // bail out 
else 

let step2 = ... 
if failed step2 then 
    () 
else 

let step3 = ... 
if failed step3 then 
    () 
else 

let step4 = ... 
... 

hoặc

let step1 = ... 
if failed step1 then() else 

let step2 = ... 
if failed step2 then() else 

let step3 = ... 
if failed step3 then() else 

let step4 = ... 
... 
0

Daniel trả lời là cú pháp đường để tiếp cận phong cách tiếp tục. Đây là phiên bản de-sugared:

let step1 parm cont = 
    if true then cont 42 else None 
let step2 parm cont = 
    if false then cont 69 else None 
let conti parm = 
    step1 parm (fun result1 -> 
    step2 parm (fun result2 -> 
    Some(result1 + result2))) 
Các vấn đề liên quan