Rubyでcall by nameっぽいこと

Algol60の引数機構であるcall by nameは今ではすっかり廃れて若い人には知られていないと思われるが、一言で言うと「クロージャを引数に渡す」ということで、クロージャの表記が非常にコンパクト。変数や式がそのままクロージャになる。


Algol60のサンプルプログラム*1
VALUE指定のfromとtoがcall by valueで、それ以外のsumとvarとexprの引数がcall by name。

BEGIN
  INTEGER m,n;
  PROCEDURE sub(sum, var, expr, from, to);
  VALUE from, to;
  INTEGER sum, var, expr, from, to;
  BEGIN
    sum := 0;
    FOR var := from STEP 1 UNTIL to DO sum := sum + expr;
  END;

  sub(m, n, n*n, 1, 10);
  print(m);
END

少し解説すると、subでsumに代入すると実引数であるmへの代入となる。varへの代入も同じくnへの代入となる。これだけならcall by referenceでも同じだ。違うのは、exprを評価すると、その時点で呼び出し側環境でn*nを評価してその結果を値とする。つまりn*nがクロージャとしてsubにexprという名前で渡ったことになる。


対応するRubyプログラム:
call by name の代わりに、値をセットするクロージャと値をゲットするクロージャのペアを渡す。式に対しての値のセットはエラーにする。

def sub(setsum,getsum,setvar,getvar,setexpr,getexpr,from,to)
  setsum[0]
  for i in from..to
    setvar[i]
    setsum[ getsum[] + getexpr[] ]
  end
end

m=nil
n=nil
sub( lambda{|x| m=x}, lambda{m}, lambda{|x| n=x}, lambda{n},
     lambda{raise "bad use"}, lambda{ n*n }, 1, 10 )

puts m

うーん、これは手で書く気がしない。

*1:ENDの直前のセミコロンは不要