LoginSignup
0
0

Ruby3.0, 3.1で追加、変更された機能

Last updated at Posted at 2023-06-11
1 / 13

アジェンダ

  • キーワード引数が通常引数から分離
  • ハッシュリテラルやキーワード引数の値が省略可能になった
  • 引数委譲の記法の拡張
  • パターンマッチがexperimentalな機能ではなくなった
    • 1行パターンマッチ
      • 右代入
      • in
  • 1行メソッド定義
  • private attr_reader :fooと書けるようになった
  • ブロックを移譲する記法が導入された
  • エラー発生位置をわかりやすく示すerror_highlightが導入された
  • compaction GC を自動でやってくれる GC.auto_compact = true が追加された

ここから下は興味があったらどうぞ

  • Ractor(並列処理ライブラリ)
  • Fiber scheduler(I/Oブロッキングのスケジューラ)
  • MJIT/YJIT(JITコンパイラの実装)
  • RBS(静的解析基盤)
  • debug gem(デバッガ刷新)

キーワード引数が通常引数から分離

ruby3からはキーワード引数を渡すときは明示的に渡す必要がある

kwargs.rb
# キーワード引数を受け取るメソッド
def foo(key: 42); end

foo(key: 42)      # OK: キーワード引数を渡している

opt = { key: 42 }
foo(opt)          # NG: 普通の引数を渡しているのでエラー(2.7では警告付きで動いていた)

foo(**opt)        # OK: ハッシュを明示的にキーワードに変換している

キーワード引数から普通の引数への暗黙的変換は維持されている

# 普通のオプショナル引数を受け取るメソッド
def foo(opt = {}); end

foo(key: 42) # OK: キーワード引数が暗黙的に普通の引数に変換される

# # ↑は動きますが、今後は次のように書くのがおすすめです
# def foo(**opt); end

ハッシュリテラルやキーワード引数の値が省略可能になった

{ x: x, y: y } の省略記法として { x:, y: } と書けるようになった

x = 1
y = 2

# h = { x: x, y: y } と同じ意味
h = { x:, y: }

p h  #=> {:x=>1, :y=>2}

また、キーワード引数でも同様の省略ができるようになりました

def foo(a:)
  p a
end

a = 1

# foo(a: a) と同じ意味
foo(a:)

最初は、JavaScriptと同じものがRubyにも欲しい、という提案だったが、数学の集合にしか見えないということで、却下されたらしい

javascript.js
// 数学の集合にしか見えないため却下
const x = 1
const y = 2
{ x, y }

パターンマッチがexperimentalな機能ではなくなった

パターンマッチ(2.7)のおさらい

case/inでパターンマッチできる機能が2.7から試験的に導入
3.0で正式な機能になった
パターンマッチの仕様自体が複雑なため簡単なののみ紹介

case {a: 0, b: 1, c: 2}
in {a: 0, x: 1}
  :unreachable
in {a: 0, b: var}
  p var #=> 1
end
# どのパターンにもマッチしない場合、case/whenと違いNoMatchingPatternError例外が投げられる

case [1, 2]
in [x, y]
  p x #=> 1
  p y #=> 2
end

# インスタンス変数はパターンマッチの代入につかえない
case 1
in @val
end
SyntaxError: unexpected instance variable

一行パターンマッチ

右代入

パターンマッチの一部として右代入が導入された

{ a: 1, b: 2, c: 3 } => hash

p hash #=> { a: 1, b: 2, c: 3 }

{ a: 1, b: 2, c: 3 } => { a:, b:, c: }

p a #=> 1
p b #=> 2
p c #=> 3

{ a: 1, b: 2, c: 3 } => { a:, b:, c:, d: }  # NoMatchingPatternError(キーワード `d` がないため)

# 例ではjsonの値取得に使っていた
json = <<END
{
  "name": "Alice",
  "age": 30,
  "children": [{ "name": "Bob", "age": 2 }]
}
END
JSON.parse(json, symbolize_names: true) => 
  {name: "Alice", children: [{name: child_name, age: }]}

p child_name #=> "Bob"
p age        #=> 2

# 括弧が省略可能
[1, 2, 3] => x, y, z
{ a: 1, b: 2, c: 3} => a:, b:, c:

in

caseを省略してinだけでもかける
右代入との違いは返り値にtrue/falseを返すこと

p [1, 2] => [a, b] #=> nil
p [1, 2] in [x, y] #=> true

if json in { type: :account, property: }
  p property
end

1行メソッド定義

1行でメソッド定義ができるようになった

def square(x) = x * x

p square(5) #=> 25

# putsに括弧がつけなくてもかける(3.1から)
def foo = puts 'hoge' 

private attr_reader :fooと書けるようになった

attr_readerが返す値やprivateに渡せる値が変わったことでアクセス指定子がシンプルにかけるようになった

class Foo
  # (1) attr_reader や attr_accessor が定義したメソッドのシンボルの配列を返すようになった
  attr_accessor :foo, :bar #=> [:foo, :foo=, :bar, :bar=]

  # (2) public や private が配列を引数に受け取れるようになった
  private [:foo, :foo=, :bar, :bar=]

  # 2 つを組み合わせると、次のように書いても同じ意味になる
  private attr_accessor :foo, :bar
end

ブロックを移譲する記法が導入された

ブロックを受ける引数を無名にして渡すことができるようになりました。

def foo(&)
  bar(&)
end

# ↓のものと意味的には同じ
def foo(&block)
  bar(&block)
end

引数委譲の記法の拡張

2.7から導入された引数以上の...が先頭引数を受け取れるようになった

def method_missing(meth, ...)
  send(:"do_#{meth}", ...)
end

エラー発生位置をわかりやすく示すerror_highlightが導入された

# hogeでエラーが起きたことがわかりやすくなった
~/repo/cabernet$ cat error.rb       
'aaa'.to_s.hoge
~/repo/cabernet$ be rails r error.rb
error.rb:1:in `<top (required)>': undefined method `hoge' for "aaa":String (NoMethodError)

'aaa'.to_s.hoge
          ^^^^^

compaction GC を自動でやってくれる GC.auto_compact = true が追加された

2.7から入っていたGC.compactを自動でやってくれる機能が追加された
詳細はよくわからん & 導入は難しいかもだが面白そうだったので一応紹介

GC.auto_compact = true とすることで、major GC が起こるとコンパクションも実行してくれます。
そのため、定期的にメモリの掃除をしてくれることになり、メモリ効率の向上、および局所性向上による性能改善が期待できます。
が、ここにも書いてある通り、コンパクション自体が結構なオーバヘッドになるので、自分のアプリで効くかどうか確認してみるといいと思います。
デフォルトは、そういうことで false です。

<中略>

正直、まだ実装がこなれていないような気がするので(拡張ライブラリあたりが怪しいです)、みんながすぐにこれを使うってのには、
ならない気がします(はまらなければ、使ってもいいと思います)。

その他大きな変更
興味ある方はどうぞ
RactorとFiberは今後重要になりそうな雰囲気ありますが、自分には難しくてよくわかりませんでした。

Ractor(並行処理ライブラリ)

actor modelを強く意識して実装されたRubyの並行処理ライブラリ
スレッドプログラミングをもっと簡単に実装したいという目的で作成されたらしい。
しかし難しい
Ractor周りの処理を楽にするために値をimmutableにする変更が今後入ってきそう

def tarai(x, y, z) =
  x <= y ? y : tarai(tarai(x-1, y, z),
                     tarai(y-1, z, x),
                     tarai(z-1, x, y))
require 'benchmark'
Benchmark.bm do |x|
  # sequential version
  x.report('seq'){ 4.times{ tarai(14, 7, 0) } }

  # parallel version
  x.report('par'){
    4.times.map do
      Ractor.new { tarai(14, 7, 0) }
    end.each(&:take)
  }
end

4 cores, 8 hardware threads CPUでの実行結果

Benchmark result:
          user     system      total        real
seq  64.560736   0.001101  64.561837 ( 64.562194)
par  66.422010   0.015999  66.438009 ( 16.685797)

Fiber scheduler(I/Oブロッキングのスケジューラ)

Fiber(軽量スレッド goroutine的なもの)で処理がI/O などでブロックされたら、
他の独立した Fiber を実行するようなスケジューラを、
Ruby で記述するための仕組みが Fiber scheduler です。

今後ActiveRecordとかの内部で非同期処理につかわれる?

MJIT/YJIT(JITコンパイラの実装)

JIT(Just In Time)コンパイラ
必要な分を、必要な時に」コンパイルするコンパイラ

  • MJIT MRI (Matz' Ruby Implementation) JIT コンパイラ
  • YJIT Shopifyが開発したRailsの高速化を目指したJITコンパイラ

RBS(静的解析基盤)

Rubyで型を定義できるようになった。

  • RBS:Ruby の型情報を扱う言語。Ruby 3 にバンドルされる。
  • TypeProf:型注釈のない Ruby コードを型解析するツール。Ruby 3 にバンドルされる。
  • Steep/Sorbet:Ruby で静的型付けのプログラミングができるツール。

debug gem(デバッガ刷新)

デフォルトのデバッガが便利になったらしい


参考

0
0
0

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
0
0