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
这里失去的是对结果必须为 'false' 而不是 'true' 的检查,但这项工作现在可以由 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
所以一种在巧妙地鼓励 'case' 的多路思维的同时,给你 'cond' 的“扁平”结构的方法是有意义的。你可以说我与其说是支持 'ouse',不如说是反对 'cond' 和过度使用布尔值。
我读了太多 “为什么 Erlang 没有 if” 的电子邮件,然后突然想起了 “Algol 68 可以用 ‘case’ 来做到这一点”。
主要问题是如何在 Erlang 中拼写 ‘ouse’。我的第一选择是 ‘or case’,但这行不通。我不喜欢 “; or case”,并且很乐意看到更好的东西。实际上,“; case” 可能可以完成这项工作,我只是觉得它有点容易出错。
所有现有的 Erlang 代码在语义不变的情况下仍然可以接受。实现将完全在解析器中完成,因此即使是检查 AST 的工具也不会受到影响。
目前还没有。它将完全在解析器中完成。
本文档已置于公共领域。