コンテンツへスキップ
GitHubリポジトリ フォーラム RSS-ニュースフィード

RubyプログラマーがCrystalを使うべきトップ5の理由

Mark Siemers

これは、Crystalブログ記事のシリーズを提案してくれたMark Siemersによるゲスト投稿です。さらに多くの投稿が期待されます。または、もっと良いことに、ご連絡ください。あなた自身の投稿を書いてください。

1. 極めて低い学習曲線

過去5~10年で人気になった言語を考えてみてください。思い浮かぶのは?Elixir、Go、Rustでしょうか?これらはすべてRubyよりもパフォーマンス上の利点がありますが、学習と習得がより困難です。

はるかに簡単な学習曲線で、パフォーマンスの向上を得ることができたらどうでしょうか?

どのくらい簡単か?いくつかのコードを見てみましょう。

Q: 以下のモジュールのうち、どれがRubyで記述されていますか?どれがCrystalで記述されていますか?

module Year
  def self.leap?(year)
    year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)
  end
end
module Hamming
  def self.distance(a,b)
    a.chars.zip(b.chars).count{|first, second| first != second }
  end
end

A: これはトリッククエスチョンです - 両方です。上記のモジュールはRubyまたはCrystalで動作します。なんてクールなんでしょう!

コードの類似性の詳細な例はこちらをご覧ください。

これは、すべてのRubyコードがCrystalで動作する(そしてその逆も)という意味ではありませんが、Crystalではすぐに多くのことができるようになり、初日から、いや、最初の数分から生産性を上げることができます。

Crystal(強く型付けされたコンパイル済み言語)は、どのようにRuby(動的かつダックタイピングされた言語)のように動作するのでしょうか?Crystalのコンパイラは、型推論ユニオン型という2つの強力な技術を組み合わせて使用します。これにより、コンパイラはRubyのようなコードを読み取り、使用する正しい型を推論できます。

類似点に加えて、CrystalはRubyよりもいくつかの重要な利点を提供します。利点としては…

2. コンパイル時のチェックとメソッドオーバーロード

Rubyでコードが壊れないようにするために、is_a?またはrespond_to?を書くのは、ハッキーな感じがしますか?チェックを入れていない場所を心配したことはありますか?それらはすべて発生する可能性のあるバグですか?

Crystalはコンパイル済み言語であり、すべてのメソッドの入力と出力をコンパイル時にチェックします。型が一致しない場合は、実行時前に検出されます。

上記のYear::leap?の例を再検討してみましょう。Rubyでは、入力が整数でない場合に何が起こりますか?

Year.leap?("2016") #=> false
Year.leap?(Date.new(2016, 1, 1)) #=> undefined method `%' for #<Date: 2016-01-01 ... >

Stringの場合、間違った答えが得られ、Dateの場合、ランタイム例外が発生します。Rubyで修正するには、少なくとも1つのis_a?文が必要です。

module Year
  def self.leap?(input)
    if input.is_a? Integer
      input % 400 == 0 || (input % 100 != 0 && input % 4 == 0)
    elsif input.is_a? Date
      input.leap?
    else
      raise ArgumentError.new("must pass an Integer or Date.")
    end
  end
end

そのメソッドはうまく見えますか?より役に立つエラーメッセージが表示されるようになっただけですが、ランタイム例外が発生する可能性はまだあります。

Crystalでは、入出力の型を明示的に指定することができます。メソッドのシグネチャをself.leap?(year : Int)に変更すると、入力が整数であることが保証されます。

実行時ではなく、コンパイル時に役に立つメッセージが表示されます。

Year.leap?("2016")
Error in line 10: no overload matches 'Year.leap?' with type String
Overloads are:
 - Year.leap?(year : Int)

Time(RubyのDateTimeと考えてください)のサポートをモジュールに追加する場合は、Year::leap?をオーバーロードできます。

module Year
  def self.leap?(year : Int)
    year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)
  end

  def self.leap?(time : Time)
    self.leap?(time.year)
  end
end

Rubyと同様に、メソッドオーバーロードにより、入力の柔軟性を確保できますが、ダックタイピングの推測作業は必要ありません。コンパイル時のチェックにより、型ミスマッチエラーが本番環境に到達するのを防ぎます。

本番環境といえば…

3. 驚くべき高速なパフォーマンス

コンパイルのもう1つの利点は、速度と最適化です。RubyとCrystalのパフォーマンスを比較する場合、多くの場合、パーセンテージではなく桁数で表すことができます。

ある例では、Crystalで乱数を合計する処理は、Rubyよりも10桁速い場合があります(約3700万%高速)。これは、コンパイラの最適化と、Crystalでプリミティブなデータ型を使用できることによるものです。これは、大きな数の整数オーバーフローのリスクを伴います。(Aryの説明を参照)

Crystalの組み込みHTTPサーバーは、ベンチマークテストで毎秒200万件以上のリクエストを処理することができました。そして、多くのウェブフレームワークは、ウェブアプリケーションに対して一貫してミリ秒以下の応答時間を提供しています。

次の点につながります…

4. あなたが望むウェブフレームワークはすでにここにあります

Rails(あるいはElixirのPhoenix)の完全性に満足していますか?Amberフレームワークを使えば、すぐに馴染むでしょう。

Sinatraのシンプルさとカスタマイズの容易さが好みですか?Kemalでは、そのシンプルさを見つけることができます。

強いパラメータ、HTTP動詞、データベースクエリに対してコンパイル時チェックを利用するフルスタックフレームワークが必要ですか?それはあなたのLuckyな日です。

1月中に、これらのウェブフレームワークのそれぞれが専用の投稿で取り上げられます。詳細については、Crystalブログを再確認してください。

5. CrystalはCrystalで書かれています!言語の理解と貢献が容易です

Rubyのソースコードを読んだことがありますか?いくつかのEnumerableメソッドの動作を理解しようとしましたか?

RubyのEnumerable#all?の実装

static VALUE
enum_all(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
    return memo->v1;
}

そのコードが何をしているかを理解するのにどれくらいの時間がかかりますか?Cコードを扱ったことがない場合は、おそらく非常に長い時間がかかります。

CrystalのEnumerable#all?の実装と比較してみましょう。

def all?
  each { |e| return false unless yield e }
  true
end

それを理解するのにどれくらいの時間がかかりましたか?RubyまたはCrystalを知っていれば、おそらく数秒です。

これを踏まえると、**Crystalの98.4%はCrystalで記述されており、わずか0.3%がC++で記述されている**ことに注目してください。

Rubyは、30.6%がCで、64.8%がRubyで記述されています。

実際、CrystalでC++で記述されているファイルは1つだけであり、LLVM拡張機能を変更していない限り、Crystal言語で探しているものは事実上Crystalで記述されていることが保証されています。

Crystalの読み取り、理解、および貢献は、見つけることができるほぼすべての言語よりも簡単です。

どこから始めれば良いですか?

Crystalプログラミング言語を試したい場合は、開始するための優れたリソースを以下に示します。