允许使用 Pattern = Guard_Expression 作为简单的 Guard 测试。将明显无意义的 Guard 作为语法错误处理。
将 6.24 节 “Guard Sequences” 的开头文本替换如下。
<guard> ::= <OR guard>
<OR guard> ::= <AND guard> {';' <AND guard>}*
一个 <OR guard>
是由分号分隔的 <AND guards>
序列。这里,如同 Erlang 中的其他地方一样,分号表示顺序 OR:一个 <OR guard>
从左到右逐个评估其 <AND guards>
,直到其中一个成功或所有都失败。
<AND guard> ::= <guard test> {',' <guard test>}*
一个 <AND guard>
是由逗号分隔的 <guard tests>
序列。这里,如同 Erlang 中经常发生的情况一样,逗号表示顺序 AND:一个 <AND guard>
从左到右逐个评估其 <guard tests>
,直到所有都成功或其中一个失败。
<guard test> ::= <guard match>
| <Boolean expression>
<guard match> ::= <pattern> '=' <guard expr>
| <pattern> '=' <guard match>
一个 <guard test>
要么是一个匹配,要么是一个布尔表达式。在一个 Guard 中,当且仅当 <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 ...
是不允许的。
Guard 中的一个 <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 表达式的评估没有副作用并且能够终止。
一个 <guard expr>
由多个子表达式组成
<guard expr>
参数<guard expr>
使用内置的算术和按位运算符组合。
这个 EEP 分为两个部分。最初它只是关于允许在 Guard 中进行匹配。后来它变成了两个,因为当前的情况太混乱了,但为了简洁起见,它又变成了一个。
考虑这种情况。一个函数接收一个元组和一个索引。如果该索引处的元素在 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 参考手册中对 Guard 的当前描述非常模糊。令人沮丧的是,这与同样模糊的实现相匹配。该描述将可以用作 Guard BIF 参数(Guard 表达式)的内容与简单的 Guard 混淆了。
考虑以下示例
X = 1,
if X+1 -> true
; X-1 -> false
end.
这显然没有任何意义,应该被视为语法错误。根据当前的参考手册,它是合法的;X+1 和 X-1 是合法的“Guard 表达式”。
在 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’ 的所有子句都被消除了,因为它们都是畸形的。更真实的例子只是在运行时悄悄地做错误的事情。
允许在 Guard 中进行匹配的语法是显而易见的;没有其他语法是可容忍的。唯一真正的问题是它们是否可以嵌入到 ‘andalso’ 和 ‘orelse’ 中,为了避免回溯问题,我说 “不”。这真的是我能想到的最简单的允许匹配的 Guard 扩展。
EEP 的其余部分旨在在编译时排除明显无意义的 Guard 测试。如何准确地做到这一点是有争议的。但它应该这样做是毋庸置疑的。允许 “27” 和 “X+5” 作为 Guard,我们目前获得了什么好处(除了编译器中不必要的简单性)?
匹配目前在 Guard 中是不允许的,因此添加它们不会破坏任何现有的应用程序代码。显然,任何使用 Erlang 解析树的东西都需要进行扩展。
清理 Guard 中允许的内容可能会影响现有代码。但是,在大多数情况下,编译器已经对此发出了警告,而兼容性问题相当于将警告消息转换为错误消息。
无。
本文档已置于公共领域。