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

EEP 28:選擇性的前導分號 #

摘要 #

‘if’、‘case’、‘receive’ 和 ‘try’ 子句可以以分號開頭。

規格 #

關鍵字 ‘if’、‘of’、‘receive’ (前提是下一個字不是 ‘after’) 以及 ‘catch’ (在 ‘try’ 表達式中) 之後允許使用分號。

分號沒有任何作用;它僅僅是為了允許一種佈局風格,這種風格更容易看到分號,更容易確保逗號是逗號,分號是分號,並且更容易更改選擇的順序。

動機 #

Peter van Roy 在他關於編譯 Prolog 的博士論文中抱怨說,逗號和分號很難區分。為了回應這一點,我開發了一種 Prolog 佈局風格,其中逗號放在行尾,分號放在行首,這樣閱讀文本的人就不會懷疑哪個是預期的。

逗號和分號在 Erlang 中仍然難以區分。事實證明,分號在前的方式也適用於 Erlang。

do_load_driver(Path, Driver, DriverFlags) ->
    case erl_ddll:try_load(Path, Driver,
           [{monitor,pending_driver}]++DriverFlags) of
    {error, inconsistent} ->
        {error,bad_driver_name};
    {error, What} ->
        {error,What};
    {ok, already_loaded} ->
        ok;
    {ok,loaded} ->
        ok;
    {ok, pending_driver, Ref} ->
        receive
        {'DOWN', Ref, driver, _, load_cancelled} ->
            {error, load_cancelled};
        {'UP', Ref, driver, _, permanent} ->
            {error, permanent};
        {'DOWN', Ref, driver, _,
                {load_failure, Failure}} ->
            {error, Failure};
        {'UP', Ref, driver, _, loaded} ->
            ok
        end
    end.

在這種佈局風格中,視覺上最突出的部分是行的開頭,除了 ‘case’、‘receive’ 和 ‘end’ 之外,每一行都可以是任何一行。僅靠縮排並不是可靠的指南,因為一些邏輯行必須跨多個物理行拆分。

我目前的風格是

do_load_driver(Path, Driver, DriverFlags) ->
    case erl_ddll:try_load(Path, Driver,
           [{monitor,pending_driver}]++DriverFlags)
     of {error, inconsistent} ->
        {error,bad_driver_name}
      ; {error, What} ->
        {error,What}
      ; {ok, already_loaded} ->
        ok
      ; {ok,loaded} ->
        ok
      ; {ok, pending_driver, Ref} ->
        receive
        {'DOWN', Ref, driver, _, load_cancelled} ->
            {error, load_cancelled}
      ; {'UP', Ref, driver, _, permanent} ->
            {error, permanent}
      ; {'DOWN', Ref, driver, _,
                {load_failure, Failure}} ->
            {error, Failure}
      ; {'UP', Ref, driver, _, loaded} ->
            ok
        end
    end.

這裡的前導分號甚至讓只看一半的人都明顯知道每個選擇的開始位置,而分號行(與 ‘end’ 的 ‘d’ 對齊)使它很容易看到結構而不需要尺子。只有一個小問題:第一個選擇必須不同。如果寫成以下形式會更一致

do_load_driver(Path, Driver, DriverFlags) ->
    case erl_ddll:try_load(Path, Driver,
           [{monitor,pending_driver}]++DriverFlags) of
      ; {error, inconsistent} ->
        {error,bad_driver_name}
      ; {error, What} ->
        {error,What}
      ; {ok, already_loaded} ->
        ok
      ; {ok,loaded} ->
        ok
      ; {ok, pending_driver, Ref} ->
        receive
          ; {'DOWN', Ref, driver, _, load_cancelled} ->
            {error, load_cancelled}
          ; {'UP', Ref, driver, _, permanent} ->
            {error, permanent}
          ; {'DOWN', Ref, driver, _,
                {load_failure, Failure}} ->
            {error, Failure}
          ; {'UP', Ref, driver, _, loaded} ->
            ok
        end
    end.

現在每個選擇都有相同的結構,如果我們希望重新排序這些選擇,我們可以很容易地做到,而無需添加、刪除或更改任何標點符號。

看看一些其他程式語言中 case 陳述式的樣子,以了解這種風格非常普遍是相關的。

  • Fortran

      SELECT CASE (expression)
      CASE (values and ranges)
          statements
      CASE (values and ranges)
          statements
      CASE DEFAULT
          statements
      END CASE
    
  • Ada

      case Expression is
      when Discrete_Choice_List =>
          Statements;
      when Discrete_Choice_List =>
          Statements;
      when others =>
          Statements;
      end case;
    
  • PL/I

      select (Expression);
        when (Values) Statement;
        when (Values) Statement;
        otherwise     Statement;
      end;
    

這些都展示了“梳子風格”,即在不添加、刪除或更改標點符號或關鍵字的情況下重新排列選擇的能力,以及在每個選擇的開頭清楚地指示。

理由 #

喜歡通常 Erlang 風格的人不應該被迫更改。這表示前導分號必須是選擇性的,而不是必需的。

通過允許選擇性的尾隨分號而不是選擇性的前導分號,可以獲得上面聲稱的一些好處。但是,就 Erlang 現狀而言,分號是一個運算符,而不是終止符。允許一個運算符同時擁有前綴版本和中綴版本並不奇怪。甚至一個除了澄清事情外沒有其他作用的前綴運算符也不奇怪:’+’ 就是一個明顯的例子。因此,在某些情況下允許使用分號的 “do-nothing” 前綴用法仍然符合 Erlang 的精神。

除此之外,變更本身非常簡單。唯一令人懷疑的地方是是否允許在 ‘after’ 前面使用分號。但是 ‘after’ 已經是一個關鍵字,解釋接下來會發生什麼,而且它無論如何都不能自由移動。由於似乎沒有什麼可獲得的,所以我們不要這樣做。

向後相容性 #

所有現有的 Erlang 程式碼在語意不變的情況下仍然可以接受。前導分號完全在解析器中處理;其他語言操作工具永遠不會知道分號的存在,因此可以與使用新風格的程式碼完美地配合使用。

參考實作 #

輔助檔案 eep-0028-1.diff 是要應用於 erl_parse.yrl 的修補程式檔案。已由 yecc 檢查過已修補的檔案,它對此感到滿意,並且產生的 .erl 檔案可以乾淨地編譯。但是,這就是已完成的所有測試。

實作所做的只是將

.... 'thingy' .....

變更為

.... thingy_kw .....

thingy_kw -> 'thingy'.
thingy_kw -> 'thingy' ';'.

在幾個地方。之所以選擇這種變更形式,而不是

.... 'thingy' optional_semicolon ....,

是為了使現有規則中的 ‘$n’ 形式不需要修改,所以我確信此變更沒有引入任何錯誤。

版權 #

本文檔已置於公共領域。