RubyistのためのCrystal¶
CrystalはRubyのような構文を持っていますが、Crystalは別のRuby実装ではなく、異なる言語です。この理由から、そして主にコンパイルされた静的型付き言語であるため、Rubyと比較するといくつかの大きな違いがあります。
コンパイル言語としてのCrystal¶
crystal
コマンドの使用¶
foo.cr
というプログラムがある場合
# Crystal
puts "Hello world"
これらのコマンドのいずれかを実行すると、同じ出力が得られます。
$ crystal foo.cr
Hello world
$ ruby foo.cr
Hello world
crystal
がファイルを解釈しているように見えますが、実際にはファイルfoo.cr
は最初に一時的な実行可能ファイルにコンパイルされ、その後この実行可能ファイルが実行されます。この動作は、通常ファイルをコンパイルしてすぐに実行したい場合に、開発サイクルで非常に役立ちます。
コンパイルだけしたい場合は、build
コマンドを使用できます。
$ crystal build foo.cr
これにより、foo
という実行可能ファイルが作成され、./foo
で実行できます。
これは最適化されていない実行可能ファイルを作成することに注意してください。最適化するには、--release
フラグを渡します。
$ crystal build foo.cr --release
ベンチマークを作成したり、パフォーマンスをテストしたりする場合は、常にリリースモードでコンパイルすることを忘れないでください。
引数なしでcrystal
を呼び出すか、コマンドと引数なしでcrystal
を呼び出す(たとえば、crystal build
はそのコマンドで使用できるすべてのフラグをリストします)ことで、他のコマンドとフラグを確認できます。または、マニュアルを読むこともできます。
型¶
Bool¶
true
とfalse
は、クラスTrueClass
またはFalseClass
のインスタンスではなく、Bool
型です。
整数¶
RubyのFixnum
型には、Crystalの整数型Int8
、Int16
、Int32
、Int64
、UInt8
、UInt16
、UInt32
、またはUInt64
のいずれかを使用します。
RubyのFixnum
に対する操作がその範囲を超えると、値は自動的にBignum
に変換されます。Crystalは代わりにオーバーフロー時にOverflowError
を発生させます。例:
x = 127_i8 # An Int8 type
x # => 127
x += 1 # Unhandled exception: Arithmetic overflow (OverflowError)
Crystalの標準ライブラリは、任意のサイズと精度の数値型を提供します。BigDecimal
、BigFloat
、BigInt
、BigRational
。
言語リファレンスの整数を参照してください。
正規表現¶
グローバル変数$`
と$'
はサポートされていません(まだ$~
と$1
、$2
、...は存在します)。$~.pre_match
と$~.post_match
を使用してください。詳細はこちら。
削減されたインスタンスメソッド¶
Rubyでは同じことをするためのメソッドがいくつかある場合、Crystalでは1つしかない場合があります。具体的には
Rubyメソッド | Crystalメソッド |
---|---|
Enumerable#detect |
Enumerable#find |
Enumerable#collect |
Enumerable#map |
Object#respond_to? |
Object#responds_to? |
length 、size 、count |
size |
省略された言語構造¶
Rubyにはいくつかの代替構造がある場合、Crystalには1つあります。
- 後置の
while
/until
はありません。ただし、接尾辞としてのifはまだ利用可能です。 and
とor
: 代わりに&&
と||
を使用し、優先順位を示すために適切な括弧を使用します。- Rubyには
Kernel#proc
、Kernel#lambda
、Proc#new
、->
がありますが、CrystalではProc(*T, R).new
と->
を使用します(参照についてはこちらを参照してください)。 require_relative "foo"
の場合は、require "./foo"
を使用します。
配列の自動スプラットと強制された最大ブロック引数¶
[[1, "A"], [2, "B"]].each do |a, b|
pp a
pp b
end
次のようなエラーメッセージが生成されます。
in line 1: too many block arguments (given 2, expected maximum 1)
ただし、不要な引数を省略しても問題ありません(Rubyの場合と同様)。例:
[[1, "A"], [2, "B"]].each do # no arguments
pp 3
end
または
def many
yield 1, 2, 3
end
many do |x, y| # ignoring value passed in for "z" is OK
puts x + y
end
タプルには自動スプラットがあります。
[{1, "A"}, {2, "B"}].each do |a, b|
pp a
pp b
end
期待どおりの結果が返されます。
Rubyの自動スプラットと同じ結果を得るために、明示的にアンパックすることもできます。
[[1, "A"], [2, "B"]].each do |(a, b)|
pp a
pp b
end
次のコードも機能しますが、前者をお勧めします。
[[1, "A"], [2, "B"]].each do |e|
pp e[0]
pp e[1]
end
#each
はnilを返す¶
Rubyでは、.each
はArray
やHash
などの多くの組み込みコレクションに対してレシーバーを返します。これにより、そのメソッドを連鎖させることができますが、Crystalではパフォーマンスとコード生成の問題につながる可能性があるため、この機能はサポートされていません。代わりに、.tap
を使用できます。
Ruby
[1, 2].each { "foo" } # => [1, 2]
Crystal
[1, 2].each { "foo" } # => nil
[1, 2].tap &.each { "foo" } # => [1, 2]
リフレクションと動的評価¶
Kernel#eval()
と奇妙なKernel#autoload()
は省略されています。オブジェクトとクラスのイントロスペクションメソッドObject#kind_of?()
、Object#methods
、Object#instance_methods
、およびClass#constants
も省略されています。
場合によっては、マクロをリフレクションに使用できます。
意味的な違い¶
シングルクォートとダブルクォートの文字列¶
Rubyでは、文字列リテラルはシングルクォートまたはダブルクォートで区切ることができます。Rubyのダブルクォートで囲まれた文字列は、リテラル内部で変数展開が行われますが、シングルクォートで囲まれた文字列は行われません。
Crystalでは、文字列リテラルはダブルクォートのみで区切られます。シングルクォートは、C言語のような文字リテラルとして機能します。Rubyと同様に、文字列リテラル内では変数展開が行われます。
まとめ
X = "ho"
puts '"cute"' # Not valid in crystal, use "\"cute\"", %{"cute"}, or %("cute")
puts "Interpolate #{X}" # works the same in Ruby and Crystal.
RubyやPythonのトリプルクォートで囲まれた文字列リテラルはサポートされていませんが、文字列リテラルには改行を埋め込むことができます。
"""Now,
what?""" # Invalid Crystal use:
"Now,
what?" # Valid Crystal
Crystalは多くのパーセント文字列リテラルをサポートしています。
[]
と[]?
メソッド¶
Rubyでは、[]
メソッドは一般的に、そのインデックス/キーによる要素が見つからない場合、nil
を返します。例として
# Ruby
a = [1, 2, 3]
a[10] #=> nil
h = {a: 1}
h[1] #=> nil
Crystalでは、このような場合、例外がスローされます。
# Crystal
a = [1, 2, 3]
a[10] # => raises IndexError
h = {"a" => 1}
h[1] # => raises KeyError
この変更の背景にある理由は、すべてのArray
またはHash
へのアクセスが潜在的な値としてnil
を返す可能性がある場合、この方法でプログラミングするのが非常に面倒になるからです。これでは機能しません。
# Crystal
a = [1, 2, 3]
a[0] + a[1] # => Error: undefined method `+` for Nil
インデックス/キーが見つからない場合にnil
を取得したい場合は、[]?
メソッドを使用できます。
# Crystal
a = [1, 2, 3]
value = a[4]? # => return a value of type Int32 | Nil
if value
puts "The number at index 4 is : #{value}"
else
puts "No number at index 4"
end
[]?
は、コンテナのようなクラスに対して定義できる(そして定義すべき)通常のメソッドです。
もう一つ知っておくべきことは、次のような場合
# Crystal
h = {1 => 2}
h[3] ||= 4
プログラムは実際には次のように変換されるということです。
# Crystal
h = {1 => 2}
h[3]? || (h[3] = 4)
つまり、[]?
メソッドは、インデックス/キーの存在を確認するために使用されます。
[]
がnil
を返さないのと同様に、一部のArray
およびHash
メソッドもnil
を返さず、要素が見つからない場合は例外を発生させます。first
、last
、shift
、pop
など。これらには、nil
の動作を取得するための疑問符付きメソッドも用意されています。first?
、last?
、shift?
、pop?
など。
慣例では、obj[key]
は値を返すか、key
がない場合は例外を発生させ(「ない」の定義はobj
のタイプによって異なります)、obj[key]?
は値を返すか、key
がない場合はnilを返します。
他のメソッドについては、異なります。同じタイプにfoo
という名前のメソッドと別のfoo?
がある場合、それはfoo
が何らかの条件で例外を発生させ、foo?
が同じ条件でnilを返すことを意味します。foo?
バリアントのみが存在し、foo
が存在しない場合は、真または偽の値(必ずしもtrue
またはfalse
ではない)を返します。
上記のすべての例
Array#[](index)
は範囲外で例外を発生させ、Array#[]?(index)
はその場合にnilを返します。Hash#[](key)
はキーがハッシュにない場合に例外を発生させ、Hash#[]?(key)
はその場合にnilを返します。Array#first
は配列が空の場合に例外を発生させ(「first」がないため、「first」がない)、Array#first?
はその場合にnilを返します。pop/pop?、shift/shift?、last/last?についても同様です。String#includes?(obj)
、Enumerable#includes?(obj)
、Enumerable#all?
がありますが、これらには疑問符なしのバリアントはありません。前のメソッドは確かにtrueまたはfalseを返しますが、それは必要な条件ではありません。
for
ループ¶
for
ループはサポートされていません。代わりに、Enumerable#each
を使用することをお勧めします。それでもfor
が必要な場合は、マクロを介して追加できます。
macro for(expr)
{{expr.args.first.args.first}}.each do |{{expr.name.id}}|
{{expr.args.first.block.body}}
end
end
for i ∈ [1, 2, 3] do # You can replace ∈ with any other word or character, just not `in`
puts i
end
# note the trailing 'do' as block-opener!
メソッド¶
Rubyでは、以下は引数エラーを発生させます。
def process_data(a, b)
# do stuff...
end
process_data(b: 2, a: "one")
これは、Rubyでは、process_data(b: 2, a: "one")
がprocess_data({b: 2, a: "one"})
の構文糖であるためです。
Crystalでは、コンパイラはprocess_data(b: 2, a: "one")
を、名前付き引数b: 2
とa: "one"
を使用してprocess_data
を呼び出すものとして扱います。これは、process_data("one", 2)
と同じです。
プロパティ¶
Rubyのattr_accessor
、attr_reader
、およびattr_writer
メソッドは、異なる名前のマクロに置き換えられています。
Rubyキーワード | Crystal |
---|---|
attr_accessor |
property |
attr_reader |
getter |
attr_writer |
setter |
例
getter :name, :bday
さらに、Crystalはnil可能またはブール値のインスタンス変数用のアクセサマクロを追加しました。それらの名前には疑問符(?
)が含まれています。
Crystal |
---|
property? |
getter? |
例
class Person
getter? happy = true
property? sad = true
end
p = Person.new
p.sad = false
puts p.happy?
puts p.sad?
これはブール値用ですが、任意の型を指定できます。
class Person
getter? feeling : String = "happy"
end
puts Person.new.feeling?
# => happy
ドキュメントのgetter?および/またはproperty?について詳しくお読みください。
一貫したドット表記法¶
たとえば、RubyのFile::exists?
は、CrystalではFile.exists?
になります。
Crystalキーワード¶
Crystalはいくつかの新しいキーワードを追加しました。これらはメソッド名として使用できますが、ドットを使用して明示的に呼び出す必要があります。例:self.select { |x| x > "good" }
。
利用可能なキーワード¶
abstract do if nil? select union
alias else in of self unless
as elsif include out sizeof until
as? end instance_sizeof pointerof struct verbatim
asm ensure is_a? private super when
begin enum lib protected then while
break extend macro require true with
case false module rescue type yield
class for next responds_to? typeof
def fun nil return uninitialized
プライベートメソッド¶
Crystalでは、各プライベートメソッドにprivate
キーワードを付ける必要があります。
private def method
42
end
RubyからCrystalへのハッシュ構文¶
Crystalは、Rubyでは利用できないデータ型、NamedTuple
を導入します。
通常、Rubyでは、いくつかの構文でハッシュを定義できます。
# A valid ruby hash declaration
{
key1: "some value",
some_key2: "second value"
}
# This syntax in ruby is shorthand for the hash rocket => syntax
{
:key1 => "some value",
:some_key2 => "second value"
}
Crystalでは、そうではありません。Crystalでハッシュを宣言するには、Hash
ロケット=>
構文が必要です。
ただし、RubyのHash
ショートハンド構文は、CrystalでNamedTuple
を作成します。
# Creates a valid `Hash(Symbol, String)` in Crystal
{
:key1 => "some value",
:some_key2 => "second value",
}
# Creates a `NamedTuple(key1: String, some_key2: String)` in Crystal
{
key1: "some value",
some_key2: "second value",
}
NamedTuple
と通常のTuple
はサイズが固定されているため、コンパイル時に既知のデータ構造に最適です。
疑似定数¶
Crystalは、実行中のソースコードに関するリフレクティブデータを提供するいくつかの疑似定数を提供します。
Crystal | Ruby | 説明 |
---|---|---|
__FILE__ |
__FILE__ |
現在実行中のcrystalファイルへのフルパス。 |
__DIR__ |
__dir__ |
現在実行中のcrystalファイルが配置されているディレクトリへのフルパス。 |
__LINE__ |
__LINE__ |
現在実行中のcrystalファイルの現在の行番号。 |
__END_LINE__ |
- | 呼び出しブロックの終了行の行番号。メソッドパラメータのデフォルト値としてのみ使用できます。 |
ヒント
__DIR__
と__dir__
の詳細
Ruby Gems用のCrystal Shards¶
多くの人気のあるRuby gemが、Crystalに移植または書き直されています。Ruby Gemsの同等のCrystal Shardsを以下に示します。
RubyとCrystalの違いに関するその他の質問については、FAQをご覧ください。