作者
Richard A. O'Keefe <ok(at)cs(dot)otago(dot)ac(dot)nz>
状态
草案
类型
标准跟踪
创建
2008年8月8日
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 中,分号是一个运算符,而不是一个终止符。允许运算符具有前缀版本以及中缀版本没有什么不寻常的。甚至对于一个除了澄清事情之外没有太多作用的前缀运算符来说,也没有什么不寻常的:‘+’ 是一个明显的例子。因此,在某些情况下允许分号的“无操作”前缀使用仍然符合 Erlang 的精神。

除此之外,这个更改非常简单。唯一有疑问的点是是否应该在 ‘after’ 之前允许分号。但是 ‘after’ 已经是一个解释接下来内容的关键字,并且无论如何都不能随意移动它。既然似乎没有什么可获得的,那就不要这样做了。

向后兼容性 #

所有现有的 Erlang 代码都保持可接受,语义不变。前导分号完全在解析器中处理;其他语言操作工具永远不会知道分号曾经存在过,因此可以完美地使用新风格的代码。

参考实现 #

辅助文件 eep-0028-1.diff 是一个要应用于 erl_parse.yrl 的补丁文件。已通过 yecc 检查了已修补的文件,yecc 对其感到满意,并且生成的 .erl 文件可以干净地编译。但是,这就是所做的所有测试。

实现所做的全部工作就是将

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

更改为

.... thingy_kw .....

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

在多个位置。选择这种形式的更改,而不是

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

是为了使现有规则中的 ‘$n’ 形式无需修改,因此我确信此更改没有引入任何错误。

版权 #

本文档已置于公共领域。