作者
Richard A. O'Keefe <ok(at)cs(dot)otago(dot)ac(dot)nz>
狀態
草案
類型
標準追蹤
建立於
2008年11月28日
Erlang 版本
OTP_R12B-4

EEP 25:解嵌套的 case 語法 #

摘要 #

Erlang 的 'case' 表達式應採用/改編 Algol 68 的一個概念,在 Erlang 中這會嚴格地泛化 'cond'。

規格 #

目前,'case' 表達式的形式為

'case' Expression 'of'
      Pattern ['when' Guard] '->' Expression
 {';' Pattern ['when' Guard] '->' Expression}...
'end'

眾所周知,Algol 68 有

if .. then .. {elif .. then ..}... [else ..] fi

表達式。比較不為人知的是,它對於 case 表達式也有類似的結構,

case .. in ... {ouse .. in ..}... [out ..] esac

其中 “ouse”(來自 “OUt caSE”)讓您可以迭代 case 匹配過程,並且只需要一個 ‘esac’。

此提案採用 Algol 68 的概念。修訂後的格式為

'case' Expression 'of'
      Pattern ['when' Guard] '->' Expression
 {';' Pattern ['when' Guard] '->' Expression}...
{';' 'or' 'case' Expression 'of'
      Pattern ['when' Guard] '->' Expression
 {';' Pattern ['when' Guard] '->' Expression}...}...
'end'

動機 #

考慮這個範例

suffix(P, Suffix, List)
    when is_function(P, 2), is_list(Suffix) ->
  suffix_loop(P, Suffix, List).

suffix_loop(P, Suffix, List) ->
  case equal(P, Suffix, List)
    of true  -> true
     ; false -> case List
          of [_|Tail] -> suffix_loop(P, Suffix, Tail)
           ; []       -> false
            end
  end.

根據此提案,我們可以這樣寫

suffix_loop(P, Suffix, List) ->
  case equal(P, Suffix, List)
    of true     -> true
  ; or case List
        of [_|Tail] -> suffix_loop(P, Suffix, Tail)
     ; []       -> false
  end.

其中所有要選擇的替代方案都具有相同的縮排。

舊的類似 Lisp 的 'cond' 提案已不再真正需要。取代

cond
    C1 -> B1
  ; C2 -> B2
  ...
  ; Cn -> Bn
end

寫成

case      C1 of true -> B1
; or case C2 of true -> B2
...
; or case Cn of true -> Bn
end

這裡失去的是檢查不是 'true' 的結果必須是 'false',但這項工作現在可以由 Dialyzer 完成。這當然比 'cond' 笨拙,但它達到了主要目的,即透過一系列布林值表達式,從邏輯上(因此在相同的縮排層次上)的一組選擇中進行選擇,但它在嚴格意義上更具通用性。它允許您將布林值表達式與守衛(包括未來任何守衛的泛化)結合使用,並且允許您根據任何形式的模式匹配(而不僅僅是布林值)進行選擇。

這比 'cond' 笨拙,但過度使用布林值,而應該使用一些更能顯示意圖的列舉,是一種已經被認可超過 20 年的反模式。如果存在 'cond',人們會有很強烈的壓力去編寫返回布林值結果的函數,而其他東西可能更有用,只是為了讓他們可以使用 'cond'。

舉例來說,假設我們希望在電壓為正常時繼續,在電壓低且沒有緊急情況時關閉設備,或者在電壓低且有緊急情況時將速度設定為慢速。

使用 cond

cond voltage_nominal() -> continue_operations()
   ; in_emergency()    -> set_speed_slow()
   ; true              -> shut_device_down()
end

使用 case

case      voltage() of nominal  -> continue_operations()
; or case status() of emergency -> set_speed_slow()
                    ; normal    -> shut_device_down()
end

當以這種方式表達時,我個人覺得更容易意識到“低”不是“正常”的反義詞;電壓不正常可能是高。所以我們真的應該有

case      voltage() of nominal   -> continue_operations()
                     ; high      -> WHAT DO WE DO HERE?
; or case status()  of emergency -> set_speed_slow()
             ; normal    -> shut_device_down()
end

因此,提供 'cond' 的“扁平”結構,同時巧妙地鼓勵 'case' 的多向思維的方法是有價值的。您可以說我支持 'ouse' 的程度不及反對 'cond' 和過度使用布林值的程度。

理由 #

我讀了太多“為什麼 Erlang 沒有 if”的電子訊息,突然想起了“Algol 68 可以使用 'case' 來做到這一點”。

主要問題是如何在 Erlang 中拼寫 ‘ouse’。我的第一個偏好是 ‘or case’,但這行不通。我不喜歡 “; or case”,而且非常樂意看到更好的東西。實際上,“; case” 也許可以勝任,我只是覺得這有點太容易出錯了。

向後相容性 #

所有現有的 Erlang 程式碼在語意不變的情況下仍然可以接受。實作將完全在剖析器中進行,因此即使是檢查 AST 的工具也不會受到影響。

參考實作 #

目前還沒有。它將完全在剖析器中進行。

版權 #

本文檔已放置於公共領域。