概要
Erlangの短絡評価
andalso
、orelse
は第2引数がboolean()
でなくても良い。
ここで「第2引数」とは「演算子の右の値」のことである。
ex.Expr1 andalso Expr2
の「第2引数」はExpr2
のこと。
andalso
と orelse
は次のような挙動を取る。
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だけではないみたいだ。
参考URL: 短絡評価 - Wikipedia
JavaScript、Perl、Ruby、Lisp等で似たような挙動があるみたいである。
また、内容は割愛するが、他にも以下のようなページに似たような内容のことがまとめられていた。
参考ページURL
- 短絡評価 - Wikipedia
- JavaScript の論理演算子と短絡評価の話。 - C#でプログラミングあれこれ
- 論理演算子(&& と ||)を応用する - てっく煮ブログ
以上より、 Expr1 andalso Expr2
や Expr1 orelse Expr2
の Expr2
は boolean()
じゃなくて良いということである。
何ができるか
こんな際どい仕様を見つけて、何ができるのだろうか。
例えば、 defined_list() = [{Key::atom(), Value::any()}] | undefined
からある Key
に一致する Value
を取り出すとしよう。
一般的には次のように書ける。
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()
でなくてもよいことを使うと、もう少し短くコードを書くことができる。
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の短絡評価
andalso
、orelse
は第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.
僕は今はまだ短絡評価を末尾再帰で使った覚えはないが、使えるところがあれば使っていきたい。