Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
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
0
290
TypeProf進捗
mame
0
16
12年前の『型システム入門』翻訳の思い出話
mame
14
1.9k
Good first issues of TypeProf
mame
4
6.1k
Revisiting TypeProf - IDE support as a primary feature
mame
1
2.2k
error_highlight: User-friendly Error Diagnostics
mame
0
19
TRICK 2022 Results
mame
0
18
クックパッド春の超絶技巧パンまつり 超絶技巧プログラミング編 資料
mame
0
25
Enjoy Ruby Programming in IDE and TypeProf
mame
0
25
Other Decks in Programming
See All in Programming
WebフロントエンドにおけるGraphQL(あるいはバックエンドのAPI)との向き合い方 / #241106_plk_frontend
izumin5210
4
1.4k
Arm移行タイムアタック
qnighy
0
330
Click-free releases & the making of a CLI app
oheyadam
2
120
よくできたテンプレート言語として TypeScript + JSX を利用する試み / Using TypeScript + JSX outside of Web Frontend #TSKaigiKansai
izumin5210
6
1.7k
見せてあげますよ、「本物のLaravel批判」ってやつを。
77web
7
7.8k
Remix on Hono on Cloudflare Workers
yusukebe
1
290
Figma Dev Modeで変わる!Flutterの開発体験
watanave
0
130
NSOutlineView何もわからん:( 前編 / I Don't Understand About NSOutlineView :( Pt. 1
usagimaru
0
340
OSSで起業してもうすぐ10年 / Open Source Conference 2024 Shimane
furukawayasuto
0
110
Generative AI Use Cases JP (略称:GenU)奮闘記
hideg
1
290
アジャイルを支えるテストアーキテクチャ設計/Test Architecting for Agile
goyoki
9
3.3k
RubyLSPのマルチバイト文字対応
notfounds
0
120
Featured
See All Featured
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
Documentation Writing (for coders)
carmenintech
65
4.4k
Measuring & Analyzing Core Web Vitals
bluesmoon
4
120
Embracing the Ebb and Flow
colly
84
4.5k
Site-Speed That Sticks
csswizardry
0
26
GraphQLとの向き合い方2022年版
quramy
43
13k
Fashionably flexible responsive web design (full day workshop)
malarkey
405
65k
A better future with KSS
kneath
238
17k
What's in a price? How to price your products and services
michaelherold
243
12k
YesSQL, Process and Tooling at Scale
rocio
169
14k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
0
96
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
250
21k
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