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

Auto

Ary Borenzweig

注:これはエイプリルフールの投稿でした。しかし、Crystalのマクロを使えば、これは可能です。

私たちCrystal開発者は、コンパイラは賢くなければならないと考えています。型注釈をどこにでも追加する必要はありません。必要な場合、または必要な場合にのみ追加します。コンパイラをさらに賢くすることはできるでしょうか?

この1ヶ月、私たちは、Crystalは比較的若い言語であり、標準ライブラリとエコシステムはまだ不完全であるため、まだコーディングすべきことがたくさんあると考えてきました。これらのアルゴリズムやデータ構造の多くが、他の言語に既に存在しているのは残念なことです。それだけでなく、これらの他の言語は長年使用されてきたため、その実装は非常に堅牢でバグがありません。私たちはCrystalでも同じ道を歩まなければならないでしょう。それとも、そうでしょうか?

まあ、もう違います。Crystalの次のリリースでは、小さくても強力な追加機能が搭載されます。それは、**auto**キーワードです。それがどのように機能するかを理解するために、実際に見てみましょう。

class String
  auto def succ
  end
end

"hello".succ #=> "hellp"

String#succ はCrystalの標準ライブラリにはないことを最初に知っておく必要があります。上記のコードでは、**auto**キーワードを使用して定義し、本文は空のままにしています。次に、いくつかの文字列でメソッドを呼び出すと、正しい値が返されます。素晴らしい! Crystalは、succ の戻り値の型を推測しただけでなく、その*動作*も推測しました!

**auto**の実装方法

**auto**がキーワードだと言いましたが、嘘でした。これはマクロです。 CrystalのマクロはASTノード、つまり構文を受け取ります。 **auto**はメソッド定義を受け取り、コンパイル時に処理して、目的の機能を実装するメソッド定義を生成します。

macro auto(method)
  ...
end

マクロは引数を検査できます。メソッドの名前、引数、またはメソッドが定義されている場所(上記の例ではString)を問い合わせることができます。より複雑なことを行う必要がある場合は、次のようにマクロで**run**を呼び出すことができます。

macro auto(method){{ run("auto/process", @type, method.name, *method.args) }}
end

これにより、auto/process.cr プログラムが呼び出され、型名、メソッド名、およびsplattedメソッド引数がプログラムに渡されます。次に、プログラムは通常のARGV 配列でこれらの引数を受け取り、処理し、元のプログラムに埋め込まれるメソッド定義を出力します。すごいですね。 ECR(ERBに似ています)にも同様の手法を使用しています。ECRテンプレートはコンパイル時に処理されます。

auto/process.cr プログラムはいくつかのことを行います。関連するメソッド定義とそのソースコード、そしておそらく関連するテスト/仕様をインターネットで検索します。現在、これはCrystalとの類似性のため、Rubyコードに対してのみ行われていますが、他の言語のサポートもまもなく提供されます。次に、コードを処理してCrystalコードを生成します。

さて、これはかなり遅くなる可能性があります。実際、数秒かかります(私たちのマシンの1つでは5秒)。幸いなことに、生成されたコードは通常の「.crystal」ディレクトリにキャッシュされるため、次回同じ型の同じメソッドに**auto**が使用されると、キャッシュされたバージョンが再利用されます。しかし、このペナルティがあっても、**auto**を使用することで節約できる時間を考えてみてください。メソッドを記述する必要がなく、既存の堅牢で十分にテストされたコードを再利用できます!

**auto**型

型に**auto**を使用することもできます。

auto class LinkedList(T)
end

list = LinkedList(Char).new
list.push 'a'
list.push 'b'
list.push 'c'
puts list.size #=> 3
puts list        #=> ['a', 'b', 'c']

そのため、**auto**マクロは、受信したASTノードがクラスかメソッドかを実際にチェックします。クラスの場合、auto/process.cr はインターネットでそのクラス名を検索し、そのクラスの定義と、それで見つけることができるすべてのメソッドを生成し、前のロジックを再利用します。

試してみる

GitHubリポジトリのautoブランチをチェックアウトすることで、これらすべてを試すことができますが、この機能のためにいくつかのマクロメソッドを追加したため、新しいコンパイラをコンパイルする必要があります。これはまだ非常に新しいものであるため、バグが見つかった場合は報告してください。