Erlangの andalso は第2引数が boolean() じゃなくても良い

More than 3 years have passed since last update.


概要


Erlangの短絡評価 andalsoorelse は第2引数が boolean() でなくても良い。

ここで「第2引数」とは「演算子の右の値」のことである。

ex. Expr1 andalso Expr2 の「第2引数」は Expr2 のこと。


andalsoorelse は次のような挙動を取る。

1> true andalso false.

false
2> true andalso ok.
ok
3> false orelse true.
true
4> false orelse ok.
ok


検証

さて、問題はこれが仕様かどうかである。


1. 公式ドキュメント


From R13A, Expr2 is no longer required to evaluate to a boolean value.


boolean valueである必要はないそうです。

参考URL: Erlang -- Expressions # 8.14 Short-Circuit Expressions


2. 一般的な短絡評価について

今まであまり気にしたことはなかったが、色々調べていると、この仕様はErlangだけではないみたいだ。

短絡評価.png

参考URL: 短絡評価 - Wikipedia

JavaScript、Perl、Ruby、Lisp等で似たような挙動があるみたいである。

また、内容は割愛するが、他にも以下のようなページに似たような内容のことがまとめられていた。


参考ページURL

以上より、 Expr1 andalso Expr2Expr1 orelse Expr2Expr2boolean() じゃなくて良いということである。


何ができるか

こんな際どい仕様を見つけて、何ができるのだろうか。

例えば、 defined_list() = [{Key::atom(), Value::any()}] | undefined からある Key に一致する Value を取り出すとしよう。

一般的には次のように書ける。


sample_code1.erl

get_value(_Key, undefined) ->

{error, not_match}; %% NOT_MATCH_PROCESS
get_value(Key, DefiendList) ->
case proplists:get_value(Key, DefinedList) of
{Key, Value} -> {ok, Value};
_ -> {error, not_match} %% NOT_MATCH_PROCESS
end.

しかし、 orelse の第2引数が boolean() でなくてもよいことを使うと、もう少し短くコードを書くことができる。


sample_code.erl

get_value(Key, DefinedList) ->

case DefinedList =:= undefined orelse proplists:get_value(Key, DefinedList) of
{Key, Value} -> {ok, Value};
_ -> {error, not_match} %% NOT_MATCH_PROCESS
end.

※上記コードは説明で使うことを考え簡単に書いてあるため、そのままでは使えないと思われる。(エラー処理等)

このコードでは値がない場合の処理が短いのでそんなに得をした感じはしないが、値がない場合の処理(NOT_MATCH_PROCESS) {error, not_match} の部分の処理が長くなった場合、後者のように共通化しておいた方が、メンテコストは下がると思われる。


まとめ


Erlangの短絡評価 andalsoorelse は第2引数が boolean() でなくても良い。


ただ、可読性等もあるので、場所を選んで使って行きたい。



ちなみに…

上の8.14 Short-Circuit Expressionsの引用部分の後には次の様に続いている。


As a consequence, andalso and orelse are now tail-recursive. For instance, the following function is tail-recursive in R13A and later:

all(Pred, [Hd|Tail]) ->

Pred(Hd) andalso all(Pred, Tail);
all(_, []) ->
true.

僕は今はまだ短絡評価を末尾再帰で使った覚えはないが、使えるところがあれば使っていきたい。