Ruby 2.0 から Ruby 3.0 を駆け足で振り返る

12.5K Views

December 23, 21

スライド概要

https://toyama-eng.connpass.com/event/198500/

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

【オンライン開催】Burikaigi2021 から Ruby 3.0 を 駆け⾜で振り返る Ruby 2.0

2.

アジェンダ ⾃⼰紹介 Ruby とは Ruby 2.0 以前の歴史 Ruby 2.0 ~ 3.0 までを振り返る どういう機能が追加されてきたのかを振り返ってみる 最近のモダンな機能紹介‧デモ Ruby 2.7, 3.0 の機能紹介 まとめ

3.

⾃⼰紹介 名前: osyo Twitter : @pink_bangbi github : osyo-manga ブログ : Secret Garden(Instrumental) 趣味で Ruby にパッチを投げている Ruby 2.5 ~ 3.0 まで何らかのパッチ投げてるぞい… Ruby 基礎⽂法最速マスターって記事を書いたので Ruby が気になる ⼈は読んでみてね!!! 令和時代の Ruby 基礎⽂法最速マスター オンライン勉強会が多いので去年はあちこちの勉強会に参加 Fukuoka.rb Hamada.rb kawasaki.rb nikotama.rb Sendai.rb Shibuya.rb Shinjuku.rb Tama.rb Toyama.rb Gotanda.rb Entaku.rb Grow.rb Hamamatsu.rb kanazawa.rb Kobe.rb Machida.rb mitaka.rb Ruby Tuesday toruby .rb ⻄⽇暮⾥

4.

わたしと Ruby :はじめて Ruby を知る eval こわい、と思っていた記憶 Ruby 2.1:初めて Ruby を書き始める チャットボットを Ruby でつくってた Ruby 2.2:Ruby をガッツリ触り始める メタプログラミングで遊びまくっていた Ruby 2.4:Ruby 本体にパッチを投げ始める 開発中の Ruby 2.5 に投げたメソッド追加のパッチが取り込まれた Ruby 2.5:お仕事で Ruby を使い始める ここで初めて Rails を本格的に使い始める Ruby 2.6, 2.7, 3.0:毎バージョンごとにパッチを投げる 最近は細々したバグ修正パッチが多い… 最近:気になった Ruby の開発チケットをブログにまとめたりしてる https://secret-garden.hatenablog.com/archive/category/bugs.ruby Ruby 1.8,1.9

5.

Ruby とは

6.

Ruby とは まつもとゆきひろ(通称 :matz)が開発したオブジェクト指向⾔語 強い動的型付け⾔語 Perl や Smalltalk、Lisp の影響を受けている プリミティブな型も含めてすべてがオブジェクトとして扱われる コードを書いていて『楽しい‧気持ちがいい』をコンセプトに⾔語 設計されている MRI(Matz Ruby Interpreter)がいわゆる Ruby と呼ばれる処理系 他には JVM 上で動作する JRuby や 組み込み向けの軽量な mruby などが存在している 実務では主に Web フレームワークの Ruby on Rails で利⽤されている Ruby は死んだ? まだ⽣きてるよ!! 去年の 2020年12⽉25⽇に Ruby 3.0 がリリース

7.

Ruby 2.0 以前の歴史

8.

Ruby 2.0 以前の歴史 年: Ruby が誕⽣(開発が開始) 年: Ruby が発表 年: Ruby 1.0 がリリース 年: Ruby 1.8 がリリース 年: Ruby on Rails が公開 年: Ruby on Rails 1.0 がリリース 年: Ruby 1.9.0 がリリース ここで初めて YARV と呼ばれる仮想マシンが導⼊された 1.8 までは AST をインタラクティブに実⾏していた 1.9 からはバイトコードに置き換えて実⾏している 2009年:Ruby 1.9.1 がリリース 1.9 系の初めての安定版 2011年:JIS規格(JIS X 3017)が制定 2012年:ISO/IEC規格(ISO/IEC 30170)として承認 2013年:Ruby 2.0 がリリース 1993 1995 1996 2003 2004 2005 2007

9.

Ruby 2.0 ~ 3.0 までを振り返る

10.
[beta]
Ruby 2.0

にリリース : NEWS
キーワード引数の追加
2013/02/24

ラベル でキーワード引数を定義

#
:
def log(msg, level: "ERROR")
puts "[#{ level }] #{ msg }"
end
log("OK")
# => [ERROR] OK
log("NG", level: "WARNING") # => [WARNING] NG

正規表現エンジンとして⿁⾞の fork である Onigumo(⿁雲) を採⽤
デフォルトのスクリプトエンコーディングが UTF-8 に変更
以前は US-ASCII だった
デバッグ⽤のトレース機能として TracePoint API が追加
クラスが定義されたときやメソッドが呼ばれたタイミングなどで
任意の処理をフックすることができる機能
この機能を利⽤して profile などが実装されている

11.

Ruby 2.0 続き 継承リストの先頭にモジュールを追加する Module#prepend メソッド が追加 module NameIs def name "name is #{super}" end end すると先に class Homu # prepend prepend NameIs def name "homu" end end の継承リスト # Homu p Homu.ancestors p Homu.new.name NameIs のメソッドが呼ばれる # => [NameIs, Homu, Object, Kernel, BasicObject] # => "name is homu"

12.

Ruby 2.0 続き が実験的に実装 特定のファイルでのみクラス拡張が反映される Refinements module StringEx # refine String refine String do def twice self + self end end end を使って したファイルでのみ # using using StringEx p "homu".twice 参照 に新しいメソッドを定義する String#twice Ruby 2.0.0 Ruby 2.0.0 が使える リリース特集 の注意点やその他の新機能

13.

Ruby 2.1 にリリース : NEWS で実験的に追加された Refinements が正式に採⽤ ファイルだけではなくて class の中でも using できるようになった キーワード引数でデフォルト値を省略できるように変更 Ruby 2.0 ではデフォルト値が必須だった 新しい整数リテラルとして r と i が追加 2013/12/25 Ruby 2.0 p 42r.class p 42i.class # => Rational # => Complex メソッドを定義する def がメソッド名のシンボルを返すように変更 method_name = def twice(value:) value + value end p method_name # => :twice 参照:NEWS for Ruby 2.1.0 (Ruby 3.0.0 リファレンスマニュアル)

14.

Ruby 2.2 にリリース : NEWS シンボルのガベージコレクトを実装 以前はシンボルが解放されずに残ったままになり、⼤量にリクエ ストが投げられて新しいシンボルが⽣成され続けてメモリが枯渇 する問題があった 参照:(翻訳) Ruby 2.2 のシンボル GC nil / true / false オブジェクトが frozen 化 Hash リテラルのキーでクオートが使えるように変更 2014/12/25 { "hoge": 1 } # => {:hoge=>1} { "#{"foo" + "bar"}": 1 } # => {:foobar=>1} 参照:NEWS for Ruby 2.2.0 (Ruby 3.0.0 リファレンスマニュアル)

15.

Ruby 2.3 にリリース : NEWS Frozen String Literal プラグマコメントが追加 2015/12/25 # frozen_string_literal: true ⽂字列リテラルがデフォルトで frozen 化される # => true # p "hoge".frozen? 通称ぼっち演算⼦ &. )が追加 safe navigation operator( レシーバが # nil p nil&.hoge did_you_mean gem の場合はメソッドは呼ばれずに # => nil nil を返す が標準ライブラリに追加 p "hoge".cahrs # output: # error: undefined method `cahrs' for "hoge":String (NoMethodError) # Did you mean? chars # chr

16.
[beta]
Ruby 2.3
Hash#dig / Array#dig

続き

メソッドが追加

user = { address: { tel: [111, 2222, 3333] } }

ネストしてる値を 回で取得するを
メソッドで取得できる
# キーがない場合はその時点で探索を終了して nil を返す
p user.dig(:address, :tel2, 2)
# => nil
#
1
# user[:address][:tel][2]
dig
p user.dig(:address, :tel, 2)
# => 3333

インデントを取り除いてくれるヒアドキュメントの記法が追加
ヒアドキュメント内でインデントしても先頭のインデントのスペースは追加
されない
str = <<~EOS
#

homu
mami
saya
mado
EOS
p str.split("\n")
# => ["homu", "mami", "

saya", "mado"]

参照: サンプルコードでわかる!Ruby 2.3の主な新機能

17.

Ruby 2.4 にリリース : NEWS ハッシュテーブルの内部実装が変更されて⾼速化された 詳細: PHPとPythonとRubyの連想配列のデータ構造が同時期に同 じ⽅針で性能改善されてた話 - hnwの⽇記 Fixnum クラスと Bignum クラスが Integer クラスに統合 基本的に整数は Fixnum で扱われているが極端に値が⼤きい、⼩ さい時に Bignum を利⽤していた コード上で対話環境を起動することができる binding.irb が実装 2016/12/25 def plus(a, b) result = a + b この処理が呼ばれた時に対話環境が起動する 対話環境内でローカル変数などが参照できる # # binding.irb return result end 参照:サンプルコードでわかる!Ruby 2.4の新機能と変更点

18.

Ruby 2.5 にリリース : NEWS が⾃動ロードされるように変更 と書かずによくなった rescue/else/ensure が do/end ブロック内にも直接書けるように変更 2017/12/25 pp require "pp" # do ~ ensure ~ end hoge do # ... ensure # ... end をまとめてかける トップレベルの定数探索の仕組みが変更 class User; end class Address; end # p # # まではトップレベルの定数まで探索されてしまっていた 2.4 Address::User 2.4 => User 2.5 => error: uninitialized constant Address::User (NameError)

19.
[beta]
Ruby 2.5
Kernel#yield_self

続き

メソッドが追加

レシーバを受け取ってブロックの結果を返すメソッド

#
p 42.yield_self { |a| a.to_s + a.to_s }
# => "4242"

Hash#slice

メソッドが追加

homu = { id: 1, name: "homu", age: 14 }

指定したキーの要素のみを返す

#
p homu.slice(:name, :age)
# => {:name=>"homu", :age=>14}

キーワード引数で Struct の初期化ができるように設定を追加
にすると
# ここでキーワード引数を使って初期化できる
homu = User.new(name: "homu", age: 14)

# keyword_init: true
User = Struct.new(:name, :age, keyword_init: true)

サンプルコードでわかる!Ruby 2.5の主な新機能と変更点 Part 1

20.

終端なし Range リテラルを追加 (1..) のように終端を省略して Range リテラルを定義できる JIT コンパイラが導⼊ ⾃動的に YARV のバイトコードからバイナリコードに変換して⾼ 速に動作させる仕組み RubyのJITコンパイラを理解したい!rubykaigiの⾹りを添えて bundler が default gems として追加 これによりデフォルトで bundle コマンドが使えるようになった Ruby コードの抽象構⽂⽊を扱うライブラリが実験的に追加 Kernel#yield_self の別名として #then が追加 #select / #select! の別名として #filter / #filter! が追加 Proc を関数合成する Proc#<< と Proc#>> が追加 参照:プロと読み解く Ruby 2.6 NEWS ファイル

21.

Ruby 2.7 にリリース : NEWS パターンマッチ構⽂が実験的に導⼊ irb のサポートが強化 コンパクション GC を導⼊ ⼿動で GC.compact メソッドを呼ぶことでコンパクションを⾏う Ruby 3.0 で⾏われるキーワード引数の⾮互換対応の前準備 ⾮互換になる書き⽅をすると警告が出るように対応 (..10) のように先端なし Range リテラルを追加 2019/12/25 以下 を で表現して探索するようなことができる # 5 Range p (1..10).grep(..5) # => [1, 2, 3, 4, 5]

22.

Ruby 2.7 続き 改⾏を含むメソッドチェインをコメントアウト可能に対応 ここでコメントアウトできるようになった hoge # # .foo .bar デフォルトのブロックの仮引数として Numbered Parameters が導⼊ (通称ナンパラ) が第⼀引数、 が第⼆引数を参照する # _1 _2 plus = proc { _1 + _2 } plus.call 1, 2 # => 3 p (20..30).map { _1.to_s(16) } # => ["14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e"] 引数をフォワードする (...) 記法を追加 の引数をそのまま def foo(...) # foo bar(...) end bar にフォワードする

23.

Ruby 2.7 Enumerable#tally 続き が追加 配列の要素数とその要素を として返す # Hash ["a", "b", "c", "b"].tally #=> {"a"=>1, "b"=>2, "c"=>1} Enumerable#filter_map を追加 ブロックの戻り値が偽なら結果から取り除く # p (1..10).filter_map { _1 + _1 if _1.even? } # => [4, 8, 12, 16, 20] 参照:プロと読み解くRuby 2.7 NEWS

24.

Ruby 3.0 にリリース : NEWS は と⽐較して 3倍早くすることを⽬標にしてきた 「ており⽬標を達成できた ベンチマーク」では JIT コンパイル時に 3倍早くなっ パターンマッチが正式に採⽤ 並列処理をサポートする Ractor ライブラリが実験的に追加 型のサポートを⾏う RBS と TypeProf の 2つのライブラリが追加 キーワード引数の⾮互換な変更 詳しくはこちら 右代⼊が追加 エンドレスメソッド定義が追加 正規表現リテラルや Range リテラルがデフォルトで frozen 化 参照: プロと読み解く Ruby 3.0 NEWS - クックパッド開発者ブログ Ruby 3.0 Advent Calendar 2020 2020/12/25 Ruby 3.0 Ruby 2.0 Optcarrot

25.

最近のモダンな機能紹介‧デモ

26.
[beta]
パターンマッチ

で実験的に⼊って 3.0 で正式に導⼊された
という書き⽅になる

Ruby 2.7
case expr; in pattern; end

各要素ごとに で条件(型)を指定できる
その要素を で変数に束縛できる
で複数の要素を受け取ることも可能
マッチしなかったら
節にくる
マッチしなくて
節がなければ例外

#
in
#
=>
# *
#
else
#
else
case [1, "homu", 14]
in [Integer, String => name, Integer => age]
{ name: name, age: age }
in [Integer, Integer => age, String => name]
{ name: name, age: age }
in [first, *middle, last]
middle
in []
[]
else
{}
end

27.
[beta]
パターンマッチ 続き

形式でも記述できる
キーの要素ごとに条件を指定できる

Hash

def check(user)
case user
# age
20
in { name:, age: (..20) }
"#{name}
"
# age
20
in { name:, age: (20..) }
"#{name}
#
else
"
"
end
end

が 未満
は魔法少⼥です
が 以上
は魔法少⼥ではありません"
それ以外
わからない
ほむ
マミ

p check({ name: "
", age: 14 })
p check({ name: "
", age: 30 })
p check(["
", 14])

まどか

ほむは魔法少⼥です"
マミは魔法少⼥ではありません"
わからない"

# => "
# => "
# => "

28.
[beta]
⾏ in (実験的)

1

パターンマッチを 1⾏でかける構⽂
これは Ruby 3.0 では実験的な機能になる
使⽤すると実験的な警告が出るので注意
Ruby 2.7 でも使⽤できるが挙動がちょっと違うので注意

これは普通のパターンマッチ

#
case user
in { name: String, age: (..20) }
end

上のパターンマッチが ⾏でかける
マッチしてたら
を返し、そうでなければ

#
1
#
true
false
# warning: One-line pattern matching is experimental, and the behavior
may change in future versions of Ruby!
user in { name: String, age: (..20) }

を返す

29.
[beta]
⾏ in (実験的) 続き

1
1

⾏ in は真理値を返すので if ⽂などの条件式として利⽤できる

user = { name: "mami", age: 30 }

条件分岐をより細かく記述できる

#
if user in { name: String, age: (..20) }
puts "OK"
else
puts "NG"
end
users = [
{ name: "homu", age: 14 },
{ name: "mami", age: 15 },
{ name: "mado", age: 14 }
]

こんな感じで

と併⽤するとより簡潔に要素の絞り込みができる

#
_1
pp users.select { _1 in { name: /^m/, age: (..15) } }
# => [{:name=>"mado", :age=>14}]

30.

右代⼊ (⼀部実験的) 左辺の値を右辺の変数に代⼊する構⽂ 左辺の値を右辺の変数に代⼊する # 42 => value コードは通常左から右に向かって記述する、その流れのまま代⼊式 を書くことができるので気持ちよくコードがかける 左からコードを書いてそのまま変数に代⼊できる # (1..10).select { _1.even? }.map { _1 + _1 } => result pp result # => [4, 8, 12, 16, 20] ちなみにインスタンス変数やグローバル変数には代⼊できないので注意 # # error: syntax error, unexpected instance variable 1 => @value 2 => $value

31.
[beta]
右代⼊ (⼀部実験的) 続き

この右代⼊はパターンマッチを利⽤している
なので Hash の要素をの中⾝を直接受け取ることができる
user = { name: "mami", age: 15 }

パターンマッチのように特定のキーの要素を束縛できる

#
user => { name:, age: }
pp name # => "mami"
pp age
# => 15

もっと厳密にパターンを書くこともできる

#
user => { name: String => name, age: (..20) => age }
pp name
pp age

とは違いパターンにマッチしなかったら例外が発⽣する

# in
user => { name: Integer }
# => raise NoMatchingPatternError

この書き⽅は Ruby 3.0 では実験的な警告が出るので注意

32.

エンドレスメソッド定義 ⾏でメソッドが定義できるようになった end を書かなくてメソッドが定義できるのでエンドレスメソッド 元々は エイプリルフールネタ だったのが matz の意向ではいっち ゃった 1 を書かずにメソッドが定義できる 引数がある場合は は省略できないので注意 # end # () def twice(a) = a + a p twice 42 # => 84 引数がない場合は # def value = 42 () は省略できる また 付きメソッドは定義できないので注意 # = # error: setter method cannot be defined in an endless method definition def value=(value) = @value = value

33.

実験的) Ractor ( は で並列処理を⾏うためのライブラリ の略 Ruby 3.0 では実験的な機能でまだちょっと不安定 CPU のコアごとに独⽴した処理を実⾏できる 基本的に処理ごとにメモリ領域は共有せず、よりスレッドセーフな 実装になっている 参照: Ruby 3.0 の Ractor を⾃慢したい - クックパッド開発者ブログ Ruby の Ractor で ActiveObject を作る Ruby に So ware Transactional Memory (STM) を⼊れようと思った 話 - クックパッド開発者ブログ Ractor Ruby Ruby + Actor

34.

実験的) 続き Ractor ( Ractor デモ のブロックが並列処理として実⾏される の引数をブロックの引数として受け取る事ができる # Ractor.new # .new ractor = Ractor.new(10) do |loop_count| loop_count.times do puts :hello sleep 0.3 end end 並列処理が終了するまでブロッキングする # # p ractor.take 10.times do puts :world sleep 0.3 end

35.

型周りのサポート では の型情報を記述するために RBS を利⽤する ファイルに のような構⽂で型情報を記述する ではこの をサポートするライブラリが追加された ファイルや ⾃体を扱うライブラリ コードを解析して .rbs を⾃動⽣成するライブラリ また という外部ライブラリで 時点では標準では型チェックを⾏う機能は⼊らない .rbs を使った型チェックを⾏うこ とができる TypePorf デモ 参照: Ruby 3 の静的解析ツール TypeProf の使い⽅ - クックパッド開発者 ブログ Ruby 3の静的解析機能のRBS、TypeProf、Steep、Sorbetの関係に ついてのノート - クックパッド開発者ブログ Ruby 3.0 Ruby .rbs Ruby Ruby 3.0 RBS RBS: .rbs RBS TypeProf: Ruby Ruby 3.0 steep

36.

最近の irb 事情 以前では GNU Readline で対話環境を実装していた の標準ライブラリにも readline がある では GNU Readline をピュア Ruby で実装した reline というラ イブラリが開発され irb はそれを利⽤するように改良された これにより Ruby 2.7 以降では以下のような機能が irb で利⽤できるよ うになった 複数⾏編集機能 シンタックスハイライト 簡単なコード補完 リファレンスの参照機能 Ruby 3.0 では measure 機能が追加された 実⾏時間が簡単に計測できるようになる gem install stackprof すると measure :stackprof でメソッドのプロ ファイリングも⾏われる 参照:IRB’s Built-in Measure | Jemma Issroff Ruby 2.7 Ruby Ruby 2.7

37.

まとめ

38.

まとめ が誕⽣してから今年で 28年になる が死んだと毎年⾔われているががんばって開発されている ちゃんと⽣きてるんじゃあ 特に近年では新しい構⽂などが取り込まれる傾向にある わたしはまだ Ruby を使い始めて浅いのでスライドを書いてて昔の Ruby について知ることができてよかった ⾃分が使っている⾔語がどのように開発されているのか調べてみる と⾊々と学びがある そのが機能が追加された背景とか調べてみたりすると楽しい Ruby は書いてて気持ちがよいのでぜひぜひ触ってみましょう! Ruby Ruby

39.

参照 とは 【第 1 回】 まつもとゆきひろさん まつもとゆきひろの履歴書|仕事中にこっそり作った Rubyが世界を 驚かせるまで - ぼくらの履歴書|トップランナーの履歴書から「仕 事⼈⽣」を深掘り! まつもと ×笹⽥、Ruby 1.9を語る − @IT これからの Ruby と今の Ruby について - Speaker Deck 12⽉25⽇にリリースされる Ruby 3.0 に備えよう! - Speaker Deck Ruby Rubyist Hotlinks

40.

ご清聴 ありがとうございました