允許使用 Pattern = Guard_Expression 作為簡單的守衛測試。將明顯愚蠢的守衛視為語法錯誤。
將 6.24 節「守衛序列」的開頭文字替換如下。
<guard> ::= <OR guard>
<OR guard> ::= <AND guard> {';' <AND guard>}*
一個 <OR guard>
是一系列以分號分隔的 <AND guard>
。在 Erlang 中,如同其他地方,分號表示循序 OR:一個 <OR guard>
會從左到右依序評估它的 <AND guard>
,直到其中一個成功,或所有都失敗為止。
<AND guard> ::= <guard test> {',' <guard test>}*
一個 <AND guard>
是一系列以逗號分隔的 <guard test>
。在 Erlang 中,如同常見的情況,逗號表示循序 AND:一個 <AND guard>
會從左到右依序評估它的 <guard test>
,直到所有都成功,或其中一個失敗為止。
<guard test> ::= <guard match>
| <Boolean expression>
<guard match> ::= <pattern> '=' <guard expr>
| <pattern> '=' <guard match>
一個 <guard test>
要嘛是一個匹配,要嘛是一個布林表達式。在守衛條件中,如果 <guard expr>
可以被評估且不拋出例外,並且結果可以與 <pattern>
匹配(可能綁定一些變數),則匹配成功。
如果一個變數在一個 <guard test>
中被綁定,它可以被用於同一個 <AND guard>
後續的 <guard test>
中。如果一個變數在一個 <OR guard>
的所有 <AND guard>
中都被綁定,它可以被用於受守衛的程式碼中,所以
if X = 1, is_atom(element(X, Tup))
; X = 2, is_atom(element(X, Tup))
-> ... uses X ...
是可以的。如果一個變數在一個 <OR guard>
的其中一個 <AND guard>
中被綁定,但不是所有,則它不能被用於受守衛的程式碼中,所以
if X = a
; Y = b
-> ... uses X ...
是不允許的。
一個守衛條件中的 <Boolean expression>
由一些子表達式組成
constant 'false'
constant 'true'
variable (must be bound to 'false' or 'true')
term comparison with `<guard expr>` operands
calls to type test BIFs with `<guard expr>` operands
`<Boolean expression>`s in parentheses
使用運算符 ‘not’、‘and’、‘or’、‘andalso’ 和 ‘orelse’ 組合而成。因此
X+1 == Y
是一個可以作為 <guard test>
的 <Boolean expression>
,但是
X+1
則不是。建議永遠不要使用 ‘and’ 和 ‘or’ 運算符,並且盡可能避免使用 ‘andalso’ 和 ‘orelse’,只要使用 ‘,’ 和 ‘;’ 可以達到你的需求即可。
<guard expr>
的集合是有效的 Erlang 表達式的子集。限制有效表達式集合的原因是,必須保證守衛表達式的評估沒有副作用並且會終止。
一個 <guard expr>
由一些子表達式組成
<guard expr>
參數<guard expr>
使用內建的算術和位元運算符組合而成。
此 EEP 分為兩個部分。最初它只是關於允許在守衛條件中使用匹配。然後它變成了兩個,因為目前的情況太混亂了,但為了簡潔又再次變成一個。
考慮這個例子。一個函數被給定一個元組和一個索引。如果該索引處的元素在 0..127 的範圍內,則應返回該元素。否則應應用其他子句。目前,我們必須寫
f(Tuple, Index)
when is_integer(element(Tuple, Index)),
0 =< element(Tuple, Index),
element(Tuple, Index) =< 127
-> element(Tuple, Index);
...
或其他更笨拙的方法。為什麼我們不能寫
f(Tuple, Index)
when X = element(Tuple, Index),
is_integer(X), 0 =< X, X =< 127
-> X;
...
在嘗試解釋如何將其添加到語言中時,我發現 Erlang 參考手冊中對守衛條件的當前描述非常模糊。令人沮喪的是,這種情況與同樣模糊的實現相匹配。該描述將可用作守衛 BIF 參數的東西(守衛表達式)與簡單守衛條件混淆了。
考慮這個例子
X = 1,
if X+1 -> true
; X-1 -> false
end.
這顯然毫無意義,應該作為錯誤的語法被拒絕。根據目前的參考手冊,它是合法的;X+1 和 X-1 是合法的「守衛表達式」。
在 shell 中,這個例子會崩潰,這確實很有道理。但是 ‘erlc’ 說
{X+1} Warning: the guard for this clause evaluates to 'false'
{X-1} Warning: the guard for this clause evaluates to 'false'
有一個警告是好的,但是警告的文字是錯誤的,這很糟糕。這些東西並不會評估為 ‘false’,它們會評估為數字。然後,儘管給出了警告,您還是會收到一個執行階段錯誤。
exited: {if_clause,[{a,f,0},{shell,exprs,6},{shell,eval_loop,3}]}
當然,在這個例子中發生的事情是 ‘if’ 的所有子句都被刪除了,因為它們都是格式錯誤的。更真實的例子只會在執行階段默默地做錯事。
允許在守衛條件中使用匹配的語法是顯而易見的;沒有其他語法是可以接受的。唯一真正的問題是它們是否可以嵌入在 ‘andalso’ 和 ‘orelse’ 內部,為了避免回溯的問題,我說「不」。這確實是我能想到的允許匹配的最簡單的守衛擴展。
EEP 的其餘部分是關於嘗試在編譯時排除明顯愚蠢的守衛測試。確切如何做到這一點是有爭議的。但肯定應該這麼做。允許 “27” 和 “X+5” 作為守衛,我們目前能獲得什麼好處(除了編譯器中不必要的簡潔性)?
目前不允許在守衛條件中使用匹配,因此添加它們不會破壞任何現有的應用程式程式碼。顯然,任何使用 Erlang 解析樹的東西都需要擴展。
清理守衛條件中允許的東西可能會影響現有的程式碼。但是,在大多數情況下,編譯器已經會對此發出警告,並且兼容性問題相當於將警告訊息變成錯誤訊息。
無。
此文件已置於公共領域。