Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
マニアックなRuby 2.7新機能紹介
Search
Yusuke Endoh
January 30, 2020
Programming
0
19
マニアックなRuby 2.7新機能紹介
鹿児島Ruby会議01
https://k-ruby.github.io/kagoshima-rubykaigi01/
Yusuke Endoh
January 30, 2020
Tweet
Share
More Decks by Yusuke Endoh
See All by Yusuke Endoh
An Invitation to TRICK: How to write weird Ruby programs
mame
1
650
TypeProf進捗
mame
0
17
12年前の『型システム入門』翻訳の思い出話
mame
14
1.9k
Good first issues of TypeProf
mame
4
6.3k
Revisiting TypeProf - IDE support as a primary feature
mame
1
2.3k
error_highlight: User-friendly Error Diagnostics
mame
0
21
TRICK 2022 Results
mame
0
23
クックパッド春の超絶技巧パンまつり 超絶技巧プログラミング編 資料
mame
0
29
Enjoy Ruby Programming in IDE and TypeProf
mame
0
28
Other Decks in Programming
See All in Programming
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
4
580
最新TCAキャッチアップ
0si43
0
260
React CompilerとFine Grained Reactivityと宣言的UIのこれから / The next chapter of declarative UI
ssssota
7
3.4k
型のインスタンス化は非常に深く、無限である可能性があります。
kimitashoichi
0
140
テスト自動化失敗から再挑戦しチームにオーナーシップを委譲した話/STAC2024 macho
ma_cho29
1
790
似たもの同士のPerlとPHP
uzulla
1
110
イマのCSSでできる インタラクション最前線 + CSS最新情報
clockmaker
5
3.8k
42 best practices for Symfony, a decade later
tucksaun
1
130
複雑な仕様に立ち向かうアーキテクチャ
myohei
0
130
Java 23の概要とJava Web Frameworkの現状 / Java 23 and Java web framework
kishida
2
380
React への依存を最小にするフロントエンド設計
takonda
21
8.9k
CSC509 Lecture 13
javiergs
PRO
0
160
Featured
See All Featured
Embracing the Ebb and Flow
colly
84
4.5k
Mobile First: as difficult as doing things right
swwweet
222
8.9k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Facilitating Awesome Meetings
lara
50
6.1k
Fireside Chat
paigeccino
34
3.1k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
The Pragmatic Product Professional
lauravandoore
32
6.3k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Fashionably flexible responsive web design (full day workshop)
malarkey
405
65k
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.3k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.6k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
191
16k
Transcript
マニアックな Ruby 2.7新機能紹介 遠藤 侑介 鹿児島Ruby会議01 1
この発表では • (基調講演らしく)Ruby開発の最新動向を紹介し、 • Ruby 2.7で導入されたりされなかったりす る新機能や非互換 を • おかしな使い方
興味深いユースケース • あやしい仕様 言語設計における細やかな配慮 • 仕様検討の楽屋裏 新機能導入の背景や開発秘話 • を交えながら紹介していきます • 興味もったら検索→"Ruby Hack Challenge" イベント 2
自己紹介:遠藤侑介 (@mametter) • クックパッドで働く フルタイムRubyコミッタ • テスト、CIの番人 • Ruby3x3ベンチマーク作成 •
Ruby 3の静的解析 • クックパッドに興味あったら お声がけください 3
お品書き • Rubyの最新開発体制の紹介 • Ruby 2.7新機能のマニアックな紹介 • まとめ 4
お品書き • ➔Rubyの最新開発体制紹介 • Ruby 2.7新機能のマニアックな紹介 • まとめ 5
プログラミング言語 Ruby • 日本発のプログラミング言語(1993~) • まつもとゆきひろ氏(matz)が設計・開発している • Ruby on Railsというフレームワークが有名で、
Webアプリの市場を(たぶんまだ) 獲得している • 処理系本体はほぼC言語で書かれている • 最近Rubyで書くところが徐々に増えている • 添付ライブラリはフルRubyのものも多数 • https://github.com/ruby/ruby
Rubyの開発コミュニティ • "Ruby core team" • アクティブな開発者たちを漠然と指す謎ワード • Ruby界隈では開発者を「コミッタ」と呼ぶ •
アクティブな開発者は10~30人程度 • 1回でもコミットしたことのある人の数:約100人 • 直近1年間で100コミット以上:約10人 • 直近1年間で10コミット以上:約20人 • 直近1年間で1コミット以上:約35人
2019年のコミット数グラフ(雰囲気) 8
Ruby開発の日常 • 年1回、クリスマスごろリリースする • 次はRuby 2.7 • バグ報告や新機能の議論はバグトラッカでやる • https://bugs.ruby-lang.org
• 言語の機能提案はmatzが最終決定権を持つ (『優しい独裁者』) • 議論促進のため、月1回程度、開発者会議を行う
お品書き • Rubyの最新開発体制の紹介 • ➔Ruby 2.7新機能のマニアックな紹介 • まとめ 10
パターンマッチ • Ruby 2.7最大の目玉新機能!!! • 関数型プログラミング言語でよくあるやつ 11 ary = [1,
2, 3] case ary in [0, y, z] # マッチしない in [1, y, z] p y #=> 2 p z #=> 3 end
パターンマッチ • 従来からあるcase/when分岐で書くと…… 12 ary = [1, 2, 3] case
ary[0] when 0 # マッチしない when 1 p ary[1] #=> 2 p ary[2] #=> 3 end
パターンマッチ 13 • 何が嬉しいの?
パターンマッチの嬉しさ • 短くてわかりやすい例を示すのが難しい… • jsonを分解するのに便利?(実事例はまだない) • Railsのルーティングの記述に便利?(実事例はまだない) • 赤黒木を実装するのに便利!(実事例ではない) •
型プロファイラを実装するのに便利!(実事例ではない) • パターンマッチは「記号処理」向きの言語機能 • Rubyで記号処理をやる人はあんまいない • (ポジティブに言えば)Rubyの適用範囲が広がる 14
記号処理とは • 数値ではなく記号を扱う処理 • アプリケーションで言えば言語処理系とか数式処理 • 今回は『SKIコンビネータ計算』で解説 15
SKIコンビネータ計算 • 型無しラムダ計算を単純化した計算モデル あるルールで木を繰り返し簡単にしていく遊び 16 K S I K S
K I K
ルール (1) と (2) 17 K x y x I
x x
ルール (3) ※どれにもマッチしないときは左のサブツリーを見る 18 S x y z x z
y z
Rubyの配列で木を表現する 19 x y [x, y] I K [:K, :I]
K S I K S K I K [[[:K,:I],[:S,:K]], [[:K,:S],[:I,:K]]] [[[:K,:I],[:S,:K]],[[:K,:S],[:I,:K]]]
変形ルールをプログラムで書く 20 I x x def ski(e) if e[0] ==
:I e[1] # xの部分 else ... end end [:I, x] x
変形ルールをプログラムで書く 21 K x y x def ski(e) if e[0]
== :I e[1] elsif e[0][0] == :K e[0][1] # xの部分 else ... end end x [[:K, x], y]
変形ルールをプログラムで書く 22 S x y z x z y z
def ski(e) if e[0] == :I e[1] elsif e[0][0] == :K e[0][1] elsif e[0][0][0]==:S [[e[0][0][1], # x e[1]], # z [e[0][1], # y e[1]]] # z else ... end end [[[:S, x], y], z] [[x,z], [y,z]]
23 • ややこしい!
変形ルールをパターンマッチで書く 24 I x x def ski(e) case e in
[:I, x] x else ... end end [:I, x] x
変形ルールをパターンマッチで書く 25 K x y x def ski(e) case e
in [:I, x] x in [[:K, x], y] x else ... end end x [[:K, x], y]
変形ルールをパターンマッチで書く 26 S x y z x z y z
def ski(e) case e in [:I, x] x in [[:K, x], y] x in [[[:S,x],y],z] [[x,z],[y,z]] else ... end end [[[:S, x], y], z] [[x,z], [y,z]]
27 • かんたん!
パターンマッチ:結論 28 • パターンにマッチさせるプログラムを書くときに パターンマッチはとても便利です • 参考文献 • パターンマッチを深く知りたいなら ➔『n月刊ラムダノート
Vol.1 No.3』 • 記号処理に興味を持ったら ➔『RubyでつくるRuby』(自著)
キーワード引数の分離 • Ruby 2.7最大の目玉非互換! • 正確にはRuby 3.0に予定されている非互換 • 前提知識:ハッシュオブジェクト •
キーと値の対応を表すデータ構造 • 他言語ではマップ、辞書とも 29 { a: 1, b: 2 } キー 値 a 1 b 2
キーワード引数の理想と現実 • 理想(Ruby 3.0予定) • 現実(Ruby 2.X) 30 def foo(a,b,c,x:42)
... end foo(1, 2, x: 43) メソッド 呼び出し 普通の引数に1と2を渡してね キーワード引数xは43でお願い あっ、引数cの分が足りないよ! 見直してね 1, 2, {:x=>43}の3引数を渡せ def foo(a,b,c,x:42) ... end a=1、b=2、c={:x=>43}で xは未指定な➔あとでクラッシュ foo(1, 2, x: 43)
Ruby 2におけるキーワード引数 • キーワード引数は、普通の引数の一部 • 一番最後にあるハッシュオブジェクト • どちらも同じ意味 → •
Ruby 2.0設計時はこれでいいと思っていた • しかし直感に合わないというバグ報告が次々と来た • 直感にあわせるためにad-hocな仕様変更が繰り返された • 今では完全なカオスになってしまった 31 foo(x: 43) foo({x: 43})
Ruby 2.6のカオスな挙動の例 • x には何が渡るか? • 答え:何もわたらない(デフォルト式のnilになる) • 理由 32
def foo(x=nil, **y) p x end foo({}, **{}) def foo(x=nil, **kw) p x #=> nil end foo({}, **{}) foo({}) kw={} x=デフォルト は、無と 同じ意味が自然 **{} 最後のハッシュは キーワード引数
Ruby 3におけるキーワード引数 • キーワード引数は、普通の引数とは全くの別物 • 混同が起きなくなったのでめでたしめでたし 33 foo(x: 43) foo({x:
43}) キーワード引数を渡す 普通のハッシュ引数を渡す
…非互換! • 意図的に混同してたコードがRuby 3で動かない 34 def foo(**opt) ... end h
= {x: 43} foo(h) def foo(opt={}) ... end foo(x: 43) このケースは Ruby 3でも 許すことになった これは 許さない と書き直してね foo(**h)
Ruby 2.7は移行支援バージョン • 基本的にはRuby 2.6と同じ意味 • しかし3.0で変わる挙動に警告を出す • www.ruby-lang.orgに移行ガイドを掲載する予定です 35
def foo(a,b,c,opt: 42) end foo(1, 2, opt:43) #=> -:3: warning: The keyword argument is passed as the last hash parameter # -:1: warning: for `foo' defined here
委譲の新記法: (...) • 委譲:引数をすべて別メソッドに横流しすること 36 def bar(a, b, c) end
def foo(...) bar(...) end foo(1, 2, 3) def foo(*args, &blk) bar(*args, &blk) end Ruby 2でまじめに書く場合 def foo(*args, **opts, &blk) bar(*args, **opts, &blk) end Ruby 3でまじめに書く場合
トラップ:(...) はカッコが必須 • 「Rubyはカッコが省略できていいよね~」 • Ruby 2.7では行末に…があったら警告が出ます 37 def foo(...)
bar ... end 残念!endless range と解釈されます (bar...) def foo(...) bar ... end #=> -:2: warning: ... at EOL, should be parenthesized?
numbered parameter • 引数名を付けたくない勢のための福音 (?) 38 ary.map {|x| x.to_s(16) }
ary.map { _1.to_s(16) } 引数を書くのが イヤ!
numbered parameter:背景 • 簡単 (?) な書き方がある • ちょっと複雑になると簡単 (?) にできなかった
• &:to_s を魔拡張する提案が繰り返された 39 ary.map {|x| x.to_s } ary.map(&:to_s) ary.map {|x| x.to_s(16) } × ary.map {|x| JSON.parse(x) } × ary.map { _1.to_s(16) } ary.map { JSON.parse(_1) } ary.map(&:to_s << 16) ary.map(&.to_s(16)) ary.map.as_self{to_s(16)} ary.map(&(:to_s.proc >> :ord.to_proc)) ary.map(&(&:to_s >> &:ord))
numbered parameter: 複数引数の罠 • _2を読み出すだけで_1の意味が変わる • こういう意味になってる 40 h =
{ 1 => 2 } h.map { p _1 } #=> [1, 2] h.map { x = _2; p _1 } #=> 1 h = { 1 => 2 } h.map {|a| p a } #=> [1, 2] h.map {|k, v| p k } #=> 1
numbered parameter: 書けない場所 • 入れ子のブロックでは1回しか書けない 41 1: n.times { 2:
_1.times { 3: _1 4: } 5: } -:3: numbered parameter is already used in -:2: outer block here こういう ややこしいのは 書いてほしくない
numbered parameter: 結論 • 名前を書きましょう 42
余談:決まるまでの長大な議論 • 2019/01: $_, @0,@1,@2, {|x|なのか {|x,|なのか • 2019/02: nobuが@1でパッチ書いてみることに
• 2019/03: @1入りでpreview1リリースの方向 • 2019/04: eregonがブロック引数の利用例統計 • 2019/06: @, @0, it, $it, ¥it, _, 複引数必要か、{|x,| • 2019/07: it, _, %0, @, @1,@2, %1,%2, :1, :2 • 2019/09: _0 / _1,_2,_3の方向で固まる • 2019/10: _0 が消える • 開発者会議の議事録は公開されています(バグトラッカから) 43
令和対応 • Ruby 2.7は令和に対応 • 正確にはRuby 2.6.3から • 言語処理系が令和に対応するとは?? 44
令和対応:Date.jisx0301 • 2019/04/01 令和発表 • 2019/04/17 Ruby 2.6.3リリース(投機実行) • 2019/05/01
令和施行 • 2019/05/20 JIS X 0301改正 45 $ ruby -rdate -e 'puts Date.new(2019, 4, 30).jisx0301' H31.04.30 $ ruby -rdate -e 'puts Date.new(2019, 5, 1).jisx0301' R01.05.01
令和対応:Unicode ¥u32FF (㋿) • 2019/04/01 令和発表 • 2019/04/?? Unicode 12.1.0
beta • 2019/04/17 Ruby 2.6.3リリース(beta採用) • 2019/05/01 令和施行 • 2019/05/07 Unicode 12.1.0リリース 46 $ ruby -e 'puts "㍻".unicode_normalize(:nfkd)' 平成 $ ruby -e 'puts "㋿".unicode_normalize(:nfkd)' 令和
入らない機能:pipeline operator • Elixirから輸入する予定だった演算子 47 |>
pipeline operatorの意味 • F#での意味 • Elixirでの意味 • Rubyで導入予定だった意味 48 x
|> foo(1) foo(x, 1) = x |> foo(1) x.foo(1) = x |> foo 1 foo 1 x =
pipeline operatorの本来の目的 • 関数名を処理順に書けるようにすること • F# • Elixir • 非オブジェクト指向言語でメソッドチェーンっぽく
書くためのハックだった • Rubyではメソッド呼び出しの別記法とするのは自然 49 x |> foo 1 |> bar 2 bar 2 (foo 1 x) = bar(foo(x, 1), 2) x |> foo(1) |> bar(2) =
pipeline operatorの「誤解」 • 本来の目的が忘れられ、 「xをfoo(1)の第1引数にする構文」と思う人多数 • Rubyの意味はそれと違うので、 「思ってたんと違う!」という苦情が殺到した • 結果、取りやめになった
50 x |> foo(1) foo(x, 1) =
Rubyが狙っていたこと • Range#eachのカッコを省略したかった • メソッドチェーンに コメントを書きたかった • プログラミング言語は 見た目が9割! 51
x # fooをやる |> foo 1 # barをやる |> bar 2 1..10 |> each {|x| ... } x # fooをやる .foo 1 # barをやる .bar 2 Ruby 2.7では これが書ける
入らない機能:メソッド取出演算子 52 .:
メソッド取出演算子の意味 • メソッドオブジェクトを取り出す • と同じ意味 53 "str".method(:upcase) m = "str".:upcase
#=> #<Method: String#upcase> m.call #=> "STR"
LISP-1とLISP-2 • Python (LISP-1) • 括弧無→メソッド取出 • 括弧付→メソッド呼出 • 一貫している
•Ruby (LISP-2) • 括弧ありでも無しでも メソッド呼び出し • メソッド取出が面倒 • 取出してから呼出も面倒 54 "str".upper() #=> "STR" "str".upper #=> <built-in method upper of str object at…> "str".upcase #=> "STR" "str".method(:upcase) #=> #<Method: String#upcase> "str".:upcase.call "str".:upcase #=> #<Method: String#upcase>
ユースケース • methodメソッドが再定義されても大丈夫 • デバッグに便利 • 関数型プログラミング? 55 p obj.:foo.source_location
#=> [ファイル名, 行番号] [json1, json2].map(&JSON.:parse) [json1, json2].map {|x| JSON.parse(x) }
消された理由 • 「関数型プログラミング?」の用途には不完全 • ↓のケースは .: で解決しない • 今後も &:to_s
を魔拡張しつづけるのか……? • Rubyの関数型プログラミングのgrand planを (誰かが)考えてから再挑戦することに 56 [json1, json2].map {|x| JSON.parse(x, symbolize_names: true) }
2.7は他にも新機能や改善 や非互換 がたくさん • IRBの刷新 • $SAFE消滅 • filter_map •
Enumerable#tally • GC.compact • Time#floor, #ceil 57 • beginless range • self.private_method • Array#intersection • Comparable#clamp with range • CESU-8 • Enumerator.produce • Enumerator::Lazy#eager • Enumerator::Yielder#to_proc • Fiber#raise • FrozenError#receiver • IO#set_encoding_by_bom • Integer#[] with range • Method#inspect • Module#const_source_location • 一部のto_sがfrozen • ObjectSpace::WeakMap#[]= • Regexp#match?(nil) • RubyVM.resolve_feature_pathの移動 • UnboundMethod#bind_call • Bundler更新 • CGI.escapeHTML高速化 • CSV更新 • Net::FTP改良 • Net::IMAP改良 • open-uri改良 • OptionParser did_you_mean • Racc 更新 • REXML更新 • RSS更新 • RubyGems更新 • StringScanner更新 • 一部の標準ライブラリがgem化 • ほか クックパッド開発者ブログで網羅解説予定 w/ ko1
お品書き • Rubyの最新開発体制の紹介 • Ruby 2.7新機能のマニアックな紹介 • ➔まとめ 58
まとめ • Ruby 2.7にご期待ください • 数多くの新機能や改良 • Ruby 3を見据えた準備 •
意外といろいろ考えてやってます • あなたもRuby開発に参加できます • メーリングリストやバグトラッカをウォッチ • もしくは、検索:"Ruby Hack Challenge" 59