作者
Richard A. O'Keefe <ok(at)cs(dot)otago(dot)ac(dot)nz>
狀態
草案
類型
標準追蹤
建立
2011年5月27日
Erlang 版本
OTP_R14B04

EEP 38: -discontiguous 指令 #

摘要 #

將加入一個 -discontiguous 指令,以便將指定的函式呈現為多個子句群組,這些群組可能會被其他指令和函式子句群組分隔開。

規格 #

新增一個新的指令

-discontiguous( Name_And_Arity_List ).

。此列表中命名的每個函式都必須在該模組中至少有一個子句群組,並且可以有多個。對於任何未在此列表中命名的函式,若有多個子句群組,仍然會產生錯誤。

-discontiguous 指令中命名的函式不一定需要有多個子句群組。如果它有多個子句群組,則會將這些子句群組合併在一起,且不重新排序,並且除了最後一個群組之外的每個群組的句點都更改為分號。編譯器不應對多個子句群組的存在或它們合併為單個子句群組發表任何評論。

解析器階段會執行重新分組,並且不會在其輸出中包含任何 -discontiguous 指令的表示,以便下游工具永遠不會知道 -discontiguous 曾經存在過。

動機 #

有一個單一機制可以解決三個問題。

第一個問題是 Erlang 具有條件編譯,但是沒有真正令人滿意的方法可以使用它來選擇函式的某些但不是所有子句。

-discontiguous 指令允許你寫

-discontiguous([f/3]).

f(a, X, Y) -> .... .
-if(Cond).
f(b, X, Y) -> .... .
-endif.
f(c, X, T) -> .... .

第二個問題可以稱為「主題導向編程」。它關係到人類圍繞計算出的資料值而不是計算它們的程式碼來組織程式碼。我在處理虛擬機器時發現了這一點:我希望將組裝指令的程式碼、進行窺孔優化的程式碼、將其編碼到記憶體中的程式碼,以及將其解釋的程式碼放置在一個位置(涉及不同的函式),而不是按函式組織,這樣會將相關資訊散佈在整個模組的長度和廣度上。

從一個範例開始可能會最清楚。erl_syntax.erl 中的程式碼讀取為

-type syntaxTree() :: #tree{} | #wrapper{} | tuple().

%% All `erl_parse' tree nodes are represented by tuples
%% whose second field is the position information (usually
%% an integer), *with the exceptions of*
%% `{error, ...}' (type `error_marker') and
%% `{warning, ...}' (type `warning_marker'),
%% which only contain the associated line number *of the
%% error descriptor*; this is all handled transparently
%% by `get_pos' and `set_pos'.

get_pos(#tree{attr = Attr}) ->
    Attr#attr.pos;
get_pos(#wrapper{attr = Attr}) ->
    Attr#attr.pos;
get_pos({error, {Pos, _, _}}) ->
    Pos;
get_pos({warning, {Pos, _, _}}) ->
    Pos;
get_pos(Node) ->
    %% Here, we assume that we have an `erl_parse' node
    %% with position information in element 2.
    element(2, Node).

set_pos(Node, Pos) ->
    case Node of
        #tree{attr = Attr} ->
            Node#tree{attr = Attr#attr{pos = Pos}};
        #wrapper{attr = Attr} ->
            Node#wrapper{attr = Attr#attr{pos = Pos}};
        _ ->
            %% We then assume we have an `erl_parse' node,
            %% and create a wrapper around it to make
            %% things more uniform.
            set_pos(wrap(Node), Pos)
    end.

這裡的類型有點模糊。額外的元組似乎是 {error,{Pos,_,_}}, {warning,{Pos,_,_}}erl_parse 返回的 {Tag,Pos...} 元組。這裡的重點是存在五種不同的情況。對於某些目的,最好寫成

-discontiguous([get_pos/1,set_pos/2]).

get_pos(#tree{attr = Attr}) -> Attr#attr.pos.
set_pos(#tree{attr = Attr} = Node, Pos) ->
    Node#tree{attr = Attr#attr{pos = Pos}}.

get_pos(#wrapper{attr = Attr}) -> Attr#attr.pos.
set_pos(#wrapper{attr = Attr} = Node, Pos) ->
    Node#wrapper{attr = Attr#attr{pos = Pos}}.

get_pos({error, {Pos,_,_}}) -> Pos.
% What should set_pos/2 do in this case?

get_pos({warning, {Pos,_,_}}) -> Pos.
% What should set_pos/2 do in this case?

get_pos(Node) -> element(2, Node).  % assume erl_parse node
set_pos(Node, Pos) ->               % assume erl_parse node
    set_pos(wrap(Node), Pos).       % wrap it for uniformity

這比任何其他可能的佈局都更清楚地突出了兩個函式之間的平行關係,以及平行關係失敗的方式。它不斷地提醒你要么使用顯而易見的方式完成平行關係

set_pos({error, {_,X,Y}}, Pos) ->
    {error, {Pos,X,Y}}.

以及

set_pos({warning, {_,X,Y}), Pos) ->
    {warning, {Pos,X,Y}}.

子句,或者至少將註解更改為

% set_pos/2 falls through to the last case.

註解。

我們在該檔案中的另外兩個函式中具有相同的模式,而沒有平行的失敗

get_com(#tree{attr = Attr}) -> Attr#attr.com;
get_com(#wrapper{attr = Attr}) -> Attr#attr.com;
get_com(_) -> none.

set_com(Node, Com) ->
    case Node of
        #tree{attr = Attr} ->
            Node#tree{attr = Attr#attr{com = Com}};
        #wrapper{attr = Attr} ->
            Node#wrapper{attr = Attr#attr{com = Com}};
        _ ->
            set_com(wrap(Node), Com)
    end.

它們可以是

-discontiguous([get_com/1,set_com/1]).

get_com(#tree{attr = Attr}) -> Attr#attr.com.
set_com(#tree{attr = Attr} = Node, Com) ->
    Node#tree{attr = Attr#attr{com = Com}}.

get_com(#wrapper{attr = Attr}) -> Attr#attr.com.
set_com(#wrapper{attr = Attr} = Node, Com) ->
    Node#wrapper{attr = Attr#attr{com = Com}}.

get_com(_) -> none.  % error, warning, erl_parse.
set_com(Node, Com) ->
    set_com(wrap(Node), Com).

嗯,再一次,平行關係並不完全完美。wrap/1 的文件說明假設它的參數是一個類別 erl_parse 元組,這裡的意思是它似乎不應該是錯誤或警告。

這裡感興趣的重點是,僅僅查看現有的函式並沒有引起任何警報;直到我說「這些函式似乎與相同的資料結構有關;我想知道交錯是否可以使關聯更清楚,並使其更容易確保 getter 和 setter 正確相關?」時,我的注意力才真正被引導到差異上。

特別有趣的是,我查看的第一個 Erlang/OTP 源檔案就提供了範例。第三個範例與第二個範例類似,但它與電腦而不是人編寫的程式碼有關。例如,如果生成某種類型的狀態機的功能表示,則可以方便地圍繞狀態組織輸出,但是目前的方案要求圍繞處理狀態的函式組織輸出。

基本原理 #

Prolog 系統已經支援 :- discontiguous 宣告 20 多年了。這種方法是一種行之有效的方法。它是語言的一個簡單的概括,可以對所有「下游」工具隱藏。只有試圖在未完全解析的情況下處理 Erlang 語法的工具才能注意到差異,並且它們應該在很大程度上忽略它。

向後相容性 #

所有現有的 Erlang 程式碼在語義不變的情況下仍然可以接受。如果現有的語言處理工具依賴於 erl_parse,則它們不會受到影響。

參考實作 #

本草案中沒有。

參考資料 #

無。

版權 #

本文檔已置於公共領域。