Slide 1

Slide 1 text

継続モナドと リアクティブプログラミング @yukikurage

Slide 2

Slide 2 text

⾃⼰紹介 ゆきくらげ (@yukikurage_2019) 好きなもの: ‧関数型プログラミング ‧猫 ‧Slay the Spire 継続モナドとリアクティブプログラミング ← X

Slide 3

Slide 3 text

内容 • リアクティブプログラミング • リソース • リソース指向リアクティブプログラミング (Quon) • 継続モナド • Rules of Hooks と ArrowChoice • Agent ⽤ DSL (Katari) • Component Based な Agent Network 継続モナドとリアクティブプログラミング 背景

Slide 4

Slide 4 text

リアクティブ‧プログラミング 何? • → データの時間軸⽅向の変化を主軸とするパラダイム • データとそれに依存する処理を分けて書く 利点 • 更新伝播の Source of Truth → 後述 使われている場所 • Webフロントエンド (React、Vue など) • ゲーム など 継続モナドとリアクティブプログラミング 背景

Slide 5

Slide 5 text

リアクティブ‧プログラミング let state = 0 state.subscribe(()  { view.text = state }) on("click", ()  { state = state + 1 }) 継続モナドとリアクティブプログラミング 背景 イベント駆動によってデータに依存する処理を切り出す let state = 0 updateView = ()  { view.text = state } on("click", ()  { state = state + 1 updateView() })

Slide 6

Slide 6 text

更新伝播の Source of Truth 継続モナドとリアクティブプログラミング 背景 Data Event Process Event 普通に書くとこうなる 値の更新 依存処理の実⾏(updateView)

Slide 7

Slide 7 text

View 更新伝播の Source of Truth 継続モナドとリアクティブプログラミング 背景 Data Event Event ? View Process Data ‧Process をすべて実⾏するのは⾮効率。ではどの Process を実⾏する? ‧Event はそれぞれの Process がどのデータに依存しているか知らない Data や Process が複雑になると……

Slide 8

Slide 8 text

更新伝播の Source of Truth 継続モナドとリアクティブプログラミング 背景 Data Event Process Event リアクティブプログラミングでは Process が Data を購読する 値の更新 購読

Slide 9

Slide 9 text

更新伝播の Source of Truth 継続モナドとリアクティブプログラミング 背景 Process Data Event Event Process Process Data 購読 Processは、⾃⾝が依存する Data のみを Subscribe すればよい。

Slide 10

Slide 10 text

種類と特徴 • React Snapshot と差分適⽤ • Vue Reactive 値を明⽰的に扱う • FRP Event 列と Behavior の分離 • Rx 実践的リアクティブプログラミングライブラリスタック 継続モナドとリアクティブプログラミング 背景

Slide 11

Slide 11 text

リソース リソースとは何か ‧メモリ上に確保されたデータ ‧WebSocket などの接続 ‧画⾯に表⽰されている UI ‧「event を購読している」という状態 → "初期化" と "解放" 継続モナドとリアクティブプログラミング 背景

Slide 12

Slide 12 text

リソースの寿命 リソース = 初期化と解放があるもの 初期化から解放までを寿命と呼ぶ 継続モナドとリアクティブプログラミング 背景 初期化 解放 Lifetime リソース t …

Slide 13

Slide 13 text

リソース安全性 リソース r が r' に依存しているとする リソース安全 = 任意の時点で、 「r が確保されている → r' が確保されている」 継続モナドとリアクティブプログラミング 背景 A B C 依存

Slide 14

Slide 14 text

観察 継続モナドとリアクティブプログラミング 背景 リソース r が r' に依存している → r' を r より前に解放してはいけない Rust とかは所有権でどうにかしている 発想: ⾃⾝の寿命を親に管理させる (つまり任意解放できない) 親側が⼦のリソースの初期化‧解放を管理 → これを時間軸⽅向に畳めば リアクティブプログラミングになりそう

Slide 15

Slide 15 text

リソース指向リアクティブプログラミング ‧「リソース指向リアクティブプログラミング」は造語 ‧作りかけ ‧重要な要素は⼆つ ‧Routine: リソースの初期化‧解放を管理 ‧Source: 変化するリソース集合 継続モナドとリアクティブプログラミング Quon Quon

Slide 16

Slide 16 text

Routine 型 継続モナドとリアクティブプログラミング Quon type Routine t = Sync {result: Async t, dispose: Async ()} Sync / Async は Haskell では IO 外側から、任意のタイミングで Sync を呼ぶ その後、Routine は内部でいくつかリソースを確保 確保したリソースの内⼀つを result に表出 Sync を呼んだ後は任意のタイミングで dispose を呼べる

Slide 17

Slide 17 text

Routine 型 継続モナドとリアクティブプログラミング Quon type Routine t = Sync {result: Async t, dispose: Async ()} time 初期化 result (t) dispose Routine t 外部

Slide 18

Slide 18 text

Routine 型の bind 継続モナドとリアクティブプログラミング Quon let program = do let r1 = useR1(x) let r2 = useR2(r1) return resource2 初期化 解放 program useR1 useR2 Routine は解放処理を逆順に合成する 提供された Routine が Safe → 合成したものも必ず Safe

Slide 19

Slide 19 text

Source 型 継続モナドとリアクティブプログラミング Quon type Source a = (a -> Routine ()) -> Routine () 直感: 内部に a 型のリソースを複数持つ構造 時間で変化しうる リソースが追加された時リスナーを初期化 リソースが削除された時そのリスナーを解放 この subscribe 状態⾃体もリソース(外側の Routine) リスナー “Subscribeリソース”の確保

Slide 20

Slide 20 text

Source による Routine の階層 継続モナドとリアクティブプログラミング Quon type Source a = (a -> Routine ()) -> Routine () リスナーは、いわゆる「コンポーネント」の初期化 Routine はモナドであるので、 リスナーを作る時内側でネストした購読を張れる リスナー

Slide 21

Slide 21 text

階層とコンポーネントシステム ‧コンポーネント = その初期化処理と解放処理 = Routine 継続モナドとリアクティブプログラミング Quon Routine Routine 状態 購読 Routine Routine 親コンポーネントの Routine ⼦コンポーネントの Routine 群

Slide 22

Slide 22 text

継続モナド 突然ですが ContT r m a = (a -> m r) -> m r と定めます 継続モナドとリアクティブプログラミング Quon

Slide 23

Slide 23 text

継続モナド ContT r m a = (a -> m r) -> m r a 型の値を少しリッチにしたもの 「後続の計算 a -> m r を受け取り m r を返す関数」 を a 型の値の代わりとして⽤いる。 継続モナドとリアクティブプログラミング Quon

Slide 24

Slide 24 text

継続モナド ContT r m a = (a -> m r) -> m r a 型の値は容易に Cont r m a へ変換できる (渡された関数 a -> m r を a に適⽤し、m r を返す) これは Monad における pure (return) になりそう 継続モナドとリアクティブプログラミング Quon

Slide 25

Slide 25 text

継続モナド ContT r m a = (a -> m r) -> m r Monad のもう⼀つの操作 Join : Cont r m (Cont r m a) -> Cont r m a つまり ((((a -> m r) -> m r) -> m r) -> m r) -> (a -> m r) -> m r はできる? 継続モナドとリアクティブプログラミング Quon

Slide 26

Slide 26 text

継続モナド ((((a -> m r) -> m r) -> m r) -> m r) -> (a -> m r) -> m r 継続モナドとリアクティブプログラミング Quon 1. m r を作らなければいけない 2. (a -> m r) -> m r から m r を作る処理があれば m r が得られる 3. (a -> m r) -> m r に (a -> m r) を適⽤すれば良い。 実は Monad 則を満たす[読者への課題]ので Monad ややこしいが、できる

Slide 27

Slide 27 text

継続モナド 結局何ができる? reset : Monad m => ContT r m r -> ContT r' m r shift : Monad m => ((a -> m r) -> ContT r m r) -> ContT r m a 継続モナドとリアクティブプログラミング Quon その時点での後続の計算 後続の計算を好きに加⼯して実⾏

Slide 28

Slide 28 text

継続モナドの使⽤例 リソースマネジメント 後続の計算 c = hdl を受け取った後の処理 shift により open / close で c を挟む → hdl の make / freeが⼀か所で書ける 継続モナドとリアクティブプログラミング Quon f = do hdl <- shift (\c -> hdl’ <- open c hdl’ close hdl’ ) content <- read hdl …

Slide 29

Slide 29 text

継続モナドの使⽤例 ⼤域脱出 後続の計算を呼ばないことも可 すると⼤域脱出が可能 data が Invalid の場合短絡 継続モナドとリアクティブプログラミング Quon f = do data  fetch something t  shift (\c  case data of Invalid  return “error” Valid text  c text ) return $ “Hello”  t その他にもいろんな制御ができる

Slide 30

Slide 30 text

余談: Gleam の use はほぼ継続モナド 継続モナドとリアクティブプログラミング Quon // f : fn(fn(a)  r)  r // g : fn(fn(b)  s)  s // に対して use x  f use y  g body → f(fn(x){ g(fn(y){ body }) }) に脱糖される use を使うことで、 その時点での継続を⼊⼿ 構⽂レベルで Cont モナドを実装

Slide 31

Slide 31 text

Source は継続モナド ContT r m a = (a -> m r) -> m r なので‧‧‧ Source a = (a -> Routine ()) -> Routine () = ContT () Routine a 継続モナドとリアクティブプログラミング Quon

Slide 32

Slide 32 text

Source は継続モナド 何故 ContT になるのか Cont は後続の処理のコントロールを可能にする Source の場合は ‧「後続処理」はリスナー ‧「コントロール」は動的なリソースの追加/削除 を表す 継続モナドとリアクティブプログラミング Quon

Slide 33

Slide 33 text

継続モナドとしてSourceを使った場合 source = do let x = sourceX let y = sourceY(x) f(x, y) 継続モナドとリアクティブプログラミング Quon ネスト構造をフラットに書ける 空間を張り合わせているように⾒える 利点: ⾯⽩い sourceX x x x y y y y f(x, y) f(x, y) f(x, y) f(x, y) sourceY sourceY sourceY

Slide 34

Slide 34 text

Quon : 例 app = do tab  useCell TabCounter onEvent $ \case EvKey (KChar '2') []  set tab TabProfile EvKey (KChar '3') []  set tab TabTodos _  pure () body  dyn loadingView $ cellSignal tab <&> \case TabProfile  profileTab TabTodos  todosTab pure $ body 継続モナドとリアクティブプログラミング Quon Tab 状態の作成 Event に対する反応 loadingView は React の Suspense みたいなもの Tab 状態の派⽣状態 (ここでは View)

Slide 35

Slide 35 text

⽐較 この Routine の仕組みは Vue や Solid.js の setup function に似ている ⼀⽅ Source のようなリソースの動的集合を主軸とするも のはあまりなさそう [要出典] Setup が Async にできるのは Async React っぽい 継続モナドとリアクティブプログラミング Quon

Slide 36

Slide 36 text

形式化(おまけ) ℛ : Resources Γ, Δ ⊆ ℛ : Environment r ↝ r′ : r depends to r' (ℛ : partial ordered by ↝) Σ = {init, dipose} ⨄ {7ill(r), dd} ⨄ {acq(r), rel(r)} : Events init, dispose: Input fill, dd: Output acq, rel: Internal 𝜎 ∈ Σ!" : Trace 𝑒 ∈ 𝜎 ↾ 𝑖: event e is evoked before time = i 𝜌# 𝑖 = {𝑟 ∈ ℛ: 𝘢𝘤𝘲 𝑟 ∈ 𝜎 ↾ 𝑖} ∖ {𝑟: 𝘳𝘦𝘭 𝑟 ∈ 𝜎 ↾ 𝑖}: Resources when t = i 継続モナドとリアクティブプログラミング Formalization

Slide 37

Slide 37 text

形式化(おまけ) Under environment Γ,𝜎 ∶ Σ!" is admissible iff: - (Uniq. resource) ack(r′) ∶ uniq. to r′, r′ ∉ Γ - (Init) 𝜎[0] = init - (Uniq. event) 7ill, dispose, dd, init ∶ up to 1 - (Fill consist.) if 𝜎[i] = 7ill(r), r ∈ 𝜌# 𝑖 - (Res. safety) if r ∈ 𝜌# 𝑖 , ∀r′ ↜ r, r′ ∈ 𝜌# 𝑖 - (Res. keep) until 7ill(r) to dispose, r ∈ 𝜌# t - (Disp.) if 𝜎[i] = dd, i is 7inal elem, and 𝜌# i = ∅ 継続モナドとリアクティブプログラミング Formalization

Slide 38

Slide 38 text

形式化(おまけ) 継続モナドとリアクティブプログラミング Formalization Definition of Routine Set M ⊆ Σ!", Γ ⊢ Routine(M) iff (1) ∀𝜎 ∈ M is admissible under Γ (2) ∃𝜎 ∈ M s. t. 𝜎[0] = init (3) ∀𝜎 ∈ M, ∀𝜎′: pre7ix of 𝜎, ∃𝜎′′ ∈ M, [. . . 𝜎′, dispose] is pre7ix of 𝜎′′

Slide 39

Slide 39 text

形式化(おまけ) 継続モナドとリアクティブプログラミング Formalization 今後の流れ ‧Routine の Monadic Chain を定義 ‧Routine が Resource Safe → それらの合成も Safe を証明 ‧Source の理想的な定義が Safe であることを証明 ここでは説明しません 何故なら…… ☝出来ていない (修論にする予定)

Slide 40

Slide 40 text

Row/Patch について ‧導⼊するか悩んでいるもの ‧連続値の現状の表現: リソースが消え、それと同時に別のリソースが現れた 継続モナドとリアクティブプログラミング Patch Semantics 0 1 2 0→1→2 と変化する時変値 = リソース 0, 1, 2 が順番に確保される Source

Slide 41

Slide 41 text

現在の表現で困ること たとえば、状態に合わせて fetch を⾏うとする すると、切り替えたときにギャップが⽣まれてしまう →実際は、fetch 完了まで前回の内容を表⽰しておきたい 継続モナドとリアクティブプログラミング Patch Semantics 0 1 fetch fetch fetch 2

Slide 42

Slide 42 text

現在の表現で困ること →実際は、fetch 完了まで前回の内容を表⽰しておきたい これが難しい それぞれの状態は独⽴リソースなので、それらの関係性を 把握できない 継続モナドとリアクティブプログラミング Patch Semantics

Slide 43

Slide 43 text

Row型 そこで導⼊するのが Row 型 Row a: 変化する a 型の連続値 (Recap: Source a: 変化するリソース集合) 詳しい data 定義は述べない (まだ決まっていない) 継続モナドとリアクティブプログラミング Patch Semantics

Slide 44

Slide 44 text

Patch型 Patch a b: Row a に対するリスナー Routine ではダメなのか? ‧Routine はそれぞれ独⽴したコンテキストを持つ ‧「前回の状態」を使いたい処理には向かない → 内部状態的なものを持つ必要がある 継続モナドとリアクティブプログラミング Patch Semantics

Slide 45

Slide 45 text

Patch の性質 Patch a b は次のような定義を持ちそう Patch a b = a  Sync { result : Async b, next : Patch a b // 再帰的に呼べる } Patch a b は同じ対象に対する連続的変更を表す next を呼び、そこから得られた next を呼ぶ…を繰り返す 内部状態を持てる 継続モナドとリアクティブプログラミング Patch Semantics

Slide 46

Slide 46 text

Patch は Monad ではない 継続モナドとリアクティブプログラミング Patch Semantics Patch a b Monad → if による分岐が可能 Patch は内部状態を持つので 1. 毎回同じ Patch を実⾏する (if ⽂なし) 2. if ⽂は特別扱いし、グラフ再構築&内部状態リセット 筋がよさそう Patch b d Patch b c Patch c d if

Slide 47

Slide 47 text

Arrow の登場 a から b への抽象化された⼿続き P a b が Arrow → 変数の導⼊と静的な P a b に対する適⽤が可能 ArrowChoice → 途中で if 分岐が可能 ArrowApply → ⼿続き P a b が第⼀級になる Arrow → ArrowChoice → ArrowApply の順で強くなる 継続モナドとリアクティブプログラミング Patch Semantics

Slide 48

Slide 48 text

Patch は ArrowChoice なしの Arrow ArrowChoice(分岐), ArrowApply(第⼀級関数) が無い これによって常に⼀本道、同じ順で評価される → グラフの再構築もしない 継続モナドとリアクティブプログラミング Patch Semantics proc pattern -> do y <- f -< e z <- g -< e’ h -< e'' こんな感じで書けるが、分岐を書くと型エラー

Slide 49

Slide 49 text

ArrowChoice なしのArrow ≒ Rules of Hooks ところで if が使えない → Rules of Hooks に似ている ‧同じ順番で use… を呼ばなければいけない React は内部状態を Linked List で持っている 順番でのみ、Hooks の判別をする (グラフ構造が動的に変化してはいけない) → ArrowChoice なしの Arrow が適任 継続モナドとリアクティブプログラミング Patch Semantics

Slide 50

Slide 50 text

発展: Agent ⽤ DSL ‧作りかけ ‧Agent 定義を簡単にできる ‧Durable Execution ‧Parallel 実⾏が楽 ‧Type Reflection、JSON Schema ⾃動⽣成 継続モナドとリアクティブプログラミング Component-based agent system Katari

Slide 51

Slide 51 text

現在の Katari: 例 Discord bot の例 (DEMO?) 継続モナドとリアクティブプログラミング Component-based agent system agent main(channel_id: string) -> string { use gemini.provider(model = "gemini-3.5-flash", api_key = env.get_secret(key = "GEMINI_API_KEY"), system =“他のユーザーからのメッセージに適切に返答してください。") use e2b.provider(api_key = env.get_secret(key = "E2B_API_KEY")) use tavily.provider(api_key = env.get_secret(key = "TAVILY_API_KEY")) use discord.provider(token = env.get_secret(key = "DISCORD_TOKEN")) let tools = [e2b.run_python, tavily.search] serve(channel_id = channel_id, tools = tools) } Function = Agent = Tool が成り⽴つ

Slide 52

Slide 52 text

その他の機能 ‧MCP に接続、MCP を提供できる ‧Webhook に対する購読を管理できる ‧Coeffect (に似た仕組み)による Private 値トラッキング ‧Algebraic Effects による Context providing ‧ 継続モナドとリアクティブプログラミング Component-based agent system

Slide 53

Slide 53 text

観察 ‧MCP に接続、MCP を提供できる ‧Webhook に対する購読を管理できる serve(channel_id = channel_id, tools = tools) : キャンセルするまで動き続ける、discord 監視 & 応答 初期化 & 解放 → リソース 継続モナドとリアクティブプログラミング Component-based agent system

Slide 54

Slide 54 text

現状の問題点 ‧状態の動的な更新が難しい ‧例えば、Claude Code における "Mode" ‧購読の⼀時停⽌、再開 ‧動的なプログラム更新が難しい ‧たとえば ‧Scrap & Build するしかない → 動的な状態 継続モナドとリアクティブプログラミング Component-based agent system

Slide 55

Slide 55 text

将来計画 リソース、動的な状態 → リソース指向リアクティブ‧プログラミングの出番 リアクティブプログラミングを応⽤して Agent Network を構成できそう ‧サンドボックス ‧分散システム(→Server Components?) ‧権限周り、コンテキスト → ここら辺をコンポーネントシステムで統括する 継続モナドとリアクティブプログラミング Component-based agent system

Slide 56

Slide 56 text

Github リポジトリ 継続モナドとリアクティブプログラミング Component-based agent system Quon Katari ちなみに全然 publish できる段階じゃないです (助けて〜)

Slide 57

Slide 57 text

まとめ ‧リソース管理は⼤事 ‧リアクティブプログラミングは便利 ‧合体→なぜか継続モナドが出てくる ‧継続モナドのネスト構造がコンポーネントのネストに対応 ‧Patch は ArrowChoice なしの Arrow ‧React の Rules of Hooks の制約 ‧ Agent Network 構築に使えそう 継続モナドとリアクティブプログラミング