Erlang の if 文のガード(条件節)に記述可能な関数

何にでも手を出すのが信条、今巷で流行っているという噂の Erlang に手を出さずにおくべきか! というわけでは必ずしもないんだけど、ちょっと Erlang かじってます。
実は関数型言語を触るのは事実上初めてなので ( XSLT も一応関数型に分類されているけど、ちょっと違うしなあ)、ずいぶん戸惑いつつ、Programming Erlang あたりをせっせと読む今日この頃。


で、戸惑ったことは結構いろいろあるわけだが、「 illegal guard expression 」と頻繁に怒られてしまうことに一番参った。
これは例えば以下のように書くと出る。

  X = 9.
  if math:sqrt(X) == 3 ->
    ok
  end.

guard というのは if や when に使う条件節のことで、この場合は math:sqrt(X) == 3 のこと。
でも何が illegal なのかわからない。 math:sqrt(X) == 3. を単独で実行したらちゃんと true が返ってくるのに……
で、下記のように直したらやっと動いてくれた。

  X = 9.
  XX = math:sqrt(X).
  if XX == 3 ->
    ok
  end.

むう。if 文には関数書くなってか。
でも、次のは怒られずにちゃんと動く。

  L = [1,2,3].
  if length(L) == 3 ->
    ok
  end.

うむむむ。


というわけで実は guard に使ってもいい関数というのが決まっていて、それは BIFs ( built-in functions ) の一部なのであった。
http://www.erlang.org/doc/man/erlang.html にて "Allowed in guard tests" と付記されている関数がそれに当たるのだが、ネット上では一覧っぽいものが見つけられなかった( Programming Erlang なら 3.8 Guards に一覧が載っている)。


というわけで以下がその guard に使っていい関数である。

is_atom(X)
is_binary(X)
is_constant(X)
is_float(X)
is_function(X)
is_function(X, N)
is_integer(X)
is_list(X)
is_number(X)
is_pid(X)
is_port(X)
is_reference(X)
is_tuple(X)
is_record(X,Tag)
is_record(X,Tag,N)

abs(X)
element(N, X)
float(X)
hd(X)
length(X)
node()
node(X)
round(X)
self()
size(X)
trunc(X)
tl(X)

関数型言語に詳しくないのではずしているかもしれないが、おそらくは副作用が生じるおそれがある(BEAM が知らない = built-in ではない関数を使った)式はガードとして適切ではないということなのだろう。
ともあれ、これでもう "illegal guard expression" と怒られずに済むよ、やれやれ。