プログラミングが楽しいかどうかは、文法が大きな役割を果たしているのは容易に想像できるでしょう。 Rubyプログラミングが楽しく感じる理由を文法から考察します。
ひと目見て最も異質な文法が、カッコなしのメソッド呼び出しでしょう。 Rubyでは
Faraday.get 'https://google.com'
のようにカッコ無しでメソッドが呼び出せます。 よく他言語利用者から「そんなにカッコを書きたくないのか」と揶揄されるわけですが、 カッコを書きたくないのもそうなんですが、それ以上に重要なメリットがあります。 それは、宣言的に見える記述をするのに必須だということです。
例えば、ActiveRecordで関連性を定義する記述は、
class User < ApplicationRecord
has_many :articles
end
と書きますが、これは has_many
というメソッド1に :articles
という引数でメソッド呼び出ししているだけなのに、
あたかも 宣言しているように見える ところがとても良いのです。
これをもし has_many(:articles)
と書いてしまったら 台無し なのです。
Rubyは、メソッド呼び出しで do-end
ブロックを渡すことができ、少し特殊な引数 &block
でアクセスできます。
[1, 2, 3].map do |n|
n * 100
end
# => [100, 200, 300]
似たような処理の固まりを与える仕組みは他の言語にもあり、関数ポインタやクロージャと呼ばれています。 しかし、他の言語のそれは関数の 引数のひとつ であり、Rubyのブロックは通常の引数とは別の特殊な引数であるという違いがあります2。 メソッド呼び出しにカッコを付けた場合、ブロックはカッコの外側に書かれます。
[1, 2, 3].reduce(5) do |sum, i|
sum + 1
end
# => 11
上記のコードはJavaScriptの場合このように書きます。
// 素のJavaScript
[1, 2, 3].map(function(n) {
return n * 100;
});
// arrow function
[1, 2, 3].map((n) => {
return n * 100;
});
// return の省略
[1, 2, 3].map((n) => n * 100);
近年だんだん記述が楽になってきましたが、処理が複数行になることも多いので2番めの記述をすることが多くなります。
見てわかるとおり、メソッド呼び出しの ()
の中に引数の一つとして書かれています。
カッコの外側に書かれることが実はものすごく重要な差を生み出しています。
その例として、Rubyの有名なテストフレームワークである rspec
と、それを真似たJavaScriptの jest
の記述を比較してみましょう。
describe SayHello do
it 'say hello world' do
expect(SayHello.hello('world')).to eq 'hello world'
end
end
同じ事をJavaScriptで書いてみます。
describe('SayHello', () => {
it('say hello world', () => {
expect(hello('world')).toBe('hello world');
});
});
美しいのはどちらか一目瞭然ですね。
JavaScriptでは、 () => {
や });
という 無意味な記号の羅列 が目に付きます。
it(
のカッコも邪魔ですし 'say hello world',
の ,
も邪魔です。
それに比べRubyは it
空白 タイトル 空白 do
という、 対称性の高い見た目になります。
JavaScriptは (
タイトル ,
という非対称で美しくないし、そもそも記号なので息が詰まる思いがします。
他言語利用者は「そんなこと・・・」と思うかもしれないけど、 そんなところが一番重要なんだ って気づいてほしい。
見た目の美しさは楽しさに繋がるし、実用面でも可読性(というかメリハリ)の高さはバグ発生の回避にもつながります。
Rubyの真髄はもしかしたら 空白 にあるのかもしれません。
expect().to eq 'hello-world'
というのも宣言的で良いですね。
とはいえ、このrspecはJavaScriptと比較しやすいように書いたコードで、まだ本気でありません。
describe SayHello do
subject { SayHello.hello 'world' }
it { is_expected.to eq 'hello world' }
end
本気を出した rspec
ではカッコが完全に消えてさらに宣言的になりました。
Rubyでは、破壊的なメソッドはメソッド名の末尾に !
、 真偽値を返すメソッドには ?
を付けると言う習慣があります。
!
の場合はそこまで厳密に運用されているわけではないですが、 ?
の方はほぼ100%守られている習慣です。
特にこの ?
が実に強力で、真偽値が返ってくるんだろうと予測できることもさることながら、なによりも その判別がしやすい のが非常に利点です。
すなわち、 ?
という文字は目に付きやすく、 isXXX
は目が滑りやすいという話です。
これも空白のときと同じように、メリハリがポイントなのかもしれません。
!
はRuby本体では破壊的なメソッドという意味で使われますが、
ライブラリやアプリケーションではそのような使われ方をすることは多くありません。
アプリケーションのクラスなどを書く場合、通常あまり非破壊なメソッドを書くことは無いからです。
その代わり、 それぞれのプログラマが思い思いのルールで利用しています。
例えば、ちょっとだけ違う動作をする本質的には同じメソッドを表現するために使うことがあります。
ActiveRecord
だと、 save
は失敗したときに false
を返し、save!
は失敗したときに例外を吐きます。
この明確だけど微々たる差異を、 !
を使わずメソッド名で表現なんてできそうにありません。
自分の場合、 !
は躍動感があるので、 本質的で特殊な動作 を表すのに使ったりします。
例えばテストコードにおいて3、HTTPリクエストをする や、 一連のブラウザ操作 などに !
付きのメソッド名を与えます。
RSpec.describe UsersController, type: :controller do
descrine 'GET #index' do
let(:request!) { get :index }
it do
request!
expect(response).to have_https_status :ok
end
end
end
!
の持つ異質性により、この「リクエスト」はなにかが違うぞ、と注意を引きます。
そしてその躍動感により、名詞ではなく動詞 であるとひと目で感づいてくれるはずです。
do_request
は2単語なのがダサいし、全ての文字が平凡すぎるのです。
ちなみに、実は文中にも出ている response
と対になる request
が元々存在し、 request
と言う名前は使えません。
そんな危機的状況をたった1文字で解決できるという点でも !
はとても偉大です。
Rubyの case
式はただの switch
文ではありません。
case foo
when XXX
と書いた時、中で XXX === foo
と言う判定がされます。
この ===
というのがかなりの曲者で、例えば String.===(x)
は x.is_a?(String)
と言う判定がされます。
この仕組みにより、 when
の横には本当にありとあらゆるものが書け、恐ろしい表現力を発揮します。
もちろん ===
は再定義可能なので、自分のライブラリで素晴らしい ===
を書くこともできます。
相当センスは必要ですが。
def odd?
-> (x) { x.odd? }
end
case foo
when /hello,.+/
when String
when odd?
when 10...100
when 1, 3, 5
end
これが全て読んで字のごとく期待通りの判定がされます。 ただ残念なのは、case式を使うこと自体があまりない ということです。
特にないです。
強いて言えばJavaScriptの
const a = 1;
const q = { a };
# => { a: 1 }
はそこそこ好きです。とは言えRubyには適応できなさそうです。
RubyはDSL言語と言われるように、あたかも宣言してるかの如き表現が自然にできる言語です。 カッコの省略が強力で、文法は基本的に空白区切りになっていて非常に読みやすく感じます。 あとは 型みたいなクソッタレなものが出現しない のも本当に最高ですね。