Crystalの紹介:Cのように速く、Rubyのように滑らか

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

   

Crystalの紹介:Cのように速く、Rubyのように滑らか

僕は、Rubyistだ。Rubyと、そのコミュニティ、その生産性など、Rubyにまつわる多くのものがとにかく大好きだ。僕がRuby専門で書いてきて現在で4年以上になる。そして、これからも続けていきたい。だが言語やツールはいずれ置き換えられていくということもわかっている。
Rubyは素晴らしいが、必ずしもその速さで知られているわけではない。時に、要求が厳しいアプリケーションには適さないこともある。また、Ruby 3×3が出るまでしばらく待たなければならない。

ここで、あなたに質問がある:

「あなたは、Rubyのように滑らかで、Cのように速いプログラミング言語を夢見たことはあるだろうか?」

正直に言って、僕はそういうものを常に夢見ていたし、なぜそれが存在しないのだろうと思っていた。そんなことを思っていると、Crystalを見つけた。まだその日のことをはっきり覚えている。それは2015年7月だった。僕は/r/programming(訳注: Reddit のプログラミング板)を読んでいて、「Crystal: Cのように速く、Rubyのように滑らか」といったようなものを見た。

だからそのサイトに行って、Crystalをダウンロードし、僕の初となるCrystal(実質Ruby)プログラムを走らせた。

crystal hello.rb
p "Hello World"

そうしたら動いたんだ!

Hello World

もう今までで最高に幸せなHello Worldだった。

Crystalを始める

さて、Crystalの目標をいくつか詳しく見てみるとしよう。

  • Rubyに似た構文を持っている(ただし、Rubyとの互換性はその目的ではない)。
  • 静的に型チェックされるが、変数またはメソッドの引数の型を指定する必要はない。
  • ボイラープレートコード(テンプレート的なコード)を避けるため、コンパイル時評価とコード生成がある。
  • 効率的なネイティブコードにコンパイルする。

ここで強調しなければならない重要な点がいくつかある。
まず、

Rubyに似た構文を持っている(ただし、Rubyとの互換性はその目的ではない)。

これは説明するまでもない。CrystalはRubyではない。Railsを実行することはできない。

静的に型チェックされるが、変数またはメソッドの引数の型を指定する必要はない。

Rubyとは違ってCrystalは型付き言語だが、ほとんどの場合型を指定する必要はない。たとえば、これを例に取ろう。

def greet(name, age)
  "I'm #{name}, #{age} years old."
end

greet "Serdar", 27 # I'm Serdar, 27 years old.

じゃあ、いつ何のために型を使うのか?

def add(x : Number, y : Number)
  x + y
end

# Ok
add 1, 2 # Ok

# Error: no overload matches 'add' with types Bool, Bool
add true, false

素晴らしい、コンパイル時エラーだ。x、yの型だけをNumberとして受け入れるようにメソッドを制限する。Rubyだと、これは実行時エラー、すなわち最悪の事態になるだろう。いいぞCrystal!

ボイラープレートコード(テンプレート的なコード)を避けるため、コンパイル時評価とコード生成がある。

マクロ好きな人~?Rubyはメタプログラミング機能で有名だ。Crystalはマクロを使用してボイラープレートコードを減らしてくれる。以下の例はCrystalの素晴らしいWebフレームワーク、Kemalのものだ。

HTTP_METHODS = %w(get post put patch delete options)

{% for method in HTTP_METHODS %}
  def {{method.id}}(path, &block : HTTP::Server::Context -> _)
   Kemal::RouteHandler::INSTANCE.add_route({{method}}.upcase, path, &block)
  end
{% end %}

KemalでDSL宣言がどのように行われているかがこちらだ。配列HTTP_METHODSをループして各HTTPメソッドのメソッドを定義している。ところで、マクロはコンパイル時に評価される。つまり、パフォーマンスに不利な条件はない。

効率的なネイティブコードにコンパイルする。

Crystalはコンパイラ型言語だ。僕はコンパイラを持っている利点に深く突っ込んでいくつもりはないが、確実に言えるのは、それが多くの最適化を無料で与えてくれるということだ。さらに、Crystalプログラムをコンパイルすると、効率的な単一ファイルのネイティブコードになる。すごく便利で簡単に実行/展開できる。

Crystalプログラムをコンパイルする方法は次の通りだ。

crystal build program.cr

これは、実行可能なものと同じ名前で、実行可能な単一のネイティブバイナリを生成する。

./program

すごい!

Crystalの素晴らしい標準ライブラリ

Crystalには素晴らしい標準ライブラリとツールが搭載されている。最近のアプリケーション構築に必要なすべてのものが揃っている。CSV、YAML、JSON、HTTP、さらにはWebSocketもCrystal自体に同梱されているので、何かを構築し始める際にはすごく簡単だ。

Webサーバが必要だって?大丈夫!

# server.cr
require "http/server"

server = HTTP::Server.new(8080) do |context|
  context.response.content_type = "text/plain"
  context.response.print "Hello world, got #{context.request.path}!"
end

puts "Listening on http://0.0.0.0:8080"
server.listen

たった5行(LOC)で、機能的なwebサーバ:crystal server.crを構築できる。
localhost:8080にアクセスしよう。

crystalコマンド自体もすごく便利だ。利用可能な組み込みのコードフォーマッタ:crystal tool format your_app.crもある。
最も驚くべき素晴らしいコマンドはcrystal playだ。これは要するにプレイグラウンドで、Crystalコードをすばやく実行して即時のフィードバックを得ることができる。では、それを実行してlocalhost:8080に移動しよう。

Crystalのパフォーマンス

Crystalは、Rubyのように滑らかでパフォーマンスは高いという独特の目標を持っている。

僕はベンチマークが大好きだが、ベンチマークを疑ってかかるべきだとも思っている。

これはCrystalのための単純なフィボナッチ実装だ(有効なRubyコードでもある):

# fib.cr
def fib(n)
  if n <= 1
    1
  else
    fib(n - 1) + fib(n - 2)
  end
end

puts fib(42)

これを実行してどのくらいかかるか見てみよう!

time crystal fib.cr
433494437
crystal fib.cr  2.45s user 0.33s system 98% cpu 2.833 total

これは有効なRubyコードでもあるので、今回はRubyでも実行してみよう。

time ruby fib.cr
433494437
ruby fib.cr  38.49s user 0.12s system 99% cpu 38.718 total

Crysitalは2.833秒で完了。 Rubyは38.718秒で完了。かなりすごい。無料で20倍のパフォーマンスが得られるのだ。最適化を有効にしてプログラムをコンパイルしたらどうなるだろうか?

crystal build --release fib.cr
time ./fib
433494437
./fib  1.11s user 0.00s system 99% cpu 1.113 total

1.113秒。今回はRubyよりほぼ35倍も速い。それってすごくない?そうこなくちゃ!Crystalコンパイラは、LLVMを使用して優れた最適化を行っている。

あなたが(僕のように)ベンチマークに関心があるのなら、crystal-benchmarks-gameレポジトリをチェックするといい。

注意:「The cake is a lie(ケーキは嘘) 」、ベンチマークも同じだ。常にパフォーマンスが35倍向上するということはないが、複雑なアプリケーションでは5倍以上、CPU集中型ではそれ以上のパフォーマンスが期待できる。

Crystalの同時並行性

Crystalでは、メインの実行をブロックすることなく、バックグラウンドで何かを動作させる(別名async)には、キーワードspawnを使用する。これを実現するために、spawnはFiberと呼ばれる軽量のスレッドを作成する。Fiberの作成は非常に安価で、その実行はプロセスによって内部的に管理される。単一のコアに何万ものFiberを簡単に作成することができる。

さて、僕たちはバックグラウンドで稼働させるためにspawnを使うことができるが、Fiberから何かを送受信するにはどうすればいいのだろうか?ここで登場するのがChannelだ。GoのChannelsに精通している方なら、すっかり馴染みがあるように感じるだろう。

FiberはこのChannelを介し、実行し、メッセージを送信し続けることができる。同じChannelから受信されそうな人に実行制御が与えられる。そのうちの1つが受信、実行されると、他のスポーンされたFiberが実行できるように、スケジューラに制御が戻される。このようにして「ping」と「それに対する返信(ponging)」をし続けることができる。

ピンポンと言えば、「Go by Example」サイトからのこんな抜粋がある。

package main
import "fmt"

func ping(pings chan<- string, msg string) {
    pings <- msg
}

func pong(pings <-chan string, pongs chan<- string) {
    msg := <-pings
    pongs <- msg
}

func main() {
    pings := make(chan string, 1)
    pongs := make(chan string, 1)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}

そしてCrystalバージョン:

def ping(pings, message)
  pings.send message
end

def pong(pings, pongs)
  message = pings.receive
  pongs.send message
end

pings = Channel(String).new
pongs = Channel(String).new
spawn ping pings, "passed message"
spawn pong pings, pongs
puts pongs.receive # => "passed message"

僕にとっては、Crystalのバージョンのほうがより自然で読みやすい。僕は、ほとんどのRubyistが同じように感じるのではないかと思っている。

CrystalはRubyistのためだけじゃない

Crystalはシンプルで学びやすく高性能な一般的なプログラミング言語であり、妥協することなくこれらすべてが独自に組み合わさっている。CrystalはRubyistのためだけのものじゃないのだ!

Crystalで何を作ることができるだろうか?ゲーム、グラフィックレンダラ、低水準エージェント、Webアプリケーションなど。次の輝くプロジェクトがCrystalになるのは本当にあなた次第だ! Crystalで構築されたプロジェクトを調べたい場合は、<crystalshards.xyz>をチェックしよう。 Crystalのプロジェクトを見つけるにはもってこいの場所だ。

おまけ:次のプロジェクト名が見つからない場合は、ネームジェネレータを試してみよう!

リソース

ここまで来たあなたは、「次はどこに行けばいいの?」と疑問に思っているかもしれない。もちろん、公式のCrystal本は学習を始めるのにうってつけだ。Crystalへの旅をブートしてくれる無料の本に、Crystal for Rubyists(RubyistのためのCrystal)がある。また、Crystalにはコミュニケーションをするための活発なGitterルームがある。そこで会おう!

原文:https://blog.codeship.com/an-introduction-to-crystal-fast-as-c-slick-as-ruby/(2017-4-18)
※元記事の筆者には直接翻訳の許可を頂いて、翻訳・公開しております。

 -Tech

FAworksではプロのコンサルタントが案件をお探しします

  関連記事

チーム作業の効率を大幅にアップしてくれるWebサービスまとめ

チーム作業では、一人で黙々と作業をするのとは違ったスキルやコツが必要。 プロジェクトの人数が増え、作

MySQL Cluster―MySQLはいかに2億QPSのスケーリングを実現したか

この記事は、オラクル社MySQL主任プロダクトマネージャ、アンドリュー・モルガン氏からのゲスト投稿で

「AIって何?」なんて今さら聞けない!最低限抑えておきたいこれからの技術トレンド4つ(最新版)

【関連記事】 ❏これから必ず伸びる!最低限抑えておきたい技術トレンド3つ(2015年度版) ❏海外エ

webを利用してイケてるガールにデプロイする方法

webを利用してイケてるガールにデプロイする方法

エンジニアに出会いはない。 彼らが業務で関わりを持つのは、チームのメンバーとPCのみ。 そしてそのメ

1家に1人!旦那がエンジニアだと便利な5つのこと

よく便利なものに対して「1家に1台」とか言いますよね。 まさしくエンジニアはその「1家に1人」の便利

Dockerコンテナとイメージの仕組みを視覚化してみた

この記事は、Docker 102レベルを意図して書かれている。Dockerが何か分からない、または仮

待ち遠しい次の祝日がコマンドラインでわかる!‐cal‐ 端末にカレンダーを表示しよう

待ち遠しい次の祝日がコマンドラインでわかる!‐cal‐ 端末にカレンダーを表示しよう

これは“コマンドライン・マンデー”シリーズの最初の投稿です。このシリーズでは、毎週月曜日に使えるコマ

100万ppsを受信するプログラムを書くのはどのくらい難しいのか?【翻訳】CloudFlare ブログ

無料枠が充実していることでも人気なコンテンツデリバリネットワーク (CDN) を提供するCloudF

何が彼女をボットづくりに没頭させるのか?ボットの魅力と可能性/Emma Winston氏インタビュー

ボット初心者の方ですか?私たちは、今度主催するイベント「The Art of Bots」に先駆けて、

Dockerコンテナのためのテスト戦略

Dockerコンテナのためのテスト戦略

おめでとう!あなたはDockerイメージの作り方を知っていて、わかりやすいアプリケーションで複数のコ