Proc#callがださい
たとえば、ふたつの引数の和を出力する関数add
やsum
について考えてみます。PythonやJavaScriptなら次のようなコードを書くことができます。
def add(x, y):
print(x + y)
sum = add # そのまま代入できる
add(1, 2)
sum(1, 2) # addとsumの呼び出し方に違いはない
function add(x, y){
console.log(x + y);
}
sum = add // そのまま代入できる
add(1, 2)
sum(1, 2) // addとsumの呼び出し方に違いはない
しかし、Rubyではメソッドをそのまま他の変数に代入したりはできず、proc
やlambda
でProc
オブジェクトを作ってから渡さなければなりませんし、受け取った側もcall
をつかって呼ぶ必要があります。これはapply
メソッドを呼ばなければならないJavaあたりでも同じですね。
def add(x, y)
puts (x + y)
end
sum = proc{|x, y| add(x, y)} # procやlambdaで包む必要がある
add 1, 2
sum.call 1, 2 #callで呼び出す必要があり、addとsumの使い方が異なってしまう
なるほどこれはダサい。でもこれにはRuby側の反論もあって、Rubyでは引数がないときはメソッド呼び出しで括弧を省略できるという利点もあります。
function answer(){
console.log(42)
}
answer() // 括弧がいる
def answer
puts 42
end
answer # 括弧がいらない
私もちょっとダサい、っつーか扱い辛いと感じることがあります。が、これは文中にも書いてある通り、Rubyに価値を与えている独特なスタイルとのトレードオフでしょう。
JavaScriptやPythonにこのような括弧の省略を導入しようとしても、関数オブジェクトそのものを表す構文と衝突してしまうので不可能です。そんなわけで、これを見る限り必ずしもどちらがいいと言い切れないようにみえるのですが……。
Haskell/PureScriptでは
Haskell/PureScriptでは、このジレンマが解決します。
add x y = logShow (x + y) -- addには引数があります
sum = add -- 関数は第一級なので、普通に代入できます
answer = logShow 42 -- answerには引数がありません
fortyTwo = answer -- answerの値も第一級なので、そのまま別の変数に代入することもできます
main = do
add 1 2 -- 3が出力されます。引数がある場合はもちろん普通に呼び出せます
sum 1 2 -- 3が出力されます。addとsumに呼び出しかたの違いはありません
answer -- 42が出力されます。引数がない場合も、もちろん括弧はありません
fortyTwo -- 42が出力されます。fortyTwoとanswerは同じものを指しているからです
Rubyの『引数のない関数呼び出しで括弧を省略できる』という利点と、Python/JavaScriptの『通常の関数呼び出しと、関数オブジェクトの呼び出しを区別しない』という利点の、ふたつの利点が両立しています。ふしぎ!Haskell/PureScriptでは何故こんなことができるのか、上のコードの各部はどういう意味なのか、どんな順序で実行が進んでいくのか、自分で考えてみると面白いかもしれません1。
それに、上のPureScriptのコードを良く見ると、ピリオドもなければコロンもないし、カンマのひとつすら存在しません。予約語はコードブロックの始まりを告げるdo
のみ。Pythonのようにインデントでブロックが表現されているなど、異様なまでにコードの見た目がスッキリしているのがわかると思います。これもHaskell/PureScriptのコードの特徴です。極限まで構文上のノイズが絞られていて筆者はすごく読みやすいと思うのですが、何故か読みにくいと言われることもあります。
これは文法の問題なので、もしかしたらこれらを両立させる文法をもった言語が将来開発されるかもしれません。
15年以上前からあります。使っている人が少なすぎて、あまり知られていませんが……。
引数がないときの括弧を省こうとするなど、Rubyを使う人はそういう構文上のオーバーヘッドを嫌う人が多いのかもしれません。Haskell/PureScriptはとにかく構文が簡潔ですから、Rubyが好きな人にも向いているんじゃないかと。このような細かいところは正直どうでもいいと私は思うのですが、私は普段『同期的な作用と非同期な作用をモナドで抽象化すると一貫性が生まれて便利だよ』みたいな重大なポイントについて利点の説明をしては『意味わかんねーよ文章長えよカス!』という感じで罵られているので、今回は『関数の引数の有無や第一級かどうかについて便利さと単純さが両立するよ』という、わかりやすいポイントについてなるべく簡潔に説明をしました。
-
もっとも、Haskell/PureScriptを知らない人は絶対にわからないと思いますが。なお、これはPureScriptのコードなので、実行の順序がぐちゃぐちゃになるHaskell特有の変態仕様『遅延評価』は関係がありません ↩