Text
Page: 1
PicoRubyに於ける
Refinementsの再解釈
橘羽角前下総守情操指南匠均之助
Page: 3
🔥
Refinementsこそ
戦乱の火種
Page: 4
そもそもRefinementsとは?
Page: 5
こんなモンキーパッチは影響範囲が広すぎる
module Kernel
def puts(*args)
$stdout.puts(*args.map{"#{it}でござる!"})
end
end
puts "Hey!", "You!"
#=>
# Hey!でござる!
# You!でござる!
Page: 6
モンキーパッチを局所化する
module Gozaru
refine Kernel do
def puts(*args)
super(*args.map{"#{it}でござる!"})
end
end
end
using Gozaru
puts "Hey!", "You!"
#=>
# Hey!でござる!
# You!でござる!
Page: 7
Refinementsの兵具
Module#refine(klass) { } -> Module
ブロックが表現する機能を現在のモジュールに追加(拡張)
Module#using(module) -> self
拡張モジュールを現在のクラス、モジュールで有効にする
class Refinement
refine ブロックの中の self のクラス。つまり黒魔術
Page: 8
Refineされる範囲
using以降に有効 そして 別のファイルには無効
puts "Hey!", "You!"
using Gozaru
puts "Hey!", "You!"
#=>
# Hey!
# You!
# Hey!でござる
# You!でござる
Page: 10
RubyとはBasicObjectから乾坤開闢するもの
class Object < BasicObject
include Kernel
end
Object.new #=> オブジェクト!
ファイルスコープ?なにそれ?
ローカル変数とかいう田舎侍の所領を安堵するための不承不
承の和睦では?(暴言)
Page: 11
「見える範囲」みたいな足軽ぽいやつじゃなくて
Page: 13
ブロックの中で暗躍するみたいな?
Page: 14
Rubyの命はブロック!
(唐突ですが真理)
Page: 17
道半ばにて潰えし夢
るびま「Refinementsとは何だったのか」
https://magazine.rubyist.net/articles/
0041/0041-200Special-refinement.html
amatsuda/activerecord-refinements
User.where { :name == 'matz' }
Page: 18
覇業なお一縷の望みを留む
Feature: Proc#using
https://bugs.ruby-lang.org/issues/16461 👍押してくれ
module IntegerDivExt
refine Integer do
def /(other)
quo(other)
end
end
end
def instance_eval_with_integer_div_ext(obj, &block)
block.using(IntegerDivExt)
obj.instance_eval(&block)
end
using Proc::Refinements
p 1 / 2 #=> 0
instance_eval_with_integer_div_ext(1) do
p self / 2 #=> (1/2)
end
p 1 / 2 #=> 0
Page: 19
人間五十年
大望を遂ぐるに足らず
Page: 20
我PicoRubyのTask#usingを所望す
PicoRubyのTaskクラスとは
“プリエンプティブ”なタスクスケジューラ
外形上はCRubyのThreadクラスに似ています(後述)
スケジューラが強制的にタスク制御を移す(yieldする)
Fiberが「自分もういいんで他の方どうぞ」と内発的にyieldするのと対照的
これをRefineしたい!したいしたい!やるぞ!
自分で好き勝手できるRuby処理系があるのって最高!
おすすめ
Page: 21
畢竟斯くの如し
task = Task.new do
using Gozaru
puts "Refineしたタスク"
end
puts "別のタスク"
task.join
#=>
# 別のタスク
# Refineしたタスクでござる!
Page: 22
そも、何ゆえ欲する
パイプラインってのがありますね
$> cat slide.md | head -1
# PicoRubyに於ける
パイプとは
OSが提供するプロセス間通信
pipe(2)システムコールが無名パイプをつくる
標準ストリームをつなぎかえる
Page: 23
そも、何ゆえ欲する
R2P2でもパイプやりたい!でもOSがない!
RubyはUNIXのラッパー(極論)だけど、
PicoRubyはベアメタル
…ここでちょっと脱
🛤️します…
Page: 24
追憶…二〇一四年夏(たぶん)
我、出雲国へ国替えして間もなき頃
まだRuby初心者でした
「高度Ruby人材養成講座」たしかそんな名前
テーマは「Thread」
講師は世界のshugomaeda!!!(とnari3)
この人に一生ついて行こうと思った
Page: 25
CRubyのThreadなる勇み足(個人の感想)
Ruby 1.8までのThreadはグリーンスレッド
PicoRubyのTaskと似たやつ。じつはよく知らんけど
Ruby 1.9あたりでpthread(OSネイティブスレッ
ド)のラッパーになった
名馬なれど御し難し
JavaScriptがシングルスレッド上のイベントルー
プであれだけの性能を叩き出しているのに……
Page: 26
CRubyのThreadなる勇み足(個人の感想)
この禍根を晴らすのがFiber::Schedulerなのかな?
てきとうに言ってます
て言うかCRubyチャン、Threadはなかったことにして
PicoRubyからTaskを逆輸入しませんか?
……いや冗談です我々にはRactorがある
Page: 27
我が流儀にて志を継ぐ
PicoRuby.WASMもTaskスケジューラで動きます
コールバックの通信に Task::Queue を活用
…この続きはKaigi on
🛤️にて(ほんとに?)
Task::Queue is 何?
Page: 29
TaskローカルRefinementsの兵具揃え
Task … 去年mruby/mrubyにマージ
(´-`).。oO(CRubyに存在しないトップレベルなコアクラスが
mrubyにマージされるの珍しくない?)
Task::Queue … つい最近mruby/mrubyにマージ
task-refinementsの実装 … 我がPCのローカルに
Page: 30
Task::Queue は Thread::Queue の鏡
q = Task::Queue.new # 最初は空っぽ
task = Task.new do
puts q.pop # popできるまでタスクがsuspend
end
puts "沙汰を待て"
# この間いろいろ忙しい
q.push "蟄居申し渡す"
task.join
#=>
# 沙汰を待て
# 蟄居申し渡す
Page: 31
例: R2P2のcatコマンド実装(簡略版)
※R2P2はマイコンで動くシェルシステムです。もちろんPure PicoRuby
if ARGV.empty?
while line = gets
# 標準入力から読む
print line
# 標準出力へ書く
end
else
ARGV.each |filename|
if File.file?(filename)
File.open(filename, 'r') do |f|
while line = f.gets # ファイルから読む
print line
# 標準出力へ書く
end
end
else
$stderr.puts "#{filename}: No such file"
end
end
end
Page: 32
此度の策の肝要
R2P2コマンド実装では、単に puts と書きたい
パイプされるかどうかはコマンドにはわからない
CRubyでもそのまま動くと開発が楽だし
R2P2コマンドは、すでに個別のTaskなんです
タスク内で Kernel#puts をRefineすればいい
パイプIO通信にTask::Queueを使うとよさそう
Page: 34
技の仔細 : Core構造体
/* include/mruby.h */
struct mrb_context {
...
struct mrb_refinement_chain *refinements; /* NULL until first #using */
};
struct mrb_cache_entry {
...
struct mrb_context *ctx;
...
}
struct mrb_state {
...
mrb_refinement_lookup_fn refinement_lookup; /* NULL until gem init */
};
/* Returns non-zero if a refined method was found for (c, mid). */
typedef int (*mrb_refinement_lookup_fn)(struct mrb_state *mrb, struct RClass *c,
mrb_sym mid, struct RClass **cp,
mrb_method_t *m);
Page: 35
技の仔細 : Refinement構造体
/* mrbgems/mruby-task/include/mruby/refinements.h */
struct mrb_refinement {
struct RClass *target_class; /* class being refined
struct RClass *methods;
/* RClass flagged REFINEMENT
struct RClass *owner;
/* module that called #refine
};
*/
*/
*/
/*
* Per-task singly-linked list of active refinements.
* Nodes are owned by the gem; the mrb_refinement pointers are NOT owned
* (they stay alive via the owner module's instance variable).
*/
struct mrb_refinement_chain {
struct mrb_refinement *ref;
struct mrb_refinement_chain *next;
};
Page: 36
技の仔細 : メソッド探索
/* src/class.c */
mrb_method_t
mrb_vm_find_method(mrb_state *mrb, struct RClass *c,
struct RClass **cp, mrb_sym mid)
{
// メソッドキャッシュをごにょごにょ
}
// Refineされているならmrb_refinement_chainをたどって
// メソッドを探す
Page: 37
技の仔細 : 標準入出力をRefine
module PipeOUT # 別途PipeINもあります
refine Kernel do
def puts(*args)
PipelineIO.puts(Task.current, *args)
#
^^^^^^^^^^^^ 呼び出し元コンテキスト
end
def print(*args)
PipelineIO.print(Task.current, *args)
#
^^^^^^^^^^^^ 呼び出し元コンテキスト
end
end
end
Page: 38
技の仔細 : パイプライン実装
class PipelineIO
# タスクごとにTask::Queueを開く(outboxとinbox)
# 例: $> cat slide.md | head -1
#
#
#
catがproducerで、headがconsumerというパタン
catのoutboxとheadのinboxがじつは「同じもの」
catがoutbox.pushすると、headがinbox.popで受け取る
# ClosedキューにpushするとTask::Error
👇 ここがかっこいい
# * `head -1`は1行読んだら店仕舞したい。つまり先にconsumerが終了
# * inbox.closeしちゃう。そこへのpushはTask::Errorになる
# * これをSIGPIPEとして扱い、producer(catコマンド)も早期終了
# * (タスクスイッチのタイミング次第で即時終了できないこともある)
end
Page: 39
勘所
catコマンド、headコマンドは「標準入出力」に
読み書きしているだけ
R2P2が差配するQueueベースのパイプラインが標
準入出力をこっそり差し替えている
以上のことがRefinementsによって実現されてお
り、コマンドからはそのことは見えない
Page: 40
総じて左様
UNIX風儀に外郭被せたるCRuby(個人の感想)
然る一方PicoRubyの足元甚だ空虚
PicoRubyでもパイプしたいじゃん!導管!同感!
荒馬たるThreadを馴馬たるTaskへと鞍替えし
神代の遺物Refinements(暴言)に松明を掲げ
理を転じてPicoRubyにUNIX風情パイプを普請せり
Page: 42
宴席にて各々方に問うべき儀あり
「Proc#usingを推進する方策」とか
「貴殿等の考える最強のRefinements」とか
来週の松江Ruby会議12でジョーカーさんがいい話します
「タスクローカルRefinementsをmrubyにマージ
するためにまつもとさんを調略する計事」とか
だがその前に……