電柱日報

日々の由無し事

プログラミング遍歴と Crystal

BASICを齧った小・中学期

以前も書いたような気もしますが,自分のプログラミング経験は小学生の頃MSX-BASICから始まりました。

当時,両親はゲームしかできないファミコンは買ってくれませんでしたが,曲がりなりにも「パソコン」という看板を背負っていたMSX2であれば買ってくれるというので,当時発売されたばかりの「FS-A1」を買ってもらいました。

当時はプログラミングがしたかったわけではなく,ファミコンほどではないもののMSX向けに発売されていたゲームがやりたかっただけでして,「ドラゴンクエスト1」などはファミコン版ではなくMSX版をクリアしたクチです。その後,外付けのフロッピードライブを買ってもらい「Wizardry」や「Ys」などのゲームを楽しんでいたのですが,いかんせん小・中学生のお小遣いでは新しいゲームは年に1本買えるかどうかというところです。

いろんなゲームで遊びたい,しかし何本も買うようなお金はない,という中でゲーム情報を仕入れる為に買っていたMSX FAN誌に投稿プログラムのコーナーがあることに気がつきます。プログラムのことはよくわかりませんでしたが,写経のように20行ちょいのプログラムを誌面の通りに打ち込めば*1ちょっとしたゲームで遊べるというのは,お金のない当時の自分には福音だったのだと思います。やがて,よくわからないなりに「この数字を変えると残機が増える」「こっちをいじると攻撃力が上がる」といった感じにソースコードを弄る(いわゆるチート)ようになります。

C言語を覚えた高専

中学を卒業して高専に入ると当時のコンピュータセンタにあったのが Sharp の MZ-5500 シリーズで,最初に実習で習ったのはFortranでしたが,これはその後まったく触れておらず,今ではすっかり記憶の彼方に去ってしまっています。

高専では電子工学系の学科であったため*2,学科独自のPC教室もありこちらは EPSON の PC-286が配置されていました。そこでは C言語のプログラミング演習があり,高専の卒業研究と専攻科の特別研究はC言語非線形関数をルンゲクッタ法で解いたりしています。

公私にわたりプログラミングを使い始めた就職初期

専攻科を卒業して就職すると,UNIX系(職場ではSoralisがメインでした)サーバのお守りをすることになります。

UNIXの世界ではテキスト処理を行う機会が増え,テキスト処理に強い,という理由でPerlに手を出すことになります。

また,1990年代後半は比較的簡単に個人でWebサイトを持てるようになった時期でもあり,プライベートではCGIを動かせるレンタルサーバを借りてPerlでゲーム系のBBSやユーザが登録可能なコンボ集なんかを作って公開していたりもします。

また,Javaに手をだしたのもこの頃で,目的はWebでアプレットを使いたいがためでしたが,C言語の構造体に関数までついたようなオブジェクト指向の考え方には痛く感動したことを覚えています。

オブジェクト指向を模索してRubyにたどり着いた就職中期

Javaオブジェクト指向の考え方に触れ,お手軽なインタプリタでもオブジェクト指向的な機能を使えないかと模索を始めます。

最初に試したのはPerlオブジェクト指向拡張でしたが,当時の自分には如何にもアドホックで不自然なものに思えて断念しました。

Javaをサーバへ導入しようとしたり,C++に手を出して文字列操作に泣きそうになったりしながらたどり着いたのが Ruby でした。

プリミティブな値が基本的に存在せず「すべてがオブジェクト」という Ruby の考え方は,非常にしっくりくるもので,その後10年以上,仕事でもプライベートの工作でもRubyをメインに使用していくことになります。

Ruby の柔軟さを持て余してしまった大学院期

Ruby を使い始めて数年たち,齢30を数えたあたりで思うところあって社会人大学院生として学生との2足の草鞋を履くことになります。

仕事柄,研究テーマはネットワーク関連でしたが,その中で,お手製のネットワークシミュレータを作ることになり,Rubyを使って最低限の機能を実装したのですが,その中で Ruby の(より正確には Ruby を使う自分の)限界が見えてきました。

例えば,Ruby は徹底して動的な型システムを持つ言語です。メソッドはどんな型の値であろうとも引数として受け入れてしまうため,想定した型とは異なる引数が与えられたとしても実行時にエラーにもならず,不可解な結果だけが出力される場合があります。もし特定の型のみを受け入れるメソッドを実装するには,メソッド側で引数の型をチェックする必要があります。

おそらく,ダックタイピング環境下では型ではなくメソッド実装の有無で引数を制約できるようなクラス設計を行うべきなのでしょうが,もう少し簡単に「引数の型を制約できるRuby」を求めるようになります。

そして Crystal と出会った現在

Crystal という名前のプログラミング言語 があります。

crystal-lang.org

Ruby と見間違えるほどよくにた構文を持ちながら,静的な型システムを持ち,LLVMを使用して高速なバイナリ形式の実行ファイルにコンパイル可能な言語です。また,静的型システムを採用していますが,コンパイラがある程度変数の型を推定してくれるので,必ずしも変数の型やメソッド引数の型を指定する必要はありません。

Ruby との構文類似性でいえば,型の定義,メソッドチェーン,イテレータブロックなど,Crystal は Ruby の特徴的な構文をそのまま受け継いでいます。例えば,以下は完全に動作するCrystalのソースコードです。

module OneToN
  def self.get(n)
    1..n
  end
end

OneToN.get(3).each do |i|
  puts i * 2
end
#=> 2
#   4
#   6

Ruby をご存知の方であればお分かりの通り,このソースコードRubyインタプリタで実行しても同様の結果が得られます。

とはいえ,必ずしもRubyのコードがそのままCrystalで実行できるとは限りません。そもそも,CrystalはCrystalで独立したプログラミング言語でありそれ自体の哲学をもっており,Crystal自身もRubyとの互換性を最終目標には掲げてはいません。

実際,ほとんどの場合 Rubyソースコードは型について曖昧さを抱えており(必ずしもそれは悪いことではなく,柔軟さとのトレードオフなのでしょう)Crystalのコンパイラでは実行できないでしょう。また,メタプログラミングを可能にするような Ruby の柔軟さは Crystal では利用できないことが多いです。

一方で,コンパイラがメソッド引数の型を予め認識できるので,Ruby では不可能だった メソッドのオーバーロードなど,Crystal では可能な機能も存在します。

これは,ある意味で自分が求めていたプログラミング言語のほぼ理想形でした。

使い始めた当初こそ Ruby との小さな違いに(非常に似ているため余計に)つまずいたりもしましたが,いまではすっかり Crystal にどっぷり浸かった日々を送っています。職場の本番環境でも一部 Crystal で書いたツールが動いていたりしますし,そのために実装したいくつかの小品を Shard(Ruby でいう Gem)としてGitHubへ公開したりもしています。

Crystal はまだ開発途上にあるプログラミング言語で,しばしば破壊的なAPIの変更が加わる状況ではありますが,バージョン1.0が登場したあかつきには,もう少し多方面に Crystal を浸かってみようと思っています。

*1:当時,「1画面プログラム」というMSXの標準的な画面表示に収まるサイズの部門がありました

*2:当時は情報系の学科がなく,電子工学系がプログラミングなんかをやってました