内容简介:當我們要根據使用者輸入的C# 7.2F# 4.1
當我們要根據使用者輸入的 OrderId 到資料庫搜尋 訂單資料 ,若找的到就回傳該筆訂單,若搜尋不到呢?一般而言有兩種處理方式,傳回 null 或拋出 exception,但實務上常會因為忘記處理 null 或 exception,而在 run-time 得到 NullReferenceException ,這種常見的錯誤,是否能在 compile-time 獲得解決呢?
Version
C# 7.2
F# 4.1
Null
var order = Order.GetById(10);
Console.WriteLine("{0}", order.Name));
getById() 會根據 user 輸入的 OrderId ,到資料庫搜尋 訂單資料 ,junior 常會這樣寫程式。
這樣的寫法乍看之下沒問題,但 code review 時一定會被 sernior 問:
Q:若找不到訂單資料而回傳 null 該怎麼辦?
var order = Order.GetById(10);
if (order == null)
{
Console.WriteLine("No orders found.");
}
else
{
Console.WriteLine("{0}", order.Name));
}
Junior 會乖乖的補上 null 判斷,如此才不會在 run-time 得到 NullReferenceException 。
若使用 null 代表 找不到資料 ,會有幾個問題:
- 必須額外加上
null判斷,否則在 run-time 會得到NullReferenceException - 因為
null也是Order型別,所以 compiler 對於null檢查無能為力,只能靠 developer 自己的細心,或靠 unit test 的 coverage 完整加以保護 - 每個 function 都可能傳回
null,是否每個 function 都必須判斷null?如此 code 會變得很髒
Exception
Q: null 的確不好,所以若找不到訂單資料就拋 exception 不就好了?
try
{
var order = Order.GetById(10);
Console.WriteLine("{0}", order.Name));
}
catch (OrderNotFoundException e)
{
Console.WriteLine("No orders found.");
}
Exception 雖然比 null 好,但依然有些問題:
-
getById()回傳Order型別,你該如何得知到底該處理null還是要處理 exception?靠文件還是要靠團隊共識? - 若 developer 忘記處理 exception,compiler 也無能為力,只能靠 developer 自己的細心,或靠 unit test 的 coverage 完整加以保護
- 每個 function 都可能傳回 exception,是否每個 function 都必須處理 exception?
try catch寫法其實也好不到哪裡,一樣會把 code 弄髒
Option
FP 語言對 null 與 exception 提出了另外一種解決方案,在 OCaml 與 F# 有 Option 型別,在 Haskell 有 Maybe 型別,讓我們可以在 compiler-time 就能處理,也不會把 code 弄髒。
open System
type Order = {
Id: int
Name: string
};
[<EntryPoint>]
let main argv =
let getById id = Some { Id = 10; Name = "Sam" }
let order = getById 10
match order with
| Some order -> printfn "%s" order.Name
| None -> printfn "No orders found."
0 // return an integer exit code
由於 getById() 可能傳回 訂單資料 ,也可能找不到資料,因此回傳的就不是 Order 型別,也不是 null ,而是另外一個 Order option 型別,也就是 Option<Order> 。
再搭配 Pattern Matching 對 Option 做處理,即可同時對 找到訂單資料 與 找不到訂單資料 進行處理。
getById() 回傳的型別不再是 Order 型別 ,而是 Order option 型別,這是個 Option 型別。
Q:什麼是 Option 型別呢 ?
type Option<'a> =
| Some of 'a
| None
Option 事實上就是 Union 型別,只是已經是先定義好 Some 與 None 兩個 case。
重點是 null 是 Order 型別,但 None 不是 Order 型別,而是 Option 型別。
Q : Option 型別對於寫程式有什麼幫助?
之前因為 null 屬於 Order 型別,也無法確定 getById() 是否拋出 exception 而造成困擾,但若確認 getById() 回傳的是 Option 而不是 Order 型別,則 client 就有心理準備,知道要怎麼處理 Option ,不需靠文件也不需靠團隊共識。
let order = Order.getById 10 printfn "%s" order.Name
就算 junior 寫出這樣的程式碼,不用等 senior 來 code review,compiler 已經編譯錯誤,因為 order 為 Option 型別,不是 Order 型別,因此無法直接由 order.Name 取值。
因為 order 不是 Order 型別,所以沒有 Name 可取,在 Intellisense 階段就已經提出警告。
為了要從 Option 取值,就一定得搭配 Pattern Matching。
match order with | Some order -> printfn "%s" order.Name | None -> printfn "No orders found."
透過 Pattern Match 的 Some 將 order 取出來。
若在 Pattern Matching 只寫了 Some ,忘記寫 None ,compiler 也會提出警告。
Summary
Option 型別具有以下優點
- 透過
Option型別,client 可以明確得知該 function 可能傳回資料,也可能不傳回資料,因此會有明確因應對策,而不像null與 exception 那樣 - 由於
Option型別不等於原本資料型別,因此 junior 無法直接對 Option 取值而造成NullReferenceException - 一定得用 Pattern Matching 處裡
Option型別,若忘記使用 Pattern Matching 或 case 不夠完整,compiler 會在 compile-time 就加以警告,不會有忘記處理Option的問題 - Pattern Matching 遠比
null checking與exception handling優雅,也不會把 code 弄髒
Conclusion
- 好的程式語言是在 compile-time 就幫我們找到錯誤,而不是要自己寫程式在 run-time 處理
-
null與 exception 除了常常忘記處理而造成NullReferenceException, 也很容易將 code 弄髒;但Option+ Pattern Matching 則可透過 compiler 幫我們檢查,code 也比較優雅 - JavaScript 已經有
Option型別草案,希望能儘早成為 JavaScript 標準
Reference
F# for fun and profit , The Option type
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ruby for Rails
David Black / Manning Publications / 2006-05-11 / USD 44.95
What's Inside * How Ruby and Rails work, separately and together * Extensive Ruby language tutorial * Ruby techniques for Rails applications * Explore the Rails framework source code A new level of pr......一起来看看 《Ruby for Rails》 这本书的介绍吧!