こんにちは。
初心者の所感というのは意外と大切で、初心者でなくなってしまうと得られた知識が空気のようになってしまい、忘れられてしまうことがあります。なので、Crystalにさわりはじめてから半年ぐらいの段階で、Rubyとここが違うなと感じたことをメモしておこうと思います。基本的にポエムです。
半年ぐらいと銘打ってますが、その後も更新しています。
Crystalにさわり始めてから半年ぐらい
大クラス主義の配列とHashがないけどなんとかなる
Rubyだと、大クラス主義の配列がいろいろな役目を果たしてくれます。配列に異なるクラスのデータを詰め込んで、あとから簡単に追加したり削除することができます。Crystalだとできません。
これは実はそこまで大きな問題ではなくて、記述量は多くなりますが、雑多なクラスを配列やHashに詰め込む変わりに、自作のクラスを定義したり、構造体を使ったり、タプルを使うことでだいたい解決できると感じます。
evalが使えない
わたし、よくわかってないんですが、直感的にはこれがRubyとCrystalの本質的な違いではないかという予感がしています。CrystalでEvalを実行するためには、Crsytal本体を実行ファイルに含めて、都度コンパイルするような必要があると思います。それは、今後どれだけCrystal言語が発展しても困難でしょう。
Crystal言語は実行速度はたいへん高速ですが、コンパイル時間が短いとは言い難い言語です。ですから、Crsytalコンパイラを実行ファイルに含めて、その都度コンパイルするようにすると相当遅くなりそうな気がします。実際どうなんだろう? ひょっとするとRubyと
同じ速度に収束していくかもしれません。あと、Ruby言語ではevalの多用はバッドプラクティスとして嫌われていますが、プログラミング学習のためにはむしろ、そういう難しいevalを多用するコードを書いてみた方がいいのかも知れないなと思いました。自分自身の出力をコードとして実行するquineとかもそういった性質を持つコードの一つかもしれません。
戻り値の型を1種類にするか、複数にするか
メソッドを実装する時、Rubyの場合は戻り値は多くの型を取るようにするのが親切です。ところがCrystalでは1つの型だけ返す方が便利だと感じます。例えば、alignment.tag("TAG")
のようなメソッドを考えたとき、Rubyの場合、戻り値の型はタグに応じて Integer
、Float
、String
、Nil
を取るとしても、Crystalでは alignment.tag_int( "TAG1")
alignment.tag_float("TAG2")
alignment.tag_string("TAG3")
といったように、それぞれの型を返すメソッドを別に用意した方が親切であると感じます。
利用者は自分が取りたい型を知っているので、使わないとわかってる型のために余計な記述(たとえばオーバーロードなど)をしないとコンパイルが通らないのは苦痛です。とすると、CrystalとRubyでは提供するべきメソッドの粒度は違うということになると思います。
nilを許容するかしないか
上記のようにRubyとCrystalでは戻り値の単位(したがって作成するべきメソッドの粒度)が異なります。ここに輪をかけて難しくなるのが、nil
の扱い方です。CrystalはNil許容型を作ることができます。しかし、nilを許容してしまうと、その都度オーバーロードが発生してしまうので、基本的にはnilを含まないようにするのが得策だと思います。
Makefile文化
Rubyでは作業の自動化にRakefileを用いますが、CrystalではRakeに相当する共通ツールが存在せず、一般的にMakefileが用いられています。(これは必ずしもCrystalのユーザーがmake
が好きだということを意味しているのではなく、単純にコミュニティのサイズが小さくてrake
のようなコマンドを作成できていないだけだと思います)個人的にはこれがストレスで、CrystalにもRakeみたいな便利なツールがあるといいなと思います。
Ruby-FFI と Crystal のCバインディングではメモリ解放用メソッドの記述場所が違う
私はRuby-FFIを使ったバインディングを多く作成しているのですが、Ruby-FFIとCrystalのCバインディングでは、ガーベジコレクション(メモリの自動解放)の実装単位が異なることに気が付きました。Ruby-FFIでは中レベルのAPIであるFFi::Structに対して、self.release
を実装しますが、Crystalでは高レベルのAPIで用いるクラスに直接 finalize
を記述します。これはほとんどの人にとっては使わない情報だと思いますが、私はCのバインディングをたくさん作るのでRubyとCrystalの違いとして印象深く感じました。
今の段階でよくわかっていないと感じること
- ClassとStructの使い分け
- マクロでどこまでできるか
- マクロでは each ではなく for 文であるという事実
この記事は以上です。