橋本 Hashimoto   Baku

橋本 Hashimoto   Baku

Memo: miyamonz @ソフトウェア科学会第40回大会チュートリアル (Scratchpad)

This page is a personal scratchpad.

ソフトウェア科学会第40回大会チュートリアル「試して作って知る ライブプログラミング入門

miyamonz.iconが雑談タイムに話したことを書きますmiyamonz.icon

  • 依存追跡、状態管理周り
    • 型検査の際に、依存を追跡する必要があって、そこらへんは、状態管理ライブラリとかの実装が参考になるかも、ということで話題に上げた気がしますmiyamonz.icon
    • vanjs
      • React, Vue的な宣言的UIの記述(ただしJSXでなく、単なる関数でやる)ができる極小ライブラリ
      • コードが1ファイルで収まっていて、reactiveなものを書く際の参考になるかも
      • Reactを使うほどでないが、vanillaで書くのはしんどい、みたいなケースで助かる
      • 宣言的UIの表現方法に対して、dom反映側の実現方法の概念は直交なんだな、ということが認識できて良いなと思ってますmiyamonz.icon
        • 仮想DOMでなくてもいい
        • solid.jsとかもあるのだが、それよりはコードが全部読めて嬉しい
      • もしかしたら、glispで採用するのはありか?
        • 大量のelementがあるときのパフォーマンスは知らない
        • ので、そういう点だとメジャーなもののほうが強そうだし、Vueのままのほうが無難なのだろうか?
    • jotai
      • React向け状態管理だが、vanillaで動く & apiが簡単なので使えるかも
      • miyamonz.iconがノードベースの環境をプロトタイプ作った際にも、これを使って楽をしました
      • 状態管理ライブラリとしても便利で(というかそれが主目的ですが)、miyamonz.iconは仕事でもバリバリ使いますmiyamonz.icon
    • vueのcomputedで任せてる部分を、vanilla側で自前でやるようにしたほうが何かといいかも、的な話をしたような気がしますmiyamonz.icon
  • 今のglisp
    • devブランチ ← こちら、ゴミゴミしちゃったので一旦手を留めてます
    • 言語部分の別リポジトリ ←こっちが言語のパーサーや評価器のメインです!
    • ここらへんを追えば良いんですかね?miyamonz.icon
      • ずっと前にmainブランチを結構読んでました
    • 静的解析してどのUIを出すかなどを確定させたい、といった話がありました
  • 突っかかっているところbaku89.icon
    • Glisp v2 はASTの任意の部分木から評価を始める事ができ、その値が必要となった時点で遅延評価される仕様にしようと考えています
      • なぜ?
        • Glispファイル(=プロジェクトファイル)全体が一つの手続き的プログラムとして意味のある処理をするものではない
        • 複数のアートワーク、コンポジション、グループが混在するなかで、プロジェクト階層の必要なアイテムの中身を表示できればいい
        • 更新ごとにコード全体を再評価させたくない
      • 通常のプログラミング言語のナイーブな実装だと:
        • シンボル名と値とを保管する環境の配列 Map<Name, Value>[] を引き回しながらコードの冒頭から評価を始める
          • graph LR  
              subgraph Env1  
                 direction TB  
                 x --> 1  
                 y --> 2  
               end  
               subgraph Env2  
                 direction TB  
                 x2[x] --> 3  
               end  
             Env1 --> Env2
            
        • 内側のスコープに入ると、新しい環境をunshiftする。
        • 変数宣言や再代入にぶち当たった時点で右辺を評価し、環境に値をセット
        •               // [{}] 環境を初期化
          const x = 1   // [{x => 1}]
          const y = 2   // [{x => 1, y = 2}]
          {             // [{}, {x => 1, y = 2}] 新しい環境をunshift
            let x = 2 // [{x => 2}, {x => 1, y = 2}]
            x = 3     // [{x => 3}, {x => 1, y = 2}]
            x         // -> 3 (Envを右側から辿り、xに対応する最初の値を返す)
          }             // [{x => 1, y = 2}] スコープを抜けるので、冒頭のEnvを削除
          x             // -> 1
          
        • つまり、7行目のxからいきなり評価を始めるというのが難しい
          • IDEの型情報のポップアップやJump to Definition機能など、静的解析でProgram Dependency Graphを構築し、「そのシンボルがどこで宣言されどう変更されたか」を逆引きする例はある。
      • Glisp v2の場合、任意のASTから評価を始め、シンボルを祖先ノードへと遡ることで解決する(BaseExp.resolve
        • 以下のような式があるとき
        • (let a: 1
               (let a: 2
                    b: 3
                    (+ a b))) ; <-- a のシンボルを解決したい
          
        • ASTはこんな構造で、内側の足し算のaを解決するには破線のような経路をたどる。
        • graph LR
          let---|a|1
          let-->let2[let]
          let2---|a|2(((2)))
          let2---|b|3
          let2-->app[@]
          app-->+
          app-->a
          app-->b
          a -.-> app
          app -.-> let2
          let2 -.-> 2
          
        • つまり、部分木は親ノードへの参照をもつ必要がある(BaseExp.parent
        • graph LR
          ast --> a
          ast --> b
          a -. parent .-> ast
          b -. parent .-> ast
          
        • すると、親から子、子から親へと相互参照をしてしまうため、Reactのような永続データ的な更新方法が取れない
          • const ast = {a, b}
            const newAst = {...ast, a: newA}
            newAst.a.parent = newAst
            newAst.b.parent === ast // -> true (newAst.b.parentは依然としてastを指している)
            
          • graph LR
            ast-->|a|a
            ast-->|b|b
            a -.-> ast
            b -.-> ast
            newAst -->|a| newA
            newAst -->|b| b
            newA --> newAst
            
        • データの更新はミュータブルに行い、forceUpdateをすれば一応のところ解決
          • const ast = {a, b}
            ast.a = newA
            forceUpdate()
            
        • この方法はReactともVueとも相性がよろしくない
        • また、評価値のキャッシュも難しくなる
        • FlashのAction Script 3や、THREE.js、Paper.jsのようなステージオブジェクトとしてASTを管理することがそもそもの間違いな気がする
          • // こんな感じ
            const group = new Group()
            group.addChild(new Item())
            group.children[0].parent === group
            
      • 評価関数 eval を純粋関数にすればいい
        • つまり、任意の部分木が親への参照を持つのをいいことに eval(ast) のようにしていたところを...
        • 親への参照は持たせず、代わりに必ず祖先ノードのリストを「環境」として渡して評価する
        • eval(ast, [parent, grandparent, ..., root]) のように
          • 厳密には、関数呼び出しにおける実引数なども環境に情報として含める必要があるが
          • ここの必要性は、例えば以下のようなケースであってますかねmiyamonz.icon
            • 特定の階層でcという変数が(let c: (toString someNumber))みたいにletされていたとき
            • 以下の2つの情報があって初めてcの型がstringで確定できるから
              • someNumberがnumber型であることと、
              • toStringが(numberまたはgeneric)=>string
          • たしかに環境を引数に渡すのは良さそうですね。miyamonz.icon
            • これならimmutableなはずだから、計算結果のキャッシュがしやすそう
    • ...というのを実装しようかと考えています…
    • 問題の難しさが分かってきましたmiyamonz.icon
      • やりたいこと(逆に言うと、課題)
        • コンパイラ or 言語処理系を作る
        • コードの変更に対するliveness、計算処理のキャッシュ等を良しなにやりたい
      • 後者の問題の解決方法が、前者に関する資料に乗ってないはずですねmiyamonz.icon
        • 言語処理系として、既存の文献、資料などを当たろうにも、一般的な言語処理系は、livenessという概念が登場しない

Vueはそんなに得意じゃない的な話をmiyamonz.iconがしましたが、

  • やりたくないという意味ではなく、Reactの時ほどのパフォーマンスは出せないかも、位の話で
  • glispは面白いので、開発協力できるところあればしたいですmiyamonz.icon
  • CSSとか
  • UIコンポーネントライブラリとか
    • miyamonz.iconがReactばっか使ってるのでVue関連で具体的なものがあまり挙げられないですmiyamonz.icon
    • 雑談の際には、「凝る必要が無いところはライブラリに頼って速く作った方が良いかも」といった趣旨の話をされてましたっけmiyamonz.icon
    • 現在、みんな絶対使ってるデファクトスタンダード的なコンポーネントライブラリが無さそうな印象です
    • ヘッドレスUIという概念
      • コンポーネントの見た目は自分で作るから、style以外の部分が欲しい、という要求がある
      • classだけを提供するtailwindと相性がいい
        • そもそもこの概念自体がtailwindから出されたものだった気がする
      • こういう概念のように、スタイルと機能を分離したいという要求がある
        • とりあえず見た目の良いコンポーネントを使いたい、提供したいというニーズ自体が減っているような気がします
        • 提供している場合でも、内部的に、ロジック(headlessな部分)とスタイルを分離して作ってるはず
    • Reactで有名なもの
      • Chakra UI
      • radix-ui
      • shdcn/ui
        • これはcomponent libraryでなく、スニペット集的な立ち位置
        • Radix UIとTailwind上で作ってあるから、自分でコピペして使え(だからnpm libraryもない)という立ち位置
        • 要件(あるいはアプリケーション側)ごとに固有な要求があり、それをライブラリ側に抽象するのは無理になりがちなので、このスタンスは正しいなあ、と思いますmiyamonz.icon

タイトル変更、編集等、自由にしてくださいmiyamonz.icon