#memo #mimium - [[多段階計算を命令型VMインストラクションで表現したい]] - 一時期考えていたが、あんまり筋が良くないのでやめた [Implement multi-stage computation intepreter by tomoyanonymous · Pull Request #136 · mimium-org/mimium-rs · GitHub](https://github.com/mimium-org/mimium-rs/pull/136) シンプルに構文木レベルでのインタプリタを別途作ることで実現できそう。 ## 課題 - 組み込みの関数の型情報がマクロ用(レベル0)とVM用(レベル1)で分かれているので、両方にあることを別々に宣言しなければならない - 逆に、ベクターのappend・removeや文字列操作など、メモリを確保する操作はレベル0でのみ利用できる組み込み関数として制限すると、便利かもしれない - 逆に、`self`や`delay`はレベル1でのみ利用できる関数とする - レベル0,1両方で利用できるpersistentなモジュールは、上記2種類のいずれも使わず、かつescapeとBracketも利用しないものであればよい、ということになる - もともとディレイの最大サイズはリテラルで指定しなければいけないという問題があったが、これを`make_delay(size:float)-> <(float,float)->float>`(レベル0で最大時間を指定すると、入力と遅延時間をとる関数のコードを返すという関数)にできるかも - ただ、これやるとディレイが絡む関数はすべてレベル0定義になるのかな - なんかそれよりは、[[数値プリミティブ型に常に範囲をつける]]とかのほうが筋がいいかも ```rust fn fbdelay(max_time){ `|input,time,fb| { (input + self*fb, time) |> make_delay!(max_time) } } ``` - こういうfbdelayを2個以上つかうときのコードはどうなるのかな - というか、fbdelayで生成されたコードをMIRに持ってくときにどうなるのか? - Value型を単なるCodeじゃなくてDelayとして特別な値にリダクションすればいいのかな [[otopoiesis]]でパラメーターを生成するのにも使える? ```rust fn synth(freq,gate){ osc(freq)*gate } fn synth_module(freq:()->float,gate:()->float){ `||{ synth(freq(),gate()) } } ``` これのモジュールを評価すると、freq,gateがUIに現れるという感じでできるのかな(そして、ここでUIの範囲制限をするためにも数値型が範囲を持っていた方がいいということになりそう) うーん、サンクを手動で使わず表現できるような何かが欲しいなあ ```rust fn wrap_module(param:Param, synth:(Param)->float ){ `{ | | synth(param |> invoke) } } ``` Param型はinvokeでuiからの値を取れる、サンクをアンラップするようなメソッドを持つ型クラスに属している、という感じで、ジェネリクスが実装出来たらいけそうね ## マクロを提供するプラグイン プラグインも、各関数が返す型と同時にどのレベルでその関数が使えるかをコンパイラに渡す必要がある ### ファイル読み込みはどうなる? [[mimiumのファイルIO]]の話。マクロの展開より先に型の評価が行われるので、例えばファイルを読み込んでからチャンネル数が分かる、みたいな場合型情報を読み込む段階でファイルを読みこまなければならない、がその評価のためにはマクロを実行して文字列の値を受け取らなければならない、、、ということで、結局[[依存型]]がないと多段階計算でも対応できない。 結局、読み込んで返す値として次元数の値を持つ配列を返すしかないのかも ただ、よく考えると[[Max]]のsfplayとかだって、ファイルのチャンネル数とは別に出力のチャンネル数先に指定するしな(入ってるファイルの中身のCh数に応じて処理を分岐させるというユースケースがあんまり思いつかない) ## シンタックス ```rust let x = `{1+2} //コード型のシンタックス ${x} //エスケープ/スプライスのシンタックス //or $x ``` ### シンタックスシュガー ファイル内でステージを変えたブロックを作りたい場合、ブロックがどんどんネストされていって面倒なので ```rust //ステージ1からスタート stage(-1){ //ここはステージ0 let x = {...} } //一度ステージ1へ stage(-1){ //ここはステージ0 let y = (x+...) } ``` これをこういう形でネストした式へ変形 ```rust //コンパイラはステージ0からスタートするためにCodeを追加 `{ //ステージ1からスタート ${ //ステージ0 let x = {...} `{ //一度ステージ1へ }  ${  //再度ステージ0へ  let y = (x+...)  } } } ``` この、最後に閉じ括弧が溜まっていく形にしない限り、yはxを参照できなくなってしまう ステージ表記は相対表記がいいのかしら