Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

マニアックなRuby 2.7新機能紹介

1 587 vues

Publié le

鹿児島Ruby会議01
https://k-ruby.github.io/kagoshima-rubykaigi01/

Publié dans : Technologie
  • Identifiez-vous pour voir les commentaires

マニアックなRuby 2.7新機能紹介

  1. 1. マニアックな Ruby 2.7新機能紹介 遠藤 侑介 鹿児島Ruby会議01 1
  2. 2. この発表では • (基調講演らしく)Ruby開発の最新動向を紹介し、 • Ruby 2.7で導入されたりされなかったりする新機能や非互換を • おかしな使い方 興味深いユースケース • あやしい仕様 言語設計における細やかな配慮 • 仕様検討の楽屋裏 新機能導入の背景や開発秘話 • を交えながら紹介していきます • 興味もったら検索→"Ruby Hack Challenge" イベント 2
  3. 3. 自己紹介:遠藤侑介 (@mametter) • クックパッドで働く フルタイムRubyコミッタ • テスト、CIの番人 • Ruby3x3ベンチマーク作成 • Ruby 3の静的解析 • クックパッドに興味あったら お声がけください 3
  4. 4. お品書き • Rubyの最新開発体制の紹介 • Ruby 2.7新機能のマニアックな紹介 • まとめ 4
  5. 5. お品書き • ➔Rubyの最新開発体制紹介 • Ruby 2.7新機能のマニアックな紹介 • まとめ 5
  6. 6. プログラミング言語 Ruby • 日本発のプログラミング言語(1993~) • まつもとゆきひろ氏(matz)が設計・開発している • Ruby on Railsというフレームワークが有名で、 Webアプリの市場を(たぶんまだ)獲得している • 処理系本体はほぼC言語で書かれている • 最近Rubyで書くところが徐々に増えている • 添付ライブラリはフルRubyのものも多数 • https://github.com/ruby/ruby
  7. 7. Rubyの開発コミュニティ • "Ruby core team" • アクティブな開発者たちを漠然と指す謎ワード • Ruby界隈では開発者を「コミッタ」と呼ぶ • アクティブな開発者は10~30人程度 • 1回でもコミットしたことのある人の数:約100人 • 直近1年間で100コミット以上:約10人 • 直近1年間で10コミット以上:約20人 • 直近1年間で1コミット以上:約35人
  8. 8. 2019年のコミット数グラフ(雰囲気) 8
  9. 9. Ruby開発の日常 • 年1回、クリスマスごろリリースする • 次はRuby 2.7 • バグ報告や新機能の議論はバグトラッカでやる • https://bugs.ruby-lang.org • 言語の機能提案はmatzが最終決定権を持つ (『優しい独裁者』) • 議論促進のため、月1回程度、開発者会議を行う
  10. 10. お品書き • Rubyの最新開発体制の紹介 • ➔Ruby 2.7新機能のマニアックな紹介 • まとめ 10
  11. 11. パターンマッチ • 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
  12. 12. パターンマッチ • 従来からあるcase/when分岐で書くと…… 12 ary = [1, 2, 3] case ary[0] when 0 # マッチしない when 1 p ary[1] #=> 2 p ary[2] #=> 3 end
  13. 13. パターンマッチ 13 • 何が嬉しいの?
  14. 14. パターンマッチの嬉しさ • 短くてわかりやすい例を示すのが難しい… • jsonを分解するのに便利?(実事例はまだない) • Railsのルーティングの記述に便利?(実事例はまだない) • 赤黒木を実装するのに便利!(実事例ではない) • 型プロファイラを実装するのに便利!(実事例ではない) • パターンマッチは「記号処理」向きの言語機能 • Rubyで記号処理をやる人はあんまいない • (ポジティブに言えば)Rubyの適用範囲が広がる 14
  15. 15. 記号処理とは • 数値ではなく記号を扱う処理 • アプリケーションで言えば言語処理系とか数式処理 • 今回は『SKIコンビネータ計算』で解説 15
  16. 16. SKIコンビネータ計算 • 型無しラムダ計算を単純化した計算モデル あるルールで木を繰り返し簡単にしていく遊び 16 K SIK S K I K
  17. 17. ルール (1) と (2) 17 K x y x I x x
  18. 18. ルール (3) ※どれにもマッチしないときは左のサブツリーを見る 18 S x y z x z y z
  19. 19. Rubyの配列で木を表現する 19 x y [x, y] IK [:K, :I] K SIK S K I K [[[:K,:I],[:S,:K]], [[:K,:S],[:I,:K]]][[[:K,:I],[:S,:K]],[[:K,:S],[:I,:K]]]
  20. 20. 変形ルールをプログラムで書く 20 I x x def ski(e) if e[0] == :I e[1] # xの部分 else ... end end [:I, x] x
  21. 21. 変形ルールをプログラムで書く 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 endx [[:K, x], y]
  22. 22. 変形ルールをプログラムで書く 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. 23. 23 • ややこしい!
  24. 24. 変形ルールをパターンマッチで書く 24 I x x def ski(e) case e in [:I, x] x else ... end end [:I, x] x
  25. 25. 変形ルールをパターンマッチで書く 25 K x y x def ski(e) case e in [:I, x] x in [[:K, x], y] x else ... end endx [[:K, x], y]
  26. 26. 変形ルールをパターンマッチで書く 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. 27. 27 • かんたん!
  28. 28. パターンマッチ:結論 28 • パターンにマッチさせるプログラムを書くときに パターンマッチはとても便利です • 参考文献 • パターンマッチを深く知りたいなら ➔『n月刊ラムダノート Vol.1 No.3』 • 記号処理に興味を持ったら ➔『RubyでつくるRuby』(自著)
  29. 29. キーワード引数の分離 • Ruby 2.7最大の目玉非互換! • 正確にはRuby 3.0に予定されている非互換 • 前提知識:ハッシュオブジェクト • キーと値の対応を表すデータ構造 • 他言語ではマップ、辞書とも 29 { a: 1, b: 2 } キー 値 a 1 b 2
  30. 30. キーワード引数の理想と現実 • 理想(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)
  31. 31. Ruby 2におけるキーワード引数 • キーワード引数は、普通の引数の一部 • 一番最後にあるハッシュオブジェクト • どちらも同じ意味 → • Ruby 2.0設計時はこれでいいと思っていた • しかし直感に合わないというバグ報告が次々と来た • 直感にあわせるためにad-hocな仕様変更が繰り返された • 今では完全なカオスになってしまった 31 foo(x: 43) foo({x: 43})
  32. 32. 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=デフォルト は、無と 同じ意味が自然 **{} 最後のハッシュは キーワード引数
  33. 33. Ruby 3におけるキーワード引数 • キーワード引数は、普通の引数とは全くの別物 • 混同が起きなくなったのでめでたしめでたし 33 foo(x: 43) foo({x: 43}) キーワード引数を渡す 普通のハッシュ引数を渡す
  34. 34. …非互換! • 意図的に混同してたコードがRuby 3で動かない 34 def foo(**opt) ... end h = {x: 43} foo(h) def foo(opt={}) ... end foo(x: 43) このケースは Ruby 3でも 許すことになった これは 許さない と書き直してね foo(**h)
  35. 35. 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. 36. 委譲の新記法: (...) • 委譲:引数をすべて別メソッドに横流しすること 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でまじめに書く場合
  37. 37. トラップ:(...) はカッコが必須 • 「Rubyはカッコが省略できていいよね~」 • Ruby 2.7では行末に…があったら警告が出ます 37 def foo(...) bar ... end 残念!endless range と解釈されます (bar...) def foo(...) bar ... end #=> -:2: warning: ... at EOL, should be parenthesized?
  38. 38. numbered parameter • 引数名を付けたくない勢のための福音 (?) 38 ary.map {|x| x.to_s(16) } ary.map { _1.to_s(16) } 引数を書くのが イヤ!
  39. 39. 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))
  40. 40. 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
  41. 41. 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 こういう ややこしいのは 書いてほしくない
  42. 42. numbered parameter: 結論 • 名前を書きましょう 42
  43. 43. 余談:決まるまでの長大な議論 • 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
  44. 44. 令和対応 • Ruby 2.7は令和に対応 • 正確にはRuby 2.6.3から • 言語処理系が令和に対応するとは?? 44
  45. 45. 令和対応: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
  46. 46. 令和対応: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)' 令和
  47. 47. 入らない機能:pipeline operator • Elixirから輸入する予定だった演算子 47 |>
  48. 48. pipeline operatorの意味 • F#での意味 • Elixirでの意味 • Rubyで導入予定だった意味 48 x |> foo(1) foo(x, 1)= x |> foo(1) x.foo(1)= x |> foo 1 foo 1 x=
  49. 49. 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) =
  50. 50. pipeline operatorの「誤解」 • 本来の目的が忘れられ、 「xをfoo(1)の第1引数にする構文」と思う人多数 • Rubyの意味はそれと違うので、 「思ってたんと違う!」という苦情が殺到した • 結果、取りやめになった 50 x |> foo(1) foo(x, 1)=
  51. 51. 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. 52. 入らない機能:メソッド取出演算子 52 .:
  53. 53. メソッド取出演算子の意味 • メソッドオブジェクトを取り出す • と同じ意味 53 "str".method(:upcase) m = "str".:upcase #=> #<Method: String#upcase> m.call #=> "STR"
  54. 54. 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>
  55. 55. ユースケース • methodメソッドが再定義されても大丈夫 • デバッグに便利 • 関数型プログラミング? 55 p obj.:foo.source_location #=> [ファイル名, 行番号] [json1, json2].map(&JSON.:parse) [json1, json2].map {|x| JSON.parse(x) }
  56. 56. 消された理由 • 「関数型プログラミング?」の用途には不完全 • ↓のケースは .: で解決しない • 今後も &:to_s を魔拡張しつづけるのか……? • Rubyの関数型プログラミングのgrand planを (誰かが)考えてから再挑戦することに 56 [json1, json2].map {|x| JSON.parse(x, symbolize_names: true) }
  57. 57. 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
  58. 58. お品書き • Rubyの最新開発体制の紹介 • Ruby 2.7新機能のマニアックな紹介 • ➔まとめ 58
  59. 59. まとめ • Ruby 2.7にご期待ください • 数多くの新機能や改良 • Ruby 3を見据えた準備 • 意外といろいろ考えてやってます • あなたもRuby開発に参加できます • メーリングリストやバグトラッカをウォッチ • もしくは、検索:"Ruby Hack Challenge" 59

×