2012-12-15 26 views
3

我被一個看似微不足道的問題困住了:如果函數以無點的方式寫入,我無法處理函數中的異常。在免費函數中的異常處理

考慮這兩個功能:

let divide1 x y = 
    try 
     x/y 
    with 
     | :? System.DivideByZeroException -> 42 

let divide2 = 
    try 
     (/) 
    with 
     | :? System.DivideByZeroException -> fun _ _ -> 42 

let x1 = divide1 5 0 // works fine 
let x2 = divide2 5 0 // doesn't handle an exception 

雖然這兩個功能都看似相同,他們有不同的類型:

val divide1: int -> int -> int 
val divide2: (int -> int -> int) 

顯然,divide2甚至沒有試圖處理異常。它只是返回一個操作符。

我能做些什麼才能使divide2以適當的方式處理異常(除非明確聲明其參數)?

+4

嗯,這是預期的。你'嘗試'返回一個函數併成功。稍後調用此函數時會引發異常。我認爲F#社區是免費的? ;) –

+0

@羅伯特:直到最後一句,我還是那麼喜歡你。 ;-( – ildjarn

+0

@ildjarn:爲什麼?少點無點,當然,但我認爲我沒有看到過單點無點不會降低可讀性的問題。我們依賴於智能感知的額外減號並且param的名字都丟失了 –

回答

7

這是我發現無點式問題的原因之一。它使得難以使用像try .. with(或標準循環和其他F#功能)的標準語言結構,並且您需要用自定義組合器替換它們。在這種情況下,你可以定義組合子tryWith2,在異常處理程序封裝了兩個參數的函數:

let tryWith2 f h a b = 
    try f a b // Call the function with two arguments 
    with e -> 
    // Try running the error handler with the exception 
    match h e with 
    | Some g -> g a b // Error handler provided another function 
    | _ -> reraise() // Error was not handled - reraise 

然後,你可以寫在這樣一個自由點式功能(錯誤處理仍然是不可─點,免費的,但我不想讓這個太傻了:-))

let divide2 = 
    tryWith2 (/) (function 
     | :? System.DivideByZeroException -> Some(fun _ _ -> 42) 
     | _ -> None) 

let x1 = divide2 5 0 // returns 42 
let x2 = divide2 5 1 // returns 5 

當然,一點自由的風格是非常有用的,即使在F#。例如,在編寫DSL時,它是編寫聲明性規範的好方法(因爲基元使用更高級別的抽象表達某些內容)。在這裏,你需要表達一些非常接近正常F#代碼的東西,而且我相信這最好用正常的F#代碼來表示。

+0

這就是我所害怕的,異常的全部原因是能夠在任何我想要的地方處理它們,如果我必須立即傳遞一個處理器,它看起來比包裝一個' /)'結果到'Option'中? – bytebuster

3

你需要記住的是,在divide2中,你沒有返回X除以Y的結果,而是返回一個將X除以Y的函數。因爲let綁定的代碼正在被立即執行,因爲它沒有給出函數語法。讓我們來看看帶有較長的函數的語法都鴻溝綁定:

let divide1 = 
    fun x -> 
     fun y -> 
      try 
       x/y 
      with 
       | :? System.DivideByZeroException -> 42 

let divide2 = 
    try 
     fun x -> 
      fun y -> 
       x/y 
    with 
     | :? System.DivideByZeroException -> fun _ _ -> 42 

當顯示這種方式,它應該是更清楚這兩個定義如何不同。 try塊位於完全不同的位置,並在不同的時間點執行。

將邏輯(例如異常處理)添加到現有函數的唯一方法是將其封裝,或者按照您在divide1中所做的操作,或者使用Tomas顯示的更高順序函數。