Folang
Folang
FSharpっぽい見た目でGoとして動く言語を作りたいなぁ、と思い、Folangと名付けて開発をしている。
レポジトリ
karino2/folang: Funcitonal language transpiler to golang.
開発動機
dotnetはやっぱりかったるさがあるので、runtimeやデプロイはGoが良いと思う。 でも言語はFSharpみたいなのが好きなので、なんかトランスパイルでどうにかならんかな? 実用にはならなくてもgoのお遊びとして結構やってみたい気もする。
とりあえず簡単なシンボルのツリーからgoのソース生成するのを作って、それを発展させていってそれっぽいものに出来ないかしら? セルフホスト出来る感じに出来たらちまちま時間をかけて進めていけそうな気もするが。
fsharpを移植したいのではなく、ランタイム的にはなるべくgoそのままにしたい。プラスアルファで型情報くらいは追加で持ってもいいかもしれないが。 という事で言語的には全く新しい言語になるだろう。
関連リンク
参考になりそうなリンクを貼っておく。
golang関連
- example/gotypes at master · golang/example go のtype checker
- elliotchance/pie: 🍕 Enjoy a slice! A utility library for dealing with slices and maps that focuses on type safety and performance. mapとかのライブラリ
- pie/v1 at master · elliotchance/pie v1はコードジェネレーションをしているらしく、コメントとかでアノテーションとかしているのでコード的には似たものが使えるかもしれない。
golangによる言語処理系
- kztomita/golisp: GoによるLISP実装 IRのツリーとかをGoでどんな感じで書くのかの参考に
- Go言語でつくるインタプリタ - O'Reilly Japan こんな本があるらしい。まぁあまり読む必要も感じないが。
- bradford-hamilton/monkey-lang: Currently extending the Monkey programming language designed in the books "Writing An Interpreter In Go" and "Writing a Compiler in Go" 上記の本で出てくるmonkey言語を拡張しているものらしい。
- google/starlark-go: Starlark in Go: the Starlark configuration language, implemented in Go 手本としてはstarlarkとかどうだろう。
- rhysd/gocaml: 🐫 Statically typed functional programming language implementation with Go and LLVM 言語的にはcamlが似てるよな(当たり前)
- google/grumpy: Grumpy is a Python to Go source code transcompiler and runtime. pythonのgolangへのトランスパイラ。こんなのあったのか!?
参考になりそうな関数型言語系
- Pattern Matching / Destructuring - ReScript Language Manual ReScriptのドキュメントはJSの例が出ていてかなり参考になる。なぜか最新のドキュメントはJSのコードがバグってるのでv10のリンクを貼っておく。
- Overview · Reason ReasonML、JSとのinteroperabilityを重視しているのでこれはこれで参考になる。(追記:ReScriptの方がメンテされてそう)
- oden/doc/compiler-overview.md at master · oden-lang/oden Haskellで書かれた似たようなコンセプトのもの。かなり頑張っているが途中で開発が止まっていて残念。
- Explore this site - F# for fun and profit fun and profitはとりあえずここから。
- Golang · fable-compiler/Fable · Discussion #3346 fableのgolangバックエンド途中まで。
- Borgo Programming Language Rustっぽい言語をgolangにトランスパイルするらしい
- borgo/std/core/core.brg at 3b9f01578941fb00ed93756e2fadc009feb50128 · borgo-lang/borgo brogoでのTupleとか。参考になりそう。
- borgo/importer/importer.go at 3b9f01578941fb00ed93756e2fadc009feb50128 · borgo-lang/borgo Importer。こういうの自分も作らないとなぁ。
- Borgo Programming Language 型情報ファイルがどうなっているか。
その他
- Parsing expressions by precedence climbing - Eli Bendersky's website
- hhvm/hphp/hack/src/utils/lsp/lsp.mli at master · facebook/hhvm Hackに入っているOcamlのlsp、1400行くらい
- 本家のocaml/ocaml-lsp: OCaml Language Server Protocol implementationが最初はここから始めたとか
仕様検討
開発日記
やった事を書く場所が欲しくてとりあえずここに置いておく。
グローバル変数定義対応、Unionのgenerics対応 2025-03-02 (日)
Unionのgenericsを対応するにあたり、再帰型の扱いが難しくなってきて、lookupを必要になるまで遅らせるように直したくなる。 けれどいちいちlookupの辞書を全てに渡すのは嫌(大変更だから)なので、グローバル変数に対応しよう、と思い立つ。 これまでもGoの側で定義して関数でラップすれば使えたけれど、 別にグローバル変数に対応しても良いでしょう。
Golangのグローバル変数は、右辺が定数じゃないとconstは使えないので、全部varにする。
グローバル変数を使ってレコードやUnionの情報を辞書に入れて必要になるまでlookupを遅らせることでrecurive問題を解決し、 それをベースにUnionのgenerics対応をする。 なんとなく動いている風味か?
2025-03-03 (月)
今後のタスクを考えたい。とりあえずcsvplrを移植したいなぁ、と思っているので、 パーサーコンビネータを作りたいと思っている。簡単な奴。 そのためにUnionのgenericsを実装したみたいな所もある。
そのためにも必要なことを列挙してく
- package_infoにUnionを書けるようにしたい
- openが欲しい
- ビルドイン型を作りたい(frtに定義)
この3つくらいかな。ビルドイン型はResult、Option、Dictあたりはビルドイン型にしたい。frtの型をプレフィクス無しで見えるようにするだけでいいとは思うんだが。
一方で今書いていて思ったが、パーサーコンビネータを作るだけなら上2つだけでいいな。もっと言えば一番上だけで良い。 ただparserはプレフィクスとしては長いので、そろそろopenは欲しいかもしれない。
とcsvplrのコードを見直してみたが、意外と面倒な機能をいろいろ使っているな。 次のターゲットにはあまり良くないかもしれない。 むしろ相互再帰のあるパーサーはparsecでは変数の副作用を使っているので、こういうのはgolangで書く方がいいのでは、という気もしてくる。
むしろ現状のセルフホストのパーサーをgenericなUnion使う版に書き直すか?もともとside effectがあまり無いスタイルのパーサーなので、 コンビネーター的なものになりつつあるので、もっと推し進めてもいいかもしれない。
一方でせっかくだから、いろいろな用途に使っていきたい、という問題もあるな。 csvplrは思ったより大変なのでもっと簡単な用途がいいかもしれん。
3要素タプルのサポート 2025-03-05 (水)
確定申告の息抜きにパーサーのコードを見直したり整理したりしていた。
ifとelifを並べたコードは、生成したコードが美しくないので、 matchのstringが欲しいなぁ、という気がする。 ただfolangとしては別にelifが並んでいるのはそんなに悪くないので、優先度は微妙。 実装もそんなに大変ではないはずだが。
あと、パーサーを、もっと共通で使える道具を増やしていこうとすると、以下のようなネストしたdestructuringに対応したいなぁ。
let (a, (b, c)) = ...
これがあれば、値を返すパースを2つつなげてtupleにする、という関数を作れば、 パーサーをつなげて書ける所が多い。
現状は、以下のコードは
let (a, b) = rhs
以下にトランスパイルされているが、
a, b := frt.Destr(rhs)
これが二段階になればいいのか?
a, _t0 := frt.Destr(rhs)
b, c := frt.Destr(_t0)
二行目以降の右辺は単なる変数になるのだから、再帰的にやっていけばそんなに大変ではなさそうではあるが。
csvplrが3要素タプルを使っていたのでサポートした そうしたら、これまで決めていた以下のルール、
[]T*Uは[](T*U)T*[]Uもvalid
の延長で、T*U*VがT*(U*V)になってしまって2要素タプルとみなされるように。 これは駄目だ。
やはり []T*U は([]T)*U とパースするしかないかぁ。
alecthomas/participle: A parser library for Go のexamplesのexpr4を見ていたら、手書きパーサーの例が出ている。 おぉ、いいじゃん、こういうのやりたかったんだよ、ということでparticipleでcsvplrの移植をやってみることにする。
UnionにStringerのメソッドを生成 2025-03-06 (木)
デバッグ時に不便なので、Stringerを生成することにした。とりあえずUnionだけ。 Recordもそのうちやってもいいかもしれないが。
csvplrのパーサーをparticipleで書いてIRはUnionを生成するのができた。まぁまぁ簡単に出来たな。このくらいならF# とそんなに面倒さは変わらない気がする。 次はdataframe系のパッケージを使ってcsvのやりとりか。
csvplrの移植はなかなかいい感じのタスクな気がしてきた。
文字列のマッチとinner関数のletはそろそろ対応してもいいかもな。 後者は内部的にはfunのletに変換する感じにして単なるシンタックスシュガーとして扱う感じで。
match周辺の型リファクタリング、exhaustive checkを実装 2025-03-13 (木)
stringのmatchをそろそろサポートしたい、と少しやってみたが、どうも既存のmatchのcasesの型がよろしくなく、 一緒に変更したらなんかいつまで経ってもコンパイル出来ない感じになってきたので一旦stashして型のリファクタリングから。 いい感じになる。
stringのパターンを増やすといかにも追加し忘れが出てきそうなので、exhausitve checkを実装することに。 割とあっさり実装出来て、これまで漏れてたのも見つかっていい感じ。
stringのmatchを実装, letのinner関数定義を実装 2025-03-16 (日)
地味に変更が多くてやる気が出なかったstringのmatchをようやく実装。 その過程でトランスパイルのエラーメッセージを改善。だいぶエラーの場所をちゃんと教えてくれるようになってきた。
ついでにletのinner関数定義を対応。内部的には以下を
let localf a = someExpr
以下に変換している。
let localf = fun a -> someExpr
ちなみにこのaは、親の関数のtype parameterとして解釈されるため、このlocalfはこの関数内では一つの型としてしか使えない。(親の関数を呼ぶ時にお異なるtype argを与えて別の型にする事は出来る)。
これでcsvplrを移植するのに必要な機能は揃ったかな。
最近の変更分のドキュメントを更新しておく。まぁ読んでる人がどれだけいるかは微妙だが。
csvplr移植、QFrameを評価 2025-03-19 (水)
Folangの応用としてcsvplrを移植するのに、DataFrameのライブラリを選び、QFrameを使ってみる事にする。
tobgu/qframe: Immutable data frame for Go
この辺は特にこだわりは無いので使ってみて駄目ならほかを試す感じで。
見た感じ結構高機能なので、csvplrを移植するよりも、ASTから直接このQFrameのフィルタとかを生成する方がいいかもなぁ。