Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Ruby 3のキーワード引数について考える

2,244 views

Published on

大江戸Ruby会議07

Published in: Technology
  • Be the first to comment

Ruby 3のキーワード引数について考える

  1. 1. Ruby 3のキーワード引数 について考える The Ruby Team 2018/09/15 (Sat.) 大江戸Ruby会議7 1
  2. 2. 今日の議題 • Ruby 2のキーワード引数の問題 • Ruby 3に向けた改善の提案 2
  3. 3. 『キーワード拡張』 • メソッドに新たにキーワード引数を持たせること • 『キーワード拡張』は常に安全か? – もともとあった呼び出しは元の通りに動いてほしい def foo(...) end foo(...) def foo(..., extension: false) end foo(...) foo(..., extension: true) 3
  4. 4. キーワード拡張は安全でない def foo(*args) p args end foo(1, 2, 3) #=> [1, 2, 3] foo(k: 42) #=> [{:k=>42}] 4
  5. 5. キーワード拡張は安全でない def foo(*args, extension: false) p args end foo(1, 2, 3) #=> [1, 2, 3] foo(k: 42) #=> unknown key: k 5
  6. 6. 問題:キーワード拡張が安全でない • rest引数をとるメソッドのキーワード拡張は危険 – 既存コードが動かなくなるリスクがある – optional引数も同様にダメ • バグ報告が多数来ている – #8316,#11967,#12104,#12717,#12821,#13336,#13647,#14130 • 実際にAPI拡張で困っている – Thread.new(stack_size: 100000)とか – Struct.new(keyword_init: true)とか 6
  7. 7. 問題の原因 • キーワードとハッシュを自動変換していること def foo(h) p h #=>{:k=>42} end foo(k: 42) def foo(k: 1) p k #=> 42 end foo({ k: 42 }) キーワード➔ハッシュ ハッシュ➔キーワード 7
  8. 8. Ruby 3での解決提案 • キーワードハッシュの変換をやめる #14183 – キーワードかハッシュか、明示してください 注意:Ruby 3の決定事項ではない def foo(h) p h #=>{:k=>42} end foo(k: 42) def foo(k: 1) p k #=> 42 end foo({ k: 42 }) def foo(h) p h[:k] #=> 42 end foo({ k: 42 }) def foo(h) p h #=>{:k=>42} end foo({ k: 42 }) def foo(**h) p h #=>{:k=>42} end foo(k: 42) def foo(k: 1) p k #=> 42 end foo(**{ k: 42 }) 8
  9. 9. 互換性の問題 • 修正箇所は少なくないが、修正は簡単 – だいたいは、**をつけるだけ • 一部、むずかしいケースがある 9
  10. 10. 互換性問題1:既存API • キーワードとハッシュのどちらも許すAPI • こういうAPIをRuby 3で定義するには? – 修正方法:両方受け取ってマージしてください erb.result_with_hash(k: 1) erb.result_with_hash(hash) def result_with_hash(h1={}, *h2) h = h1.merge(h2) ... end 10
  11. 11. 互換性問題2:委譲 • 引数を丸投げするコード • こういうコードをRuby 3で書くには? – 修正方法:**kw も委譲してください def forward(*args, &blk) target(*args, &blk) end def forward(*args, **kw, &blk) target(*args, **kw, &blk) end 11
  12. 12. 議題 • 議題0:問題と提案に対するお気持ちは? • 議題1:foo(:key=>1)はキーワード? • 議題1':foo("str"=>1, key:2)は? • 議題2:既存APIのための記法? • 議題3:委譲のための記法? 12
  13. 13. 議題1 • Ruby 3で、以下はキーワード?ハッシュ? – 案1:キーワードにする?(Ruby 2と互換) – 案2:=>だったら常にハッシュにする?(非互換) – 案3:文法エラー?(=>は{}を書かないとダメ) foo(:key=>1) 13
  14. 14. 議題1' • Ruby 3で、以下はキーワード?ハッシュ? – 案1:キーの種類で自動分割する? • foo({"str"=>1}, key:2) – 案2:非Symbolがあったら全部ハッシュ? • foo({"str"=>1, key:2}) – 案3:文法エラーにする?(混ぜるな危険) • 参考:こういうAPIは実在する(Kernel#spawn) foo("str"=>1, key:2) 14
  15. 15. 議題1'' • Ruby 3で、以下はキーワード?ハッシュ? – 案1:キーの種類で自動分割する? • foo({"s"=>2,"ss"=>4}, k:1, kk:3) – 案2:非Symbolがあったら全部ハッシュ? • foo({k:1, "s"=>2, kk:3, "ss"=>4}) – 案3:文法エラーにする?(混ぜるな危険) foo(k:1, "s"=>2, kk:3, "ss"=>4) 15
  16. 16. 議題2 • Ruby 2のように動くメソッドを簡単に定義したい – 案1:諦めてもらう • 手動マージしてください – 案2:互換定義用のAPIを提供する def result_with_hash(***h); end ruby2 def result_with_hash(h); end define_ruby2_method(:result_with_hash){} 16
  17. 17. 議題3 • Ruby 2.6 と Ruby 3 の両方でうごく委譲は? – 委譲専用の記法を導入する? #3447 def forward(...) target(...) end 17
  18. 18. 議題3' • Ruby 2.6 と Ruby 3 の両方でうごく委譲は? – ↓は Ruby2.6で若干問題がある – forward() が target(**{}) を呼ぶ • target()とtarget(**{})は微妙に意味が違う • 実用上は問題ない? def forward(*args, **kw, &blk) target(*args, **kw, &blk) end 18
  19. 19. 議題3'' • Ruby 2.5 は **{} がバグっている #15052 • 2.6で、どう直す? def foo(opt=42) p opt end foo(**{}) #=> 42 h={} foo(**h) #=> {} 19
  20. 20. 議題3''' • Ruby 2.6で、以下の意味は? foo(**{}) def foo(opt=42,**kw) p [opt, kw] end foo({}, **{}) #foo({})と同じ #=> [42, {}] def foo(opt=42) p opt end foo(**{}) #=> {} 案1:「無」とみなす? 案2:空ハッシュを渡す? 案3:いい感じに(難しい) 20

×