LoginSignup
4
4

More than 5 years have passed since last update.

crystalをやってみる

Last updated at Posted at 2016-09-18

3連休ということで?、erlang, elixir, scala, rustとやった。で、頭がフラフラしてきたので、ほぼrubyだというcrystalを使ってみる。

まず、インストール

terminal
brew install crystal-lang

以下、使用感をいつくか。

シェルも正規表現もOK

最近シェルスクリプトの代わりにrubyを使ったので``(シェルプログラム呼び出し)が動くか見てみる。以下のようにmyがつくファイルを列挙し、正規表現でキャッシュしてみる。

test1.cr
text = `find ~/proj/ -name *my*`
text.split("\n").each do |row|
    if row =~ /(my.*)/
        puts $1
    end
end

上記は、Ruby(ruby test1.cr)でもCrystal(crystal test1.cr)でも全く同じに動く。合格

Rubyとは違うところ(空配列)

https://praveenatc42.wordpress.com/2016/01/09/crystal-for-rubyists/

を見るにRubyとCrystalは色々と違うらしい。例えば以下のように、型推定ができないので空配列は無理がある。

Compilation:- compiler error introduction
   array = []

   array << "hello" #=>  CRASH in crystal
   array << "hello" #=>  WORKS FINE in Ruby

   Solution : Specify types of value array can have upfront.

   array = [] of String # In crystal

しかし[] of Stringと型を追加してみれば、acc_text配列に集めていくようなコードも問題なく動く。

test2.cr
text = `find ~/proj/ -name *my*`
#acc_text = [] # for Ruby
acc_text = [] of String
text.split("\n").each do |row|
    if row =~ /(my.*)/
        acc_text << $1
    end
end
puts "#{acc_text}"

また、ダミーでもなんでも、最初に要素を入れておけば(例text = ["dummy"])型の指定は不要である。

test3.cr
text = `find ~/proj/ -name *my*`
acc_text = ["dummy"]
text.split("\n").each do |row|
    if row =~ /(my.*)/
        acc_text << $1
    end
end
acc_text.delete_at(0)   # remove "dummy" from array
puts "#{acc_text}"

並行プログラム = Beyond Ruby (spawn, concurrency, channel)

これがないと最近の言語を名乗れないらしい、「spawn, concurrency, channel」にcrystalは対応している

http://nithinbekal.com/notes/crystal/

Rubyにはない(Rubyで試すとspawn.cr:1:in': undefined method Channel' for main:Object (NoMethodError))ともちろんコンパイルエラーになる。

せっかくなので試してみる。

spawn.cr
ch = Channel(String).new
spawn do
  puts ch.receive
  puts ch.receive
  puts ch.receive
end

spawn do
  ch.send("apple")
  ch.send("orange")
end
ch.send("grape")
terminal
$ crystal spawn.cr
grape
apple
orange

#よく理解していないが動いているようだ。

が、次のようにすると

spawn2.cr
ch1 = Channel(String).new
ch2 = Channel(String).new
spawn do
  puts ch1.receive
  puts ch2.receive
  puts ch1.receive
end

spawn do
  ch1.send("apple2")
  ch2.send("orange2")
end
ch1.send("grape2")

以下のように、grape2しか出てこない。。なぜ??

terminal
$ crystal spawn2.cr
grape2

わかったような。

  • puts ch1.receiveのブロックに、ch1.send("grape2")は問題なし。
  • puts ch2.receiveのブロックに、ch1.send("apple2")は・・・、
    • 受信側は、ch2で待っているのに来たのはch1へのsend。といういうことでまだ待つ。
    • 送信側は、ch1.send("apple2")の後、ch2.send("orange2")をするかと思うと、ch1を受信してくれないので、次のch2.send("orange2")には行けず。

なるほど、送信側でもブロックするのか。。

最後に

erlang, elixir, scala, rustよりは簡単だろう。ということでちょっとcrystalを使ってみたが、思ったよりいい。

  • 単に高速なrubyではなく、新しい要素もあるようで思ったより良いかもしれない。asm文の挿入などもできるみたいだし。
  • 確かにrubyでかけるのは嬉しい!。少なくともrustのようにうるさくないし(が正規表現でキャッチせずに$1を表示したら実行時にfaultしたのでcrystalはrustのようにはメモリ安全ではない)。mix deps.getとかiex -S mix とかやるelixirより手続きが楽。
4
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4