Admittedly something small.
2016年9月22日

【短編ポエム】Haskell/PureScriptの関数呼び出しはRuby/Python/JavaScriptよりイケている

Ruby Python JavaScript Haskell ポエム


Proc#callがださい

たとえば、ふたつの引数の和を出力する関数addsumについて考えてみます。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ではメソッドをそのまま他の変数に代入したりはできず、proclambdaProcオブジェクトを作ってから渡さなければなりませんし、受け取った側も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では何故こんなことができるのか、上のコードの各部はどういう意味なのか、どんな順序で実行が進んでいくのか、自分で考えてみると面白いかもしれません

それに、上のPureScriptのコードを良く見ると、ピリオドもなければコロンもないし、カンマのひとつすら存在しません。予約語はコードブロックの始まりを告げるdoのみ。Pythonのようにインデントでブロックが表現されているなど、異様なまでにコードの見た目がスッキリしているのがわかると思います。これもHaskell/PureScriptのコードの特徴です。極限まで構文上のノイズが絞られていて筆者はすごく読みやすいと思うのですが、何故か読みにくいと言われることもあります。

これは文法の問題なので、もしかしたらこれらを両立させる文法をもった言語が将来開発されるかもしれません。

15年以上前からあります。使っている人が少なすぎて、あまり知られていませんが……。

引数がないときの括弧を省こうとするなど、Rubyを使う人はそういう構文上のオーバーヘッドを嫌う人が多いのかもしれません。Haskell/PureScriptはとにかく構文が簡潔ですから、Rubyが好きな人にも向いているんじゃないかと。このような細かいところは正直どうでもいいと私は思うのですが、私は普段『同期的な作用と非同期な作用をモナドで抽象化すると一貫性が生まれて便利だよ』みたいな重大なポイントについて利点の説明をしては『意味わかんねーよ文章長えよカス!』という感じで罵られているので、今回は『関数の引数の有無や第一級かどうかについて便利さと単純さが両立するよ』という、わかりやすいポイントについてなるべく簡潔に説明をしました。


^ もっとも、Haskell/PureScriptを知らない人は絶対にわからないと思いますが。なお、これはPureScriptのコードなので、実行の順序がぐちゃぐちゃになるHaskell特有の変態仕様『遅延評価』は関係がありません


このエントリーをはてなブックマークに追加