Crystalは、Rubyのシンタックスを持つ静的型付けのプログラミング言語で、以下のような特徴があります。
コンパイル型言語：Crystalはコンパイル型言語であり、コンパイルによって実行可能なバイナリが生成されます。これにより、実行速度が速く、静的型付けによって安全性が高まります。
オブジェクト指向：Crystalはオブジェクト指向のプログラミング言語であり、クラスや継承などのオブジェクト指向の機能をサポートしています。
Rubyとの互換性：CrystalはRubyのシンタックスに似ているため、Ruby開発者にとっては比較的簡単に学習することができます。また、CrystalのコードはRubyのコードに変換できるため、Rubyのライブラリを利用することができます。
静的型付け：Crystalは静的型付け言語であり、型エラーをコンパイル時に検出することができます。これにより、実行時にエラーが発生する可能性を減らし、コードの品質を向上させます。
並列処理：Crystalは、並列処理をサポートするために、Green Threadsと呼ばれる軽量スレッドを利用しています。これにより、CPUのコアを最大限に活用し、処理速度を高めることができます。
メタプログラミング：Crystalは、メタプログラミングをサポートしています。これにより、コード生成や動的な型変換など、より高度なプログラミング技術を利用することができます。
C言語とのインタフェース：Crystalは、C言語とのインタフェースをサポートしています。これにより、既存のC言語のライブラリを利用することができ、既存のシステムとの連携が容易になります。
お約束のHello_worldですが、ここではRubyとの比較も兼ねて、Ruby#Hello, World!をそのまま実行してみます。
''hello.cr''を用意しますCrystalのソースファイルの拡張子は''.cr'' です。
hello.cr:
puts 'Hello, World!'
コマンドラインでの操作:
% cat hello.cr 
puts 'Hello, World!'
% crystal hello.cr
In hello.cr:1:6
 1 | puts 'Hello, World!'
          ^
Error: unterminated char literal, use double quotes for strings
% sed -i -e "s@'@Q@g" -e 's@Q@"@g' hello.cr
% cat hello.cr 
puts "Hello, World!"
% crystal hello.cr
Hello, World!
Crystalでは、文字列の場合は二重引用符(")を使用するので、' を " に sed で置換えました。
修正後の hello.cr も問題なく ruby で実行できます。
コンパイル型言語 : １つまたは複数のソースコードをコンパイルして実行ファイルを生成し、生成した実行ファイルを実行します。
静的型付け : 値・変数・関数のパラメーター・関数の戻値などの型はコンパイル時に検証され、型安全性が担保されます。
例外 : begin / rescue / else / ensure / end 形の例外処理をサポートします。
演算子オーバーロード: サポートします。
メソッドオーバーロード : 同じ名前で引数の数が異なるメソッドを定義することが可能です。
型推論 : サポートします。
ガベージコレクション : サポートします。
クラス : クラスベースのオブジェクト指向言語です。
全てがオブジェクト : Javaで言うプリミティブもオブジェクトで、メソッドを持ちます。
コンストラクター : initalize() と名付けられたメソッドがインスタンス化に使われます。
デストラクター : ありません。
継承 : 単一継承をサポートします。
抽象クラス : Java や Go の interface や Swift の protocol はありませんが、abstract class があります。
Mix-in : include 文でサポートします。
名前空間 : モジュールが名前空間を持ちます。
defer : 提案されましたが採用されませんでした[https://github.com/crystal-lang/crystal/issues/2582 go like defer #2582]。例えば、File.open() で開いたファイルの始末は、File.open(){…}とブロックを渡すことで、File.open()の終了とともに close() される Ruby 流のモデルを使えるので、不要とのことです。また、deferを採用するとerrdferはいらないのかと連鎖的に問題が広がる懸念があります（従来のコードとの意味論の整合性も）。
制御構造は式 : if, while の制御構文は式です。
文末の;（セミコロン）: 必要ありません。
実際にCrystalで書かれたのコードを見てみましょう。
Crystalを使ってファイルを読み込むには、Fileクラスを使用します。
以下は、Crystalでファイルを読み込む例です。
file = File.open("filename.txt")
contents = file.gets_to_end
puts contents
file.close
File.open メソッドを使用して、ファイルを開きます。引数にファイルのパスを指定します。これにより、 file 変数にファイルのインスタンスが格納されます。
gets_to_end メソッドを使用して、ファイルの内容を読み込みます。これにより、 contents 変数にファイルの内容が格納されます。
puts メソッドを使用して、ファイルの内容を標準出力に出力します。
ファイルを開く際に File.open メソッドを使用した場合は、 File.close メソッドを使用してファイルを閉じる必要があります。
File.openにブロックを渡すと、ブロックを抜けると自動的にファイルが閉じられます。明示的なファイルのクローズが不要です。
File.open("filename.txt") { |file|
  contents = file.gets_to_end
  puts contents
}
File.open("filename.txt")は、指定されたファイルを開きます。このメソッドはブロックを取り、ブロック内でファイルを操作した後、自動的にファイルを閉じます。ブロックの終了時にファイルが閉じられるため、明示的なファイルのクローズが不要です。
|file|の部分は、ブロックパラメータで、この場合はfileという名前の変数がファイルオブジェクトを表します。
file.gets_to_endは、ファイルから現在の位置から末尾までの内容を読み込みます。ファイル全体を読み込むことができるメソッドです。
puts contentsは、読み込んだ内容を出力します。
このコードは、ファイルを開いて内容を読み込む際にブロックを使用し、ブロックを抜けるとファイルが自動的に閉じられるというCrystalの一般的なイディオムを示しています。
この方法は、ファイルの操作後にクローズ処理を忘れることを防ぎ、コードをより安全かつ簡潔にします。
エラトステネスの篩を、若干 Crystal らしく書いてみました。
エラトステネスの篩:
def eratosthenes(n)
  sieve = [] of Bool
  n.times do |i| sieve.push(i >= 2) end
  n.times do |i|
    next unless sieve[i]
 
    print("#{i} ")
    (2 * i).step(to: n, by: i, exclusive: true) do |j|
      sieve[j] = false
    end
  end
end
 
eratosthenes(1000)
実行結果:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997 
Crystalのプログラムを作り、コンパイル・実装するには、「オンライン実行環境を使う」・「エディト・コンパイル・実行環境を用意してそれを使う」の２通りの方法があります。
公式のオンライン実行環境、 https://play.crystal-lang.org/ があります。
まずは、これを使って本書に例示されているコードを実行してみることをお勧めします。
エディタについては本書では触れませんが、プログラミング時間の大半はエディタの操作に費やされるため、良いエディタを選択することが重要です。
Crystal の言語処理系は、 https://crystal-lang.org/install/ から入手します。
自分の、OSやGNU/Linuxであればディストリビューションに合わせてインストールしてください。
また、FreeBSDのように crystal と shards が別パッケージとなっていることもあるので、その場合は shards も追加インストールします。
多くの場合、インストールされた crystal はスタティック リンクされているので、ダイナミック リンク版の crystal を入手するには、ソースコードからビルドします。
また、interactive Crystalを有効にするためにも、ソースコードからのビルドが必要です。
crystal は、ソースコードが Github の https://github.com/crystal-lang/crystal.git で公開されているので、必要に応じてソースコードからビルドすることができます。
crystalは、セルフホスティング言語crystalコンパイラーを始めとする、crystalの言語処理系（標準ライブラリーを含む）とツールチェインやユーティリティーなどは、crystal自身で書かれています。なので、最初にバイナリーを入手してブートストラップするか、クロスビルドしたバイナリーを持込むか、パッケージシステムからインストールし、ターゲットでセルフコンパイル出来る状態を作る方法があります。
ビルドには、Chromebook(メモリー4GB, Celeron N4000, OS Version: reven-release/R104-14909.124.0, Chromebrew version: `1.25.3`, llvm-15.0.0)で約30分かかりました。
crystal コマンドは Crystal のコンパイラであると同時に、ビルドツールなどを含んだツールチェインです（プログラミング言語のCrystalは、先頭を大文字、コマンドのcrystalは、先頭を小文字にして区別します）。
[TODO: コマンドラインツール crystal の解説。 crystal ファイル名 は crystal run ファイル名 の短縮形で、インタープリタ的な実行…ではなく、内部ビルドツールでコンパイル・実行を行う]
Crystalは、Rubyに触発された構文を持つものの、Rubyとの互換性をゴールに定めてはいません。
このため、細部を見ると仕様に差異があり、Rubyのソースコードをcrystalに掛けても前節の 'Hello World' の様にコンパイルに失敗することがあります。
また、コンパイルできても実行結果に違いが出ることがあります。
ここでは、Ruby との違いについて実際のコードと双方の結果を比較することで、差異についての理解を深めていきます。
大きな整数:
p 2 ** 999
p (2 ** 999).class
rubyの実行結果:
5357543035931336604742125245300009052807024058527668037218751941851755255624680612465991894078479290637973364587765734125935726428461570217992288787349287401967283887412115492710537302531185570938977091076523237491790970633699383779582771973038531457285598238843271083830214915826312193418602834034688
Integer
crystalの実行結果:
Unhandled exception: Arithmetic overflow (OverflowError)
  from /usr/local/share/crystal/share/crystal/src/int.cr:295:9 in '**'
  from pow.cr:1:1 in '__crystal_main'
  from /usr/local/share/crystal/share/crystal/src/crystal/main.cr:115:5 in 'main_user_code'
  from /usr/local/share/crystal/share/crystal/src/crystal/main.cr:101:7 in 'main'
  from /usr/local/share/crystal/share/crystal/src/crystal/main.cr:127:3 in 'main'
  from /usr/local/lib64/libc.so.6 in '__libc_start_main'
  from /usr/local/.cache/crystal/crystal-run-pow.tmp in '_start'
  from ???
Ruby の整数は、桁あふれが起こると自動的に多倍長整数に型変換されるので、継ぎ目なしに大きな数を扱うアルゴルズムが使えます。
Crystal の整数は、固定長です（大きさについては後述）。なので大きな答えになる式を評価すると桁あふれが生じます。桁あふれが生じますが、C言語のように寡黙に処理を続けるのではなく、実行時に例外（OverflowError）が上がるので、例外を捕捉し然るべき処置を施すことが可能です。
big を require すると BigInt が使えるようになります。
BigInt:
require "big"
p BigInt.new(2) ** 999
p (BigInt.new(2) ** 999).class
実行結果:
5357543035931336604742125245300009052807024058527668037218751941851755255624680612465991894078479290637973364587765734125935726428461570217992288787349287401967283887412115492710537302531185570938977091076523237491790970633699383779582771973038531457285598238843271083830214915826312193418602834034688
BigInt
n : BigInt = 2
Error: type must be BigInt, not Int32
のように型アノテーションすることも出来ません。
様々なリテラルと型:
[nil, false, true, 42, 2.73, 'Q', "string", [1,2,3], {a:1, b:2}].each
Crystalのキーワード( ''keywords'' ) は、以下の通り。
abstract alias as asm begin break case class def do else elsif end ensure enum extend for fun if include instance_sizeof lib macro module next of out pointerof private protected rescue return require select sizeof struct super then type typeof uninitialized union unless until when while with yield
Crystalは、1つ、2つ、または3つのオペランドを持つ数多くの演算子をサポートしています[https://crystal-lang.org/reference/1.5/syntax_and_semantics/operators.html Operators]access-date:2022-07-22。
演算子式は、実際にはメソッド呼び出しとしてパースされます。例えば、a + b は a.+(b) と意味的に同じで、引数 b を持つ a のメソッド + を呼び出すことになります。
制御構造（せいぎょこうぞう、''control flow''）とは、「順次」「分岐」「反復」という基本的な処理のことを言います。
{{コラム|Crystalの真理値|2=
制御構造は「条件式」が真であるか偽であるかによって分岐や反復の振る舞いが変わります。
では「条件式」が真・偽はどの様に決まるのでしょう？
Crystalでは false あるいは nil であると偽、それ以外が真です。
なので 0 も []（空のArray） も {}（空のNamedTuple）も真です。
Crystalの条件分岐には、if, until と caseの3つの構文があります。
ifは条件式によって実行・否を切り替える構造構文で、評価した式の値を返すので条件演算子でもあります。
ifの例:
a = 0.0 / 0.0
if a < 0
    puts "minus"
elsif a > 0
    puts "plus"
elsif a == 0
    puts "zero"
else
    puts a
end
p! (
    if a < 0
        "minus"
    elsif a > 0
        "plus"
    elsif a == 0
        "zero"
    else
        a
    end
)
実行結果:
NaN 
(if a < 0 
 "minus" 
else 
 if a > 0 
   "plus" 
 else 
   if a == 0 
     "zero" 
   else 
     a 
   end 
 end 
end) # => NaN
elsif節:ifは、オプショナルな elsif 節を設け、条件式が偽であった時に別の条件に合致した処理を実行させることが出来ます。
else節:ifは、オプショナルな else 節を設け、条件式が偽であった時に処理を実行させることが出来ます。
ifは値を返すので、メソッドの実引数に使うことが出来ますし、代入演算の右辺にも使えます。
Crystalには、RubyやPerlのような後置のifがあります。
後置のifの例:
n = 0
puts "nは0" if n == 0
puts "nは1" if n == 1
実行結果:
nは0
unless（アンレス）は条件式によって実行・否を切り替える構造構文ですが、ifとは条件式に対する挙動が逆です。
unless文の例:
a = 0.0 / 0.0
unless a == 0
    puts "Non-zero"
else
    puts a
end
実行結果:
Non-zero
else節 : unless文は、オプショナルな else 節を設け、条件式が真であった時に処理を実行させることが出来ます。
また、unless文は elsif 節は持てません。
Crystalには、RubyやPerlのような後置のunlessがあります。
後置のunlessの例:
n = 0
puts "nは0" unless n == 0
puts "nは1" unless n == 1
実行結果:
nは1ではない
caseは、複数の条件式によって処理を降る分ける用途の為に用意されています。
caseの例:
n = 2
case n
when 1
    puts "one"
when 2
    puts "two"
when 3
    puts "three"
else
    puts "other"
end
p! (
case n
when 1
    "one"
when 2
    "two"
when 3
    "three"
else
    "other"
end
)
実行結果:
two 
(case n 
when 1 
 "one" 
when 2 
 "two" 
when 3 
 "three" 
else 
 "other" 
end) # => "two"
C言語系のswitch文に慣れた人はbreakがないことに気がつくと思います。Crystalのcaseはfall throughしませんし、fall throughさせる方法もありません。
ifを使ったコードをcaseに書き換えてみましょう。
case の式の省略:
a = 0.0 / 0.0
 
case
when a < 0
  puts "minus"
when a > 0
  puts "plus"
when a == 0
  puts "zero"
else
  puts a
end
 
p! (
case true
when a < 0
  "minus"
when a > 0
  "plus"
when a == 0
  "zero"
else
  a
end
)
実行結果:
NaN 
(case true 
when a < 0 
 "minus" 
when a > 0 
 "plus" 
when a == 0 
 "zero" 
else 
 a 
end) # => NaN
このコードは when 節の式の値とcaseの式を === で比較し、最初に一致した when に対応する式が実行される事を利用しています。
when 節が式ではなく型であった場合、caseの式を is_a? で評価し、最初に一致した when に対応する式が実行されます。
型による分岐:
p! 0.class,
	0.is_a?(Object),
	0.is_a?(Int32),
	0.is_a?(Number),
	0.is_a?(String)
 
case 0
when String
    puts "String"
when Number
    puts "Number"
when Int32
    puts "Int32"
when Object
    puts "Object"
else
    puts "Unknown"
end
実行結果:
0.class         # => Int32 
0.is_a?(Object) # => true 
0.is_a?(Int32)  # => true 
0.is_a?(Number) # => true 
0.is_a?(String) # => false 
Number
暗黙のオブジェクト構文を使うと
case 0
when .is_a?(String)
    puts "String"
when .is_a?(Number)
    puts "Number"
when .is_a(Int32)
    puts "Int32"
when .is_a(Object)
    puts "Object"
else
    puts "Unknown"
end
と書くことが出来ます。
メソッドは、.is_a? に限定しないので、 .odd? .even? .include? など Bool を返すメソッドなら何でも使えます。
when に対応する式は、1つのことが珍しくないので、その場合は省略可能な then を補うと、１行で書けます。
case 0
when String then puts "String"
when Number then puts "Number"
when Int32  then puts "Int32"
when Object then puts "Object"
else             puts "Unknown"
end
Crystalにおいて、タプルとは、複数の値をグループ化して1つのオブジェクトとして扱うための機能です。タプルは、角括弧 [ ] 内にコンマで区切られた値を指定して作成します。
例えば、次のように2つの値を持つタプルを作成できます。
 
tuple = [1, "hello"]
タプルの要素にアクセスするには、インデックスを指定して取得します。
 
tuple[0] #=> 1
tuple[1] #=> "hello"
また、Crystalにはダミー識別子 _ があります。この識別子は、未使用の引数や変数を表すために使用されます。例えば、次のように _ を使って、2番目の要素を無視して1番目の要素だけを取得することができます。
 
tuple = [1, "hello"]
a, _ = tuple
puts a #=> 1
ここでは、2番目の要素を _ で受け取っています。このように、ダミー識別子 _ を使うことで、ある変数や引数が使用されないことを明示することができます。
when の代わりに in を使用すると、exhaustive case 式が作成されます。exhaustive case では、必要な in 条件を省略するとコンパイル時にエラーとなります。exhaustive case 式では、when 節と else 節を含むことはできません。
Enumの網羅性チェック（網羅不完全）:
enum Colours
  Red
  Green
  Blue
end
colour : Colours = Colours::Red
q = case colour
    in Colours::Red then "赤"
    in .green?      then "緑"
    end
p q
コンパイルエラー:
Showing last frame. Use --error-trace for full trace.
In enumcase.cr:8:5
 8 | q = case colour
         ^
Error: case is not exhaustive for enum Colours.
Missing members:
 - Blue
case - in 式の in が列挙型の要素を網羅していないと、コンパイル時にこの様にエラーになります。
Colours::Red と .red? は同義です（enum では、要素名を小文字にし最後に ? が付いたメソッドが生えてきます）。
Enumの網羅性チェック（網羅完全）:
enum Colours
  Red
  Green
  Blue
end
colour : Colours = Colours::Red
q = case colour
    in Colours::Red then "赤"
    in .green?      then "緑"
    in .blue?       then "青"
    end
p q
実行結果:
"赤"
[TODO:短絡評価 && || ]
Crystalには、他のプログラミング言語のような繰返し構文と、イテレーターメソッドがあります。
Crystalの繰返し構文には、while と untilの2つがありますfor も do-while も loop もありません。。
while（ホワイル）は条件が真である間、式を実行しつづけます。
構文:
while 条件式
    式１
    式２
      ：
    式ｎ
end
Rubyと違い、条件式の後ろに do をつけることは出来ません。
while文のコード例:
i = 0
p! (
while i < 10
    p! i
    i += 1
    break i if i > 5
end
)
２行目の i < 5が真の間、次の２行を繰返します。
実行結果:
(while i < 10 
 p!(i) 
 i = i + 1 
 if i > 5 
   break i 
  end 
end)# => 
i # => 0 
i # => 1 
i # => 2 
i # => 3 
i # => 4 
i # => 5 
6
until（アンティル）は条件が偽である間、式を実行しつづけます。whileとは条件に対する挙動が逆です。
構文:
until 条件式 [ do ]
    文１
    文２
      ：
    文ｎ
end
do は省略できます。
untilのコード例:
i = 0
until i == 3
    puts i
    i += 1
end
２行目の i == 3が偽の間、次の２行を繰返します。
実行結果:
0
1
2
Crystalにはforがありませんが、コレクションのイテレーションメソッドを使うことで繰返しを簡素に実現出来ます。
Rangeオブジェクトは、整数の区間を表し範囲演算子 開始 .. 終了 や 開始 ... 終了 で生成します。
範囲演算子の終了は省略でき、その場合は数学の半開区間（半閉区間）となり、例えば、1 ..は自然数となります（ただし、日本的な０を自然数に含まない場合）。 
コード:
rng = 1..3
puts rng.class
rng.each do | n |
  puts "#{n}番";
end
実行結果:
Range(Int32, Int32) 
1番 
2番 
3番
Arrayオブジェクトは、任意の Crystal オブジェクトを要素として持つことができます。
配列式[ 要素1, 要素2, … 要素n ] で生成します。
コード:
animals = [ "ネコ", "金魚", "ハムスター" ]
puts animals.class
animals.each do | animal |
  puts "動物 #{animal}"
end
p! ([ "イヌ", *animals , "イグアナ" ])
実行結果:
Array(String)
動物 ネコ
動物 金魚
動物 ハムスター
(["イヌ", *animals, "イグアナ"]) # => ["イヌ", "ネコ", "金魚", "ハムスター", "イグアナ"]
Tupleオブジェクトは、任意の Crystal オブジェクトを要素として持つことができます。
配列式{ 要素1, 要素2, … 要素n } で生成します。
コード:
animals = { "ネコ", "金魚", "ハムスター" }
puts animals.class
animals.each do | animal |
  puts "動物 #{animal}"
end
p! ({ "イヌ", *animals , "イグアナ" })
実行結果:
Tuple(String, String, String)
動物 ネコ
動物 金魚
動物 ハムスター
({"イヌ", *animals, "イグアナ"}) # => {"イヌ", "ネコ", "金魚", "ハムスター", "イグアナ"}
Setオブジェクトは集合です。任意の Crystal オブジェクトを要素として持つことができますが、1つの値は重複して持てません。
Set.newに配列式{ 要素1, 要素2, … 要素n } などを渡し初期化します。
コード:
animals = Set.new({ "ネコ", "金魚", "ハムスター" })
 
puts animals.class
animals.each do | animal |
  puts "動物 #{animal}"
end
 
p! animals,
  animals.includes?("ネコ"),
  animals.includes?("イヌ")
 
animals.delete "ネコ"
animals.add    "イヌ"
 
p! animals,
  animals.includes?("ネコ"),
  animals.includes?("イヌ")
 
animals = Set.new({ "ネコ", "イヌ", "金魚", "ハムスター", "カナリヤ", "クサガメ" })
mammals = Set.new({ "ネコ", "イヌ", "ハムスター" })
p!  animals , mammals,
   animals & mammals,
   animals | mammals,
   animals + mammals,
   animals ^ mammals,
   animals - mammals,
   mammals - animals
実行結果:
Set(String)
動物 ネコ
動物 金魚
動物 ハムスター
animals                 # => Set{"ネコ", "金魚", "ハムスター"}
animals.includes?("ネコ") # => true
animals.includes?("イヌ") # => false
animals                 # => Set{"金魚", "ハムスター", "イヌ"}
animals.includes?("ネコ") # => false
animals.includes?("イヌ") # => true
animals           # => Set{"ネコ", "イヌ", "金魚", "ハムスター", "カナリヤ", "クサガメ"}
mammals           # => Set{"ネコ", "イヌ", "ハムスター"}
animals & mammals # => Set{"ネコ", "イヌ", "ハムスター"}
animals | mammals # => Set{"ネコ", "イヌ", "金魚", "ハムスター", "カナリヤ", "クサガメ"}
animals + mammals # => Set{"ネコ", "イヌ", "金魚", "ハムスター", "カナリヤ", "クサガメ"}
animals ^ mammals # => Set{"金魚", "カナリヤ", "クサガメ"}
animals - mammals # => Set{"金魚", "カナリヤ", "クサガメ"}
mammals - animals # => Set{}
NamedTupleオブジェクトは、任意の Crystal オブジェクトをキーに、任意の Crystal オブジェクトを値に持つことができる連想配列です。
NamedTuple式{キー1 => 値1, キー2 => 値2, キーn => 値n} で生成します。
また、キーが Symbol の場合
NamedTuple式{キー1: 値1, キー2: 値2, キーn: 値n} で生成することが出来ます。
コード:
animals = {cat: "ネコ", gold_fish: "金魚", hamster: "ハムスター"}
 
puts animals.class
 
animals.each do | en, animal |
  puts "動物 #{en}: #{animal}"
end
実行結果:
NamedTuple(cat: String, gold_fish: String, hamster: String)
動物 cat: ネコ
動物 gold_fish: 金魚
動物 hamster: ハムスター
このように、Crystalではforがなくてもコレクションのメソッドで同様の処理を実現できます。
loop ありません。
while true で代用します。
loopの代用コード例:
i = 1
while true
    puts "0b%b" % i
    i <<= 1
    break if i > 2**8
end
実行結果:
0b1
0b10
0b100
0b1000
0b10000
0b100000
0b1000000
0b10000000 
0b100000000
５行目の、break if i > 2**8でループを脱出するようにしています。この様に break や return あるいは例外が上がらないとループは永久に終わりません。
このコードは、Crystalにはない do-while文を模倣する例にもなっています。
Integer#timesは与えられたブロックをオブジェクトの示す整数値回くりかえします。
3.times{ puts "Hello, world!" }
実行結果:
Hello, world!
Hello, world!
Hello, world!
ループ変数を使た例:
3.times do |i|
  puts "#{i}の倍は#{2 * i}"
end
実行結果:
0の倍は0
1の倍は2
2の倍は4
ブロックを伴わないtimesメソッド:
iter = 3.times
puts iter.class
p! iter.next
p! iter.next
p! iter.next
p! iter.next
p! iter.next
p! iter.next
実行結果:
Int::TimesIterator(Int32) 
iter.next # => 0 
iter.next # => 1 
iter.next # => 2 
iter.next # => # 
iter.next # => # 
iter.next # => #
Integer#times にブロックを渡さないと、Int::TimesIterator([T])オブジェクトが返ります。
Int::TimesIterator([T])オブジェクトは外部イテレーターと呼ばれnextメソッドで反復を行えます。
Crystal では、全てがオブジェクトです。
オブジェクトのリテラルとクラス:
[nil, false, true, 1, 3.14, "abc", :abc, 1..10, 1...10, 1..,
 [1, 2_u8, 3_i128],
 [1, 2, 3], [1, "abc"],
 {1, 2, 3}, {1, "abc"},
 {"a" => 1, "b" => 2},
 {a: 1, b: 2},
 Set.new([:a, :bc, :def]),
 ->(x : Int32) { 2 * x },
 100.times,
 (1..).each,
 [1, 2, 3].each,
 {1, 2, 3}.each,
 {"a" => 1, "b" => 2}.each,
].each do |obj|
  p [obj, obj.class]
end
実行結果:
[nil, Nil]
[false, Bool]
[true, Bool]
[1, Int32]
[3.14, Float64]
["abc", String]
[:abc, Symbol]
[1..10, Range(Int32, Int32)]
[1...10, Range(Int32, Int32)]
[1.., Range(Int32, Nil)]
[[1, 2, 3], Array(Int128 | Int32 | UInt8)]
[[1, 2, 3], Array(Int32)]
[[1, "abc"], Array(Int32 | String)]
[{1, 2, 3}, Tuple(Int32, Int32, Int32)]
[{1, "abc"}, Tuple(Int32, String)]
[{"a" => 1, "b" => 2}, Hash(String, Int32)]
[{a: 1, b: 2}, NamedTuple(a: Int32, b: Int32)]
[Set{:a, :bc, :def}, Set(Symbol)]
[#, Proc(Int32, Int32)]
[#, Int::TimesIterator(Int32)]
[#, Range::ItemIterator(Int32, Nil)]
[#, Indexable::ItemIterator(Array(Int32), Int32)]
[#, Indexable::ItemIterator(Tuple(Int32, Int32, Int32), Int32)]
[# 1, "b" => 2}, @index=0>, Hash::EntryIterator(String, Int32)]
Rubyのオブジェクトのリテラルとクラス:
require 'set'
[nil, false, true, 1, 3.14, "abc", :abc, 1..10, 1...10, 1..,
 [1, 2, 3], [1, "abc"],
 {"a" => 1, "b" => 2},
 {a: 1, b: 2},
 Set.new([:a, :bc, :def]),
 ->(x) { 2 * x },
 100.times,
 (1..).each,
 [1, 2, 3].each,
 {"a" => 1, "b" => 2}.each,
].each do |obj|
  p [obj, obj.class]
end
実行結果:
[nil, NilClass]
[false, FalseClass]
[true, TrueClass]
[1, Integer]
[3.14, Float]
["abc", String]
[:abc, Symbol]
[1..10, Range]
[1...10, Range]
[1.., Range]
[[1, 2, 3], Array]
[[1, "abc"], Array]
[{"a"=>1, "b"=>2}, Hash]
[{:a=>1, :b=>2}, Hash]
[#, Set]
[#, Proc]
[#, Enumerator]
[#, Enumerator]
[#, Enumerator]
[#1, "b"=>2}:each>, Enumerator]
シンプルなクラス:
class Hello
  def initialize(@name : String = "World")
  end
  def greeting
    puts "Hello #{@name}!"
  end
end
hello = Hello.new()
hello.greeting
universe = Hello.new("Universe")
universe.greeting
実行結果:
Hello World!
Hello Universe!
  def initialize(@name : String = "World")
  end
  def initialize(name = "World")
    @name = name
  end
とするところですが、Crystalでは、型アノテーション  : String を使い、引数の型を限定しました。
また、（@ 付きの）アトリビュート名を仮引数にすると、そのままアトリビュート（a.k.a. インスタンス変数）に仮引数が代入されます。
これは、C++のコンストラクターのメンバー初期化リストと同じアイディアですが、Crystalではインスタンス変数に @ が前置されるので、仮引数に @ が出現すればインスタンス変数の初期値だと自明で、聡明な選択です。
Ruby#ユーザー定義クラスの都市間の大圏距離を求めるメソッドを追加した例を、Crystalに移植しました。
都市間の大圏距離:
class GeoCoord
  getter :longitude, :latitude
  def initialize(@longitude : Float64, @latitude : Float64)
  end
  def to_s(io)
    ew, ns = "東経", "北緯"
    long, lat = @longitude, @latitude
    ew, long = "西経", -long if long < 0.0
    ns, lat = "南緯", -lat if lat < 0.0
    io << "(#{ew}: #{long}, #{ns}: #{lat})"
  end # https://github.com/crystal-lang/crystal/issues/259
  def distance(other)
    i, r = Math::PI / 180, 6371.008
    Math.acos(Math.sin(@latitude*i) * Math.sin(other.latitude * i) +
              Math.cos(@latitude*i) * Math.cos(other.latitude * i) * Math.cos(@longitude * i - other.longitude * i)) * r
  end
end
Sites = {
  "東京駅":         GeoCoord.new(139.7673068, 35.6809591),
  "シドニー・オペラハウス": GeoCoord.new(151.215278, -33.856778),
  "グリニッジ天文台":    GeoCoord.new(-0.0014, 51.4778),
}
Sites.each { |name, gc|
  puts "#{name}: #{gc}"
}
puts ""
keys, len = Sites.keys, Sites.size
keys.each_with_index { |x, i|
  y = keys[(i + 1) % len]
  puts "#{x} ⇔ #{y}: #{Sites[x].distance(Sites[y])} [km]"
}
実行結果:
東京駅: (東経: 139.7673068, 北緯: 35.6809591)
シドニー・オペラハウス: (東経: 151.215278, 南緯: 33.856778)
グリニッジ天文台: (西経: 0.0014, 北緯: 51.4778)
東京駅 ⇔ シドニー・オペラハウス: 7823.269299386704 [km]
シドニー・オペラハウス ⇔ グリニッジ天文台: 16987.2708377249 [km]
グリニッジ天文台 ⇔ 東京駅: 9560.546566490015 [km]
  getter :longitude, :latitude
としました。
将来、  attr_accessor  が実装される可能性はありますが、姉妹品の  setter との併用が下位互換性を考えると確実です。
  def to_s()
    "(#{ew}: #{long}, #{ns}: #{lat})"
  def to_s(io)
    io << "(#{ew}: #{long}, #{ns}: #{lat})"
Ruby にはクラス名と同じ名前のメソッドで .new を呼出す文化があるのですが、Crystalはメソッドの先頭を大文字に出来ないので、これは見送りました。
JavaScript/クラス#包含と継承を、Rubyに移植したRuby#包含と継承を、Crystalに移植しました。
包含と継承の例:
class Point
  def initialize(@x = 0, @y = 0)
  end
  def inspect(io)
    io << "x:#{@x}, y:#{@y}"
  end
  def move(dx = 0, dy = 0)
    @x, @y = @x + dx, @y + dy
    self
  end
end
class Shape
  def initialize(x = 0, y = 0)
    @location = Point.new(x, y)
  end
  def inspect(io)
    @location.inspect(io)
  end
  def move(x, y)
    @location.move(x, y)
    self
  end
end
class Rectangle < Shape
  def initialize(x = 0, y = 0, @width = 0, @height = 0)
    super(x, y)
  end
  def inspect(io)
    super(io)
    io << ", width:#{@width}, height:#{@height}"
  end
end
rct = Rectangle.new(12, 32, 100, 50)
p! rct,
  rct.is_a?(Rectangle),
  rct.is_a?(Shape),
  rct.is_a?(Point),
  rct.move(11, 21)
(END)
実行結果:
rct                  # => x:12, y:32, width:100, height:50 
rct.is_a?(Rectangle) # => true 
rct.is_a?(Shape)     # => true 
rct.is_a?(Point)     # => false 
rct.move(11, 21)     # => x:23, y:53, width:100, height:50 
crystal tool hierarchy:
% crystal tool hierarchy inclusion-and-inheritance.cr -e Shape
- class Object (4 bytes)
  |
  +- class Reference (4 bytes)
     |
     +- class Shape (16 bytes)
        .   @location : Point (8 bytes)
        |
        +- class Rectangle (24 bytes)
               @width  : Int32 (4 bytes)
               @height : Int32 (4 bytes)
crystal の tool hierarchy サブコマンドで、クラスの継承関係がわかります。
Crystal には、RubyのClassにあるメソッド superclass と subclasses がないので、マクロで実装しました。
superclass と subclasses:
class Class
  def self.superclass
  end
  def self.subclasses : Array(self.class)
.map(&.as(self.class))
  end
  def self.all_subclasses : Array(self.class)
    {% begin %}
] of self.class
    {% end %}
  end
end
 
class A end
class AA < A end
class AAA < AA end
class AAB < AA end
class AB < A end
p! A,
  A.subclasses,
  A.all_subclasses,
  AAA.superclass,
  A.superclass
 
c = AAA
while !c.is_a? Nil
  p! c.superclass
  c = c.superclass
end
実行結果:
A                # => A 
A.subclasses     # => [AA, AB] 
A.all_subclasses # => [AA, AAA, AAB, AB] 
AAA.superclass   # => AA 
A.superclass     # => Reference 
c.superclass # => AA 
c.superclass # => A 
c.superclass # => Reference 
c.superclass # => Object 
c.superclass # => nil
Java/抽象クラスを、Crystalに移植しました。
抽象クラスの宣言:
abstract class クラス名
  #
end
このクラス名は、 .new でインスタンス化出来ません。
となります。
インスタンス化することは出来ませんが、抽象クラスを別のクラスが継承する事は出来ます。
また、抽象クラスを super() を使うことでメソッドを呼び出せるので、抽象メソッドではないメソッド（具象メソッド）を持つことも、インスタンス変数も持つことも出来ます。
抽象クラスの例では、Shapeのinitializeメソッドが抽象クラスの具象メソッドとなっています。
抽象メソッドの宣言:
  abstract def メソッド名
抽象クラスの例:
abstract class Shape
  def initialize(@x = 0.0, @y = 0.0)
  end
  abstract def to_s(io)
  abstract def area
end
class Square < Shape
  def initialize(x, y, @wh = 0.0)
    super(x, y)
  end
  def to_s(io)
    io << "Square(#{@x}, #{@y}, #{@wh})"
  end
  def area
    @wh * @wh
  end
end
abstract class Shape
  def initialize(@x = 0.0, @y = 0.0)
  end
  abstract def to_s(io)
  abstract def area
end
class Square < Shape
  def initialize(x, y, @wh = 0.0)
    super(x, y)
  end
  def to_s(io)
    io << "Square(#{@x}, #{@y}, #{@wh})"
  end
  def area
    @wh * @wh
  end
end
class Recrangle < Shape
  def initialize(x, y, @w = 0.0, @h = 0.0)
    super(x, y)
  end
  def to_s(io)
    io << "Rectanle(#{@x}, #{@y}, #{@w}, #{@h})"
  end
  def area
    @w * @h
  end
end
class Circle < Shape
  def initialize(x, y, @r = 0.0)
    super(x, y)
  end
  def to_s(io)
    io << "Circle(#{@x}, #{@y}, #{@r})"
  end
  def area
    3.1425926536 * @r * @r
  end
end
shapes = [
  Square.new(5.0, 10.0, 15.0),
  Recrangle.new(13.0, 23.0, 20.0, 10.0),
  Circle.new(3.0, 2.0, 20.0),
] of Shape
shapes.each do |shape|
  puts("#{shape}: #{shape.area}")
end
実行結果:
Square(5.0, 10.0, 15.0): 225.0
Rectanle(13.0, 23.0, 20.0, 10.0): 200.0
Circle(3.0, 2.0, 20.0): 1257.03706144
crystal tool hierarchy:
% crystal tool hierarchy abstract.cr -e Shape
- class Object (4 bytes)
  |
  +- class Reference (4 bytes)
     |
     +- class Shape (24 bytes)
        .   @x : Float64 (8 bytes)
        .   @y : Float64 (8 bytes)
        |
        +- class Circle (32 bytes)
        |      @r : Float64 (8 bytes)
        |
        +- class Recrangle (40 bytes)
        |      @w : Float64 (8 bytes)
        |      @h : Float64 (8 bytes)
        |
        +- class Square (32 bytes)
               @wh : Float64 (8 bytes)
crystal の tool hierarchy サブコマンドで、クラスの継承関係がわかります。
「包含と継承の例」と比べると、ShapeとRectangleが同じ階層にあることがわかると思います。
オブジェクトの値や機能を呼び出すためには、メソッドを使います（多くの演算子もメソッドです）。
Crystal には、Objectクラスにmethodsメソッドがないので、マクロで実装しました。
RubyのObject#methods:
p Object.methods.sort,
  Integer.methods.sort,
  Float.methods.sort,
  Array.methods.sort,
  Range.methods.sort
実行結果:
[:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :__id__, :__send__, :alias_method, :allocate, :ancestors, :attr, :attr_accessor, :attr_reader, :attr_writer, :autoload, :autoload?, :class, :class_eval, :class_exec, :class_variable_defined?, :class_variable_get, :class_variable_set, :class_variables, :clone, :const_defined?, :const_get, :const_missing, :const_set, :const_source_location, :constants, :define_method, :define_singleton_method, :deprecate_constant, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :include, :include?, :included_modules, :inspect, :instance_eval, :instance_exec, :instance_method, :instance_methods, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :method_defined?, :methods, :module_eval, :module_exec, :name, :new, :nil?, :object_id, :prepend, :private_class_method, :private_constant, :private_instance_methods, :private_method_defined?, :private_methods, :protected_instance_methods, :protected_method_defined?, :protected_methods, :public_class_method, :public_constant, :public_instance_method, :public_instance_methods, :public_method, :public_method_defined?, :public_methods, :public_send, :remove_class_variable, :remove_instance_variable, :remove_method, :respond_to?, :send, :singleton_class, :singleton_class?, :singleton_method, :singleton_methods, :subclasses, :superclass, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :undef_method, :untaint, :untrust, :untrusted?, :yield_self]
[:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :__id__, :__send__, :alias_method, :allocate, :ancestors, :attr, :attr_accessor, :attr_reader, :attr_writer, :autoload, :autoload?, :class, :class_eval, :class_exec, :class_variable_defined?, :class_variable_get, :class_variable_set, :class_variables, :clone, :const_defined?, :const_get, :const_missing, :const_set, :const_source_location, :constants, :define_method, :define_singleton_method, :deprecate_constant, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :include, :include?, :included_modules, :inspect, :instance_eval, :instance_exec, :instance_method, :instance_methods, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :method_defined?, :methods, :module_eval, :module_exec, :name, :nil?, :object_id, :prepend, :private_class_method, :private_constant, :private_instance_methods, :private_method_defined?, :private_methods, :protected_instance_methods, :protected_method_defined?, :protected_methods, :public_class_method, :public_constant, :public_instance_method, :public_instance_methods, :public_method, :public_method_defined?, :public_methods, :public_send, :remove_class_variable, :remove_instance_variable, :remove_method, :respond_to?, :send, :singleton_class, :singleton_class?, :singleton_method, :singleton_methods, :sqrt, :subclasses, :superclass, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :try_convert, :undef_method, :untaint, :untrust, :untrusted?, :yield_self]
[:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :__id__, :__send__, :alias_method, :allocate, :ancestors, :attr, :attr_accessor, :attr_reader, :attr_writer, :autoload, :autoload?, :class, :class_eval, :class_exec, :class_variable_defined?, :class_variable_get, :class_variable_set, :class_variables, :clone, :const_defined?, :const_get, :const_missing, :const_set, :const_source_location, :constants, :define_method, :define_singleton_method, :deprecate_constant, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :include, :include?, :included_modules, :inspect, :instance_eval, :instance_exec, :instance_method, :instance_methods, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :method_defined?, :methods, :module_eval, :module_exec, :name, :nil?, :object_id, :prepend, :private_class_method, :private_constant, :private_instance_methods, :private_method_defined?, :private_methods, :protected_instance_methods, :protected_method_defined?, :protected_methods, :public_class_method, :public_constant, :public_instance_method, :public_instance_methods, :public_method, :public_method_defined?, :public_methods, :public_send, :remove_class_variable, :remove_instance_variable, :remove_method, :respond_to?, :send, :singleton_class, :singleton_class?, :singleton_method, :singleton_methods, :subclasses, :superclass, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :undef_method, :untaint, :untrust, :untrusted?, :yield_self]
[:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :[], :__id__, :__send__, :alias_method, :allocate, :ancestors, :attr, :attr_accessor, :attr_reader, :attr_writer, :autoload, :autoload?, :class, :class_eval, :class_exec, :class_variable_defined?, :class_variable_get, :class_variable_set, :class_variables, :clone, :const_defined?, :const_get, :const_missing, :const_set, :const_source_location, :constants, :define_method, :define_singleton_method, :deprecate_constant, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :include, :include?, :included_modules, :inspect, :instance_eval, :instance_exec, :instance_method, :instance_methods, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :method_defined?, :methods, :module_eval, :module_exec, :name, :new, :nil?, :object_id, :prepend, :private_class_method, :private_constant, :private_instance_methods, :private_method_defined?, :private_methods, :protected_instance_methods, :protected_method_defined?, :protected_methods, :public_class_method, :public_constant, :public_instance_method, :public_instance_methods, :public_method, :public_method_defined?, :public_methods, :public_send, :remove_class_variable, :remove_instance_variable, :remove_method, :respond_to?, :send, :singleton_class, :singleton_class?, :singleton_method, :singleton_methods, :subclasses, :superclass, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :try_convert, :undef_method, :untaint, :untrust, :untrusted?, :yield_self] 
[:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :__id__, :__send__, :alias_method, :allocate, :ancestors, :attr, :attr_accessor, :attr_reader, :attr_writer, :autoload, :autoload?, :class, :class_eval, :class_exec, :class_variable_defined?, :class_variable_get, :class_variable_set, :class_variables, :clone, :const_defined?, :const_get, :const_missing, :const_set, :const_source_location, :constants, :define_method, :define_singleton_method, :deprecate_constant, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :include, :include?, :included_modules, :inspect, :instance_eval, :instance_exec, :instance_method, :instance_methods, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :method_defined?, :methods, :module_eval, :module_exec, :name, :new, :nil?, :object_id, :prepend, :private_class_method, :private_constant, :private_instance_methods, :private_method_defined?, :private_methods, :protected_instance_methods, :protected_method_defined?, :protected_methods, :public_class_method, :public_constant, :public_instance_method, :public_instance_methods, :public_method, :public_method_defined?, :public_methods, :public_send, :remove_class_variable, :remove_instance_variable, :remove_method, :respond_to?, :send, :singleton_class, :singleton_class?, :singleton_method, :singleton_methods, :subclasses, :superclass, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :undef_method, :untaint, :untrust, :untrusted?, :yield_self]
Crystalに実装したmethodsマクロ:
class Object
  macro methods
  end
end
p! Object.methods,
  Reference.methods,
  Array.methods,
  Box.methods,
  Channel.methods,
  Deque.methods,
  Dir.methods,
  Exception.methods,
  ArgumentError.methods,
  DivisionByZeroError.methods,
  IndexError.methods,
  InvalidByteSequenceError.methods,
  Fiber.methods,
  Hash.methods,
  IO.methods,
  File.methods,
  Mutex.methods,
  PrettyPrint.methods,
  Process.methods,
  Regex.methods,
  String.methods,
  Thread.methods,
  Bool.methods,
  Int32.methods,
  Float64.methods,
  Proc.methods
実行結果:
Object.methods                   # => ["!=", "!~", "==", "===", "=~", "class", "crystal_type_id", "dup", "hash", "in?", "inspect", "itself", "not_nil!", "pretty_inspect", "pretty_print", "tap", "to_s", "try", "unsafe_as"]
Reference.methods                # => ["==", "dup", "exec_recursive", "exec_recursive_clone", "hash", "inspect", "object_id", "pretty_print", "same?", "to_s"]
Array.methods                    # => ["&", "*", "+", "-", "<<", "<=>", "==", "[]", "[]=", "[]?", "calculate_new_capacity", "check_needs_resize", "check_needs_resize_for_unshift", "clear", "clone", "compact", "compact!", "concat", "delete", "delete_at", "dup", "each_repeated_permutation", "fill", "first", "flatten", "increase_capacity", "increase_capacity_for_unshift", "index", "initialize", "insert", "inspect", "internal_delete", "last", "map", "map_with_index", "needs_resize?", "pop", "pop?", "pretty_print", "product", "push", "reject!", "remaining_capacity", "repeated_permutations", "replace", "reset_buffer_to_root_buffer", "resize_if_cant_insert", "resize_to_capacity", "resize_to_capacity_for_unshift", "reverse", "root_buffer", "rotate", "rotate!", "select!", "shift", "shift?", "shift_buffer_by", "shift_when_not_empty", "shuffle", "size", "size=", "skip", "sort", "sort!", "sort_by", "sort_by!", "to_a", "to_lookup_hash", "to_s", "to_unsafe", "to_unsafe_slice", "transpose", "truncate", "uniq", "uniq!", "unsafe_fetch", "unsafe_put", "unshift", "unstable_sort", "unstable_sort!", "unstable_sort_by", "unstable_sort_by!", "|"]
Box.methods                      # => ["initialize", "object"]
Channel.methods                  # => ["close", "closed?", "dequeue_receiver", "dequeue_sender", "initialize", "inspect", "pretty_print", "receive", "receive?", "receive_impl", "receive_internal", "receive_select_action", "receive_select_action?", "send", "send_internal", "send_select_action"]
Deque.methods                    # => ["+", "<<", "==", "buffer", "clear", "clone", "concat", "delete", "delete_at", "dup", "each", "halfs", "increase_capacity", "initialize", "insert", "inspect", "internal_delete", "pop", "pop?", "pretty_print", "push", "reject!", "rotate!", "select!", "shift", "shift?", "size", "size=", "to_s", "unsafe_fetch", "unsafe_put", "unshift"]
Dir.methods                      # => ["children", "close", "each", "each_child", "entries", "initialize", "inspect", "path", "pretty_print", "read", "rewind", "to_s"]
Exception.methods                # => ["backtrace", "backtrace?", "callstack", "callstack=", "cause", "initialize", "inspect", "inspect_with_backtrace", "message", "to_s"]
ArgumentError.methods            # => ["initialize"]
DivisionByZeroError.methods      # => ["initialize"]
IndexError.methods               # => ["initialize"]
InvalidByteSequenceError.methods # => ["initialize"]
Fiber.methods                    # => ["cancel_timeout", "dead?", "enqueue", "initialize", "inspect", "makecontext", "name", "name=", "next", "next=", "previous", "previous=", "push_gc_roots", "resumable?", "resume", "resume_event", "run", "running?", "stack_bottom", "stack_bottom=", "timeout", "timeout_event", "timeout_select_action", "timeout_select_action=", "to_s"]
Hash.methods                     # => ["==", "[]", "[]=", "[]?", "add_entry_and_increment_size", "clear", "clear_entries", "clear_impl", "clear_indices", "clone", "compact", "compact!", "compare_by_identity", "compare_by_identity?", "compute_indices_bytesize", "delete", "delete_entry", "delete_entry_and_update_counts", "delete_impl", "delete_linear_scan", "dig", "dig?", "do_compaction", "double_indices_size", "dup", "each", "each_entry_with_index", "each_key", "each_value", "empty?", "entries", "entries_capacity", "entries_full?", "entries_size", "entry_matches?", "fetch", "find_entry", "find_entry_with_index", "find_entry_with_index_linear_scan", "first_entry?", "first_key", "first_key?", "first_value", "first_value?", "fit_in_indices", "get_entry", "get_index", "has_key?", "has_value?", "hash", "indices_malloc_size", "indices_size", "initialize", "initialize_clone", "initialize_clone_entries", "initialize_compare_by_identity", "initialize_copy_non_entries_vars", "initialize_default_block", "initialize_dup", "initialize_dup_entries", "inspect", "invert", "key_for", "key_for?", "key_hash", "keys", "last_entry?", "last_key", "last_key?", "last_value", "last_value?", "malloc_entries", "malloc_indices", "merge", "merge!", "merge_into!", "next_index", "pretty_print", "proper_subset_of?", "proper_superset_of?", "put", "realloc_entries", "realloc_indices", "rehash", "reject", "reject!", "resize", "select", "select!", "set_entry", "set_index", "shift", "shift?", "size", "subset_of?", "superset_of?", "to_a", "to_a_impl", "to_h", "to_s", "transform_keys", "transform_values", "transform_values!", "update", "update_linear_scan", "upsert", "values", "values_at"]
IO.methods                       # => ["<<", "check_open", "close", "closed?", "decoder", "each_byte", "each_char", "each_line", "encoder", "encoding", "flush", "getb_to_end", "gets", "gets_peek", "gets_slow", "gets_to_end", "has_non_utf8_encoding?", "peek", "peek_or_read_utf8", "peek_or_read_utf8_masked", "pos", "pos=", "print", "printf", "puts", "read", "read_at", "read_byte", "read_bytes", "read_char", "read_char_with_bytesize", "read_fully", "read_fully?", "read_line", "read_string", "read_utf8", "read_utf8_byte", "rewind", "seek", "set_encoding", "skip", "skip_to_end", "tell", "tty?", "utf8_encoding?", "write", "write_byte", "write_bytes", "write_string", "write_utf8"]
File.methods                     # => ["delete", "initialize", "inspect", "path", "read_at", "size", "truncate"]
Mutex.methods                    # => ["initialize", "lock", "lock_slow", "synchronize", "try_lock", "unlock"]
PrettyPrint.methods              # => ["break_outmost_groups", "breakable", "comma", "current_group", "fill_breakable", "flush", "group", "group_queue", "group_sub", "indent", "initialize", "list", "nest", "newline", "surround", "text"]
Process.methods                  # => ["channel", "close", "close_io", "copy_io", "ensure_channel", "error", "error?", "exists?", "finalize", "initialize", "input", "input?", "output", "output?", "pid", "signal", "stdio_to_fd", "terminate", "terminated?", "wait"]
Regex.methods                    # => ["+", "==", "===", "=~", "capture_count", "clone", "dup", "finalize", "hash", "initialize", "inspect", "internal_matches?", "match", "match_at_byte_index", "matches?", "matches_at_byte_index?", "name_table", "options", "source", "to_s"]
String.methods                   # => ["%", "*", "+", "<=>", "==", "=~", "[]", "[]?", "ascii_only?", "blank?", "byte_at", "byte_at?", "byte_delete_at", "byte_index", "byte_index_to_char_index", "byte_slice", "byte_slice?", "bytes", "bytesize", "calc_excess_left", "calc_excess_right", "camelcase", "capitalize", "center", "char_at", "char_bytesize_at", "char_index_to_byte_index", "chars", "check_no_null_byte", "chomp", "clone", "codepoint_at", "codepoints", "compare", "count", "delete", "delete_at", "downcase", "dump", "dump_char", "dump_hex", "dump_or_inspect", "dump_or_inspect_char", "dump_or_inspect_unquoted", "dump_unquoted", "dup", "each_byte", "each_byte_index_and_char_index", "each_char", "each_char_with_index", "each_codepoint", "each_grapheme", "each_grapheme_boundary", "each_line", "empty?", "encode", "ends_with?", "find_start_and_end", "grapheme_size", "graphemes", "gsub", "gsub_append", "gsub_ascii_char", "has_back_references?", "hash", "hexbytes", "hexbytes?", "includes?", "index", "insert", "insert_impl", "inspect", "inspect_char", "inspect_unquoted", "just", "lchop", "lchop?", "lines", "ljust", "lstrip", "match", "matches?", "partition", "presence", "pretty_print", "rchop", "rchop?", "remove_excess", "remove_excess_left", "remove_excess_right", "reverse", "rindex", "rjust", "rpartition", "rstrip", "scan", "scan_backreferences", "scrub", "single_byte_optimizable?", "size", "size_known?", "split", "split_by_empty_separator", "split_single_byte", "squeeze", "starts_with?", "strip", "sub", "sub_append", "sub_index", "sub_range", "succ", "titleize", "to_f", "to_f32", "to_f32?", "to_f64", "to_f64?", "to_f?", "to_f_impl", "to_i", "to_i128", "to_i128?", "to_i16", "to_i16?", "to_i32", "to_i32?", "to_i64", "to_i64?", "to_i8", "to_i8?", "to_i?", "to_s", "to_slice", "to_u128", "to_u128?", "to_u16", "to_u16?", "to_u32", "to_u32?", "to_u64", "to_u64?", "to_u8", "to_u8?", "to_unsafe", "to_unsigned_info", "to_utf16", "tr", "underscore", "unicode_delete_at", "unsafe_byte_at", "unsafe_byte_slice", "unsafe_byte_slice_string", "upcase", "valid_encoding?"]
Thread.methods                   # => ["detach", "event_base", "gc_thread_handler", "gc_thread_handler=", "initialize", "join", "main_fiber", "next", "next=", "previous", "previous=", "scheduler", "stack_address", "start", "to_unsafe"]
Bool.methods                     # => ["!=", "&", "==", "^", "clone", "hash", "to_s", "to_unsafe", "|"]
Int32.methods                    # => ["!=", "&", "&*", "&+", "&-", "*", "+", "-", "/", "<", "<=", "==", ">", ">=", "^", "clone", "leading_zeros_count", "popcount", "to_f", "to_f!", "to_f32", "to_f32!", "to_f64", "to_f64!", "to_i", "to_i!", "to_i128", "to_i128!", "to_i16", "to_i16!", "to_i32", "to_i32!", "to_i64", "to_i64!", "to_i8", "to_i8!", "to_u", "to_u!", "to_u128", "to_u128!", "to_u16", "to_u16!", "to_u32", "to_u32!", "to_u64", "to_u64!", "to_u8", "to_u8!", "trailing_zeros_count", "unsafe_chr", "unsafe_div", "unsafe_mod", "unsafe_shl", "unsafe_shr", "|"]
Float64.methods                  # => ["!=", "*", "**", "+", "-", "/", "<", "<=", "==", ">", ">=", "ceil", "clone", "fdiv", "floor", "next_float", "prev_float", "round_away", "round_even", "to_f", "to_f!", "to_f32", "to_f32!", "to_f64", "to_f64!", "to_i", "to_i!", "to_i128", "to_i128!", "to_i16", "to_i16!", "to_i32", "to_i32!", "to_i64", "to_i64!", "to_i8", "to_i8!", "to_s", "to_u", "to_u!", "to_u128", "to_u128!", "to_u16", "to_u16!", "to_u32", "to_u32!", "to_u64", "to_u64!", "to_u8", "to_u8!", "trunc"]
Proc.methods                     # => ["==", "===", "arity", "call", "clone", "closure?", "closure_data", "hash", "internal_representation", "partial", "pointer", "to_s"]
Crystalにはマクロがあります。マクロは、プログラムのコンパイル時に実行され、コードの生成や変換を行うための強力なツールです。Crystalのマクロは、プログラムの柔軟性を高めるために広く使用されます。
Crystalのマクロはmacroキーワードを使用して定義されます。以下は、基本的なマクロの例です：
macro say_hello
  puts "Hello, world!"
end
say_hello
 
このマクロは、say_helloを呼び出すとコンパイル時にputs "Hello, world!"が挿入され、プログラムが実行される際にはそのままputs "Hello, world!"が実行されます。
マクロは、引数を取ることもできます。例えば、以下のマクロは引数を受け取ってそれを使って出力します：
 
macro say(message)
  puts message
end
say "Hello, world!"
マクロは、コンパイル時にコードを生成するため、プログラムの最適化やパフォーマンスの向上にも役立ちます。しかし、過度に使用すると可読性が低下する可能性があるため、慎重に使う必要があります。
{% ... %}は、テンプレートエンジンやマクロなどの制御構造を含むテンプレート言語の一部として使用される構文です。Crystalにおいてもこのような構文が存在します。
Crystalでは、{% ... %}はマクロのブロックを定義するために使用されます。マクロはコンパイル時にコードを生成するためのものであり、{% ... %}ブロック内のコードはコンパイル時に実行されます。
以下は、Crystalでの{% ... %}を使用したマクロの例です：
macro generate_code
  {% for i in 1..3 %}
"
  {% end %}
end
 
generate_code
    #=>  Generated code 1
    #    Generated code 2
    #    Generated code 3
この例では、generate_codeというマクロが定義されています。マクロの中で{% for ... %}ブロックを使ってループを定義し、それぞれのループイテレーションでコンパイル時にコードが生成されます。
このように{% ... %}構文を使用することで、Crystalではコンパイル時の制御構造やコード生成を行うことができます。
class Point
  def initialize(@x : Int32, @y : Int32)
  end
  # macro定義
  macro attr_accessor(*attrs)
    {% for attr in attrs %}
 end
 = var end
    {% end %}
  end
  # macro呼出し
  attr_accessor :x, :y
end
pt = Point.new(20, 30)
p [pt.x, pt.y]
t = pt.x
pt.x = pt.y
pt.y = t
p [pt.x, pt.y]
実行結果:
[20, 30]
[30, 20]
Ruby には、attr_accessor と言う「クラスのメンバーのアクセサーを自動生成するメソッド」がありますが、Crystalにはないようなので、マクロで実装しました。
def name()     @name end
def name=(val) @name = val end
相当のコードが生成されます。
Crystalのマクロは、実行時ではなくコンパイル時に実行される機能であり、ソースコードを操作することができます。以下はCrystalのマクロの主な機能と構文の説明です。
マクロを定義するときに、引数に * を付けることができます。これにより、任意の数の引数を受け取ることができます。例えば、以下のマクロは、任意の数の引数をコンマで区切った文字列を出力します。
macro join_arguments(*args)
end
このマクロを使用すると、以下のようにコードを記述できます。
puts join_arguments(1, 2, 3) # => "1, 2, 3"
puts join_arguments("foo", "bar") # => "foo, bar"
で囲んだ式を埋め込むことができます。この式は、コンパイル時に評価され、その結果がコードに挿入されます。例えば、以下のマクロは、引数に指定された式を2倍にして返します。
macro double(expr)
 * 2
end
このマクロを使用すると、以下のようにコードを記述できます。
puts double(4) # => 8
puts double(6 + 2) # => 16
Crystalのマクロは、条件分岐や反復処理を行うことができます。以下に、それぞれの構文と使用例を示します。
条件分岐は、if/elsif/elseやcase/when/elseのような構文を使用することができます。マクロ内では、条件分岐によって生成するコードを制御することができます。
macro is_positive(number)
  {% if number > 0 %}
    puts "The number is positive"
  {% elsif number == 0 %}
    puts "The number is zero"
  {% else %}
    puts "The number is negative"
  {% end %}
end
is_positive(5)  # => The number is positive
is_positive(0)  # => The number is zero
is_positive(-5) # => The number is negative
macro to_japanese_weekday(day)
  {% case day %}
  {% when "Sunday" %}
    "日曜日"
  {% when "Monday" %}
    "月曜日"
  {% when "Tuesday" %}
    "火曜日"
  {% when "Wednesday" %}
    "水曜日"
  {% when "Thursday" %}
    "木曜日"
  {% when "Friday" %}
    "金曜日"
  {% when "Saturday" %}
    "土曜日"
  {% else %}
    raise "Invalid weekday: \#{day}"
  {% end %}
end
to_japanese_weekday("Monday") # => "月曜日"
to_japanese_weekday("foobar") # => Invalid weekday: foobar (RuntimeError)
反復処理は、whileやuntil、forのような構文を使用することができます。マクロ内では、反復処理によって生成するコードを制御することができます。
macro countdown(start)
  {% while start >= 0 %}
    puts "\#{start}..."
    {% start -= 1 %}
  {% end %}
  puts "Blast off!"
end
countdown(3)
macro countdown(start)
  {% until start < 0 %}
    puts "\#{start}..."
    {% start -= 1 %}
  {% end %}
  puts "Blast off!"
end
countdown(3)
macro fizzbuzz(n)
  {% for i in 1..n %}
    {% if i % 15 == 0 %}
      puts "FizzBuzz"
    {% elsif i % 3 == 0 %}
      puts "Fizz"
    {% elsif i % 5 == 0 %}
      puts "Buzz"
    {% else %}
      puts i
    {% end %}
  {% end %}
end
fizzbuzz(15)
メソッド p は、与えられた「式」の inspaect() の返す値を puts しますが、マクロ p! は、それに先んじて（評価前の）「式」を表示します[https://crystal-lang.org/api/1.5.0/Crystal/Macros.html#p%21%28%2Aexpressions%29%3ANop-instance-method def p!(*expressions) : Nop]。
p!の例:
x, y = true, false
p! x,y,x && y, x || y, x ^ y, !x, x != y, x == y
ary = [ 1, 2, 3 ]
p! ary
p! ary.map(&. << 1)
p! ary.map(&.to_f)
実行結果:
x      # => true
y      # => false
x && y # => false
x || y # => true
x ^ y  # => true
!x     # => false
x != y # => true
x == y # => false
ary # => [1, 2, 3]
ary.map(&.<<(1)) # => [2, 4, 6]
ary.map(&.to_f) # => [1.0, 2.0, 3.0]
マクロ p! は入れ子に出来ます。また、一旦ASTに変換してから再度ソースコードに変換するので、等価な別の構文に変換されることがあります。
入れ子のp!:
p! (
  100.times{|i|
    p! i
    break i if i > 12
  }
)
実行結果:
(100.times do |i|
 p!(i)
 if i > 12
   break i
 end
end) # => i
i # => 1
i # => 2
i # => 3
i # => 4
i # => 5
i # => 6
i # => 7
i # => 8
i # => 9
i # => 10
i # => 11
i # => 12
i # => 13
13
macro count_args(args)
end
macro tpl(value)
  {% if value.is_a?(String) %}
"
  {% else %}
  {% end %}
end
macro instance_var(name)
end
macro loop(num, █)
  {% for i in 0...num %}
  {% end %}
end
macro ifelse(condition, if_block, else_block)
  {% if condition %}
  {% else %}
  {% end %}
end
macro while(condition, █)
  {% while condition %}
  {% end %}
end
macro switch(value, cases)
  {% for case in cases %}
    {% if case[:value] == value %}
    {% end %}
  {% end %}
end
macro defined?(name)
end
これらのマクロは、Crystalのプログラム中で使用することができます。たとえば、以下のように使用することができます。
puts count_args("one", "two", "three")  # 3
puts tpl(123)  # 123
puts tpl("hello")  # "hello"
puts instance_var("name")  # @name
loop(5) do |i|
  puts i
end
ifelse(true, "It is true", "It is false")
i = 0
while i < 10 do
  puts i
  i += 1
end
switch(2, [
  { value: 1, block: "It is 1" },
  { value: 2, block: "It is 2" },
  { value: 3, block: "It is 3" }
])
puts defined?("name")  # @name
puts defined?("nonexistent")  # nil