memomimiumprogramming-language
mimium と 多段階計算
多段階計算を取り入れたい → 多段階計算を命令型VMインストラクションで表現したい
とりあえずW Calculusを自然に拡張してみる。
W Calculusとmimiumの形式は似ているが、主に2つの違いがある。
- W Calculus はLinear-Time Invariant なシステムを想定しているため、基本演算は項の加算と、項と定数の乗算しか使えない。
- W Calculusでは関数が数をとって数を返すものしかない。つまり、関数やfeedの項を取ったり返すような高階関数は想定されていない。
問題になるのは後者の方だ。
型
n以下の自然数In (ディレイのbounded access用)
τ::=∣ ∣RaInτ→τa∈Nn∈Na,b∈N
とりあえず1要素のタプルと普通のRは区別しないことにする
(そしてよく見るとこれは関数→関数のような高階関数を許してないんだな)
そうか高階関数を考えなければクロージャを考慮する必要もないものな
ブロックとサンプルの互換性をどうするかが問題?
値
一旦タプルについては考えないことにしよう
v::=∣∣Rλx:τ.efeedx.e[lambda][feed]
項
e::=∣∣∣∣∣xx∈Vλx.eeefixx.efeedx.edelayee[value][lambda][app][fixpoint][feed][delay]
基本演算(Intrinsic)は直感に任せる
本来はfixの中でfeedを使ったり、feedの中でfixを使うとエラーだが、結局シンタックスレベルでは排除できないので型でエラーとして弾くことにする…?
いや値レベルでの切り分けは不可能なので、こうする
実例
ちょっとわかりやすさのためにself
を使わずfeedにしてみる
あー、今まではfn(x)
でself使うものをfeed(self).lambda(x).e,
って感じに自動的に変換してたけど、変換するとしたらlambda(x).feed(x).e
の方が良かったってことなんだな
これをcascade(3,0.9)
とかで簡約してみるか
feedの項に対する加算とか乗算の計算は簡約がしづらいなあ
時間0の時のyは全て0として、
やっぱdenotationalの方が定義しやすいかもなあ
ああでもfeedを無事に展開できるということは、feedの項に対してCell
を割り当てることそのものには間違いはないのか
ただ、例えばFeedの項の中に関数が残っちゃうような可能性もあるため、Feedのhistoryの中にLambdaの項が保存されるような状況が回避できない。
型付規則の中でfeed x.eのeがプリミティブというか、Boxedにならないものしか取れないようにすればいいのかね。そうするとValueはCopyトレイトを実装できて、feedの中に実際にはラムダが入ってたとしても、簡約後は必ずValueになっていると
型(改正版)
というわけで型の定義再訪
τp::=∣τ::=∣RaInτpτ→τa∈Nn∈Na,b∈N
でラムダ抽象とFeedの型付け規則こういう感じになると
Γ⊢λx.e:τa→τbΓ,x:τa⊢e:τb
Γ⊢feed x.e:τpaΓ,x:τpa⊢e:τpa
タプルとかレコードもできるけど、関数をタプルの要素にしたりはできない(できないでもないけど、「そういう型をとれるタプル」と「そういうのできないタプル」を分けて考える必要がある)、って感じでユーザーにはややこしいですねえ
Feedのスコープと簡約の順番について
ところでさっきのcascade
関数ってさ、わざわざ高階関数にせず一括でやれるんですかね、
ともあれコピーキャプチャのクロージャでも問題はなさそうだけども、この状態だとcascade
のfeedのコンテキストは毎サンプル終了しちゃうって感じなんだよね
VMのインストラクションとデータ構造
この関数だけだとfeedidをどうつければいいかなあ
lowpassは最終的にlambda{feed{self}}的な感じになるが、それはfilterbankの中で呼ばれるまではわからない
lowpassのバイトコードはこんな感じか
lowpass: //stack 0:input, 1: fb
moveconst 2 0
sub 3 1 2
getfeed 4
mult 5 2 4
add 6 3 5
retfeed 6 1
const:
1_i64
upindexes:
//nothing
global_ftable:
lowpass
filterbank
filterbank: //stack 0:N,1:input,2:lfreq,3:margin,4:filter
moveconst 5 0
gt 6 0 5
jmpifneg 6 16
mult 6 0 3
add 7 2 0
move 6 6 7
move 7 4 // get filter
move 8 1
move 9 6
callcls 7 2 1 //result on stack 7
moveconst 8 1
sub 9 0 8
move 10 -1 // get recursive call
move 11 9 // prepare arguments...
move 12 1
move 13 2
move 14 3
closure 15 4 // hof requires all function should be closure
call 10 5 1 //recursive call, result on stack 10
add 0 7 10
jmp 1
moveconst 0 0
ret 0 1
const:
0_i64
-1_u64
feedが呼ばれた時にどのfeedidかを判別するのはランタイム側の役目
なんかこんな感じだとして、レキシカルに何番目の関数呼び出しか、
mimiumの中間表現を考える