最新

val it : α → α = fun

<<  2007/05  >>

2007/05/16 Erlang のパターンガード

ところで Erlang の言語仕様で個人的に気になること。

  1. 「ASCII(7bit)の範囲内の整数のみで構成されているリスト」が「文字列」とみなされること(「文字」というのはないし、マルチバイトはおろか8bit clean ですらないんじゃないかなあ)
  2. 関数内ローカルな関数が書けないこと
  3. パターンガード=if条件部に書ける述語は、あらかじめ定められたものしか許されないこと

とりあえずこのみっつかな。最初のもひどいんだが、 Haskell を使っているとそれなりに感覚がマヒしてくるので(とりあえずiconvとかあるしいーや派)ひとまず脇に置くとして、関数内にローカルな定義を書けないのも、それが Erlang の流儀ならそれでよしとしようじゃない。

で、最後のことについて。たとえば member という次のような関数があるとします。

member(X, []) = false;
member(X, [X|_]) = true;
member(X, [_|L]) = member(X, L).

でも、この member をガードに使うことができないわけです。たとえば、メッセージを受信したいが、あらかじめ事前に、リストで渡したデータだけを受け取り、ほかのものは無視したいとする。 ML とか Haskell とかに慣れていると、自然と次のように書くことでしょう。

receiver(L) ->
  receive
    Msg when member(Msg, L) -> ...
  end.

でもこうは書けないっつーことです。 when 以降には任意の式が置けるわけじゃないから。

……when は特別なんですよと覚えればいいじゃないって思いましたか? でもね、 Erlang では、実は if 式というのもパターンガードの集まりであるという原理がある。したがってif の条件部には任意の式は置けません。これはけっこうびっくりしませんか?

1> if
     member(1, [1,2,3,4,5]) -> ok;
     true -> fail
   end.
** 1: illegal guard expression **

どうするかというと、こういう場合には case を使えばいい。

1> case member(1, [1,2,3,4,5]) of
     true -> ok;
     _    -> fail
   end.

それじゃ、 receive でガードする場合はどうでしょう。ガードが成立してもしなくても処理をする場合はともかく、ガードが成立しなくて今この場では処理したくないメッセージはどうればいいんですかねー。自分で自分に send するのかしら?

receiver(L) ->
  receive
    Msg -> case member(Msg, L) of
             true -> ...;
             _    -> self() ! Msg
           end
  end.

もっと複雑なパターンの場合にメッセージの順番が維持されるかとかが気になるけれど、ひとまずこう書けばいいのかなー。

でもなんで、パターンガードにそんな制約を加えているんだろう。確かに、任意の関数を許すよりはパフォーマンスは上がるのかもしれないが、なんかちょっと微妙。