Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What a cool Ruby-2.7 is !

What a cool Ruby-2.7 is !

大阪Ruby会議02 発表スライド

Tomohiro Hashidate

September 15, 2019
Tweet

More Decks by Tomohiro Hashidate

Other Decks in Programming

Transcript

  1. Usage foo = Foo.new promise1 = foo.:slowly_foo.async(sleep_time: 0.1) promise1.value #

    => wait and return value pr = foo.:with_args.partial(0.1, b: _any) foo.call(b: 3) # => call foo.with_args(0.1, b: 3) [1, 2, 3].each do |i| foo.:exec_later.defer(i) # => resolve arguments here p 1 # => call exec_later(i) end foo.:heavy.memoize(10) # => save result on foo instance_variable
  2. async check_arity(*args) Concurrent::Promises.future_on(:io, *args) do |*args| call(*args, &block) end concurrent-ruby

    をwrap してるだけで簡単、と思いきや引数のチェックがめんどい。 引数間違いとか実⾏時に即指摘したいが、Ruby の引数チェックはC の実装の中に組込 まれていて再利⽤できない。 特に引数の定義に合わせて適切なメッセージでArgumentError 出すのが…… 。 ( このチェック処理をRuby 側にexport して欲しい気がする。)
  3. check_arity こういう構造化情報でパターンが⼀杯あるケースはパターンマッチがめっちゃ便利。 parameters.each do |pr| case pr in [:req, _]

    req_size += 1 in [:opt, _] opt_size += 1 in [:rest, _] has_rest = true in [:keyreq, name] has_kw = true keyreqs << name in [:key, _] has_kw = true in [:keyrest, _] has_kw = true else end end マッチングと抽出が同時に出来るのが⼤事。
  4. partial lambda でラップしてPlaceholder オブジェクトに置き換えておいて、 call した時に実際に値を嵌め込み直して呼ぶ。 def partial(*args, **kw, &block)

    ->(*args2, **kw2, &block2) do placeholder_idx = 0 new_args = args.each_with_object([]) do |a, arr| if a.is_a?(MethodPlus::Placeholder) if (args2.size - 1) >= placeholder_idx arr << args2[placeholder_idx].tap { placeholder_idx += 1 }; end else arr << a; end; end # ... new_block = block2 || block call(*new_args, **new_kw, &new_block) end end
  5. defer TracePoint です。( いつもの) ブロック呼び出し毎にstacklevel を記録して、合致する時にmethod をcall する TracePoint を動かす。

    かなり単純化するとこんな感じ。 stack_level = 0 trace = TracePoint.new(*events) do |tp| if tp.event == :b_call stack_level += 1; next; end if tp.event == :b_return && stack_level > 0 stack_level -= 1; next; end tp.disable call(*args, &block) end trace.enable(target: iseq) # => important 実際は、もうちょっと⼯夫が要る。
  6. TracePoint をターゲット指定する時の課題 今処理中のブロックのiseq を取得する⽅法が、ほとんど無い。 (1..10).each do |i| # ここで、このdo-end 内のiseq

    を取るのが困難 end それが取れれば、iseq が持ってる情報が⾊々使えたり、b_return フックで使い易くなる のだが…… 。 ⼀応、抜け道は( ⾃分の知る限り) ⼀つだけある。 ちなみに、メソッドは⾃⾝の処理中に method(:__callee__) でMethod が取れる。
  7. DebugInspector CRuby に組込みのAPI だがRuby 側から触れるAPI が無いのでラッパーgem を利⽤する。 # numbered parameter

    is Good! iseq = RubyVM::DebugInspector.open { @1.frame_iseq(2) } これを利⽤してスタックを遡ってiseq を取得することができる。 このiseq を使うことで、処理中のブロックを抜けた時だけ発動するTracePoint を仕込む ことができる。 ちなみにこのgem のREADME にはこう書いてある。 do not use this library outside of debugging situations.