俺言語アイデアメモ
方向性
プログラム知らなくても日本語のように読める言語
オーバーロードをうまく使える言語
静的に色々と解析できる言語
純粋か?スレッドセーフか?環境のどの機能に依存してるか?どんな例外を投げる可能性があるか?参照透明性
意味論がしっかりしている言語
スペースや改行に意味のある言語
めっちゃ速いけど書きにくい言語
アセンブラ書けで終わりそう……
プリプロセッサめっちゃいいなぁ
単純なものならアリ
do while は良くないけど、演算子を引数にできちゃうのは強い。
欠点もある
デバッグだるそう
C言語はスコープとか名前空間がない
強力じゃなくて良いから、良い具合に制限を加えて、良い感じにできないかなぁ
もちろんCの ## とかは禁止
再帰的にプリプロセスするの禁止するとか
仮引数が展開される時、強制でカッコがつくとか(これだと演算子のやつは動かなくなっちゃう)
プリプロセスが展開される時、強制でカッコがつくとか
引数として渡す時には、評価した値を渡すようにするとか(プリプロセスじゃないなぁ)
関数みたいに呼べちゃうから、アレなだけで HOGE<a, b> など呼び出し方を差別化するとか
「展開後、複数の文になる」のは禁止して1式のみに制限するとか
オーバーロードは勝手にキャストしなければ一意に定まる
code:typescript
class A {}
class B extends A {}
function add(a: A, b: B) {}
function add(b: B, a: A) {}
add(b, b) // これが従来だと候補が二つ見つかりエラー
// しかしこの設計だと勝手にキャストしないので、何も関数が見つからないエラーになる。
// 適切に動かすには、下記のようにキャストする
add(b as A, b)
毎回キャストするのが面倒な場合下記もありかも
1. キャストせずに検索
2. 見つからなければキャストして検索
3. 複数の候補が出たらエラー
面倒な問題を含んでそう
後から関数が追加されて複数候補になってしまったら?クロージャがあるから平気?
明示的にキャストしてる場合は「キャストして検索」はしないようにする?
入力する値は引数で良いけど、出力される情報は返り値だけでは足りない。
返り値、エラーなど
if() then() else()
Ruby の loop のように例外をキャッチして止まることでイテレータみたいにする
code:ruby
loop {
i = list.next
pp i
}
# list が空の時に next を呼ぶと StopIteration が raise する
{} を cascade としてやる。関数は cascade にローカル変数を注入する (x, y) => {}
code:text
型注釈によるコンストラクタの省略 (構造体ではなく名前付きタプルとしてやる)
a : Num = 1
c : (List Num) = { 1 2 3 }
d : (Map Str Num) = { "one" 1 "two" 2 }
a : { name: Str age: Num } = { name: "kekemoto" age: 28 }
b : { name: Str age: Num } = { "kekemoto" 28 }
e : { name: Str list: (List Num) } = { name: "kekemoto" list: {1 2 3} }
f : { name: Str list: (List Num) } = { "kekemoto" {1 2 3} }
Human : Struct = { name: Str age: Num }
g : Human = { name: "kekemoto" age: 28 }
add : (left: Num right: Num) -> Num = { left + right }
型注釈と代入を別行にも同行にも書ける
a :: Num // この式の返り値を (Symbol a) にする
a = 1 // (Symbol b) に 1 を代入の意味
b :: Num = 2
後置 catch
構文 expr catch var Type expr
return であれば普通に値を返すだけ。throw されたら catch されるまで上に登る。代数効果みたいに継続を使っても良いが……
関数の引数は一個のみ。0個も許さない。リストや名前付きタプルを使う。制御文も引数は固定。
カッコが消えるし、セミコロンも改行もいらないかも
本当に引数ゼロの関数なんてほとんどない。メソッドは引数一個あるようなもの
できれば null リテラルなくして () {} とかにしたい。
code:text
add = fn (List Num) -> Num { sum it }
// if は引数3個で固定。elseがいらない場合は and を使う
if condition {} {}
condition and {}
condition or {}
自身のスコープ外の変数は参照はできるが代入を禁止する
code:text
var a = "global"
show = fun { print a }
{
(show) // global
a = "internal"
(show) // これで internal と表示されるのは変ではないか?
}
{
count = 0
inc = fun { count = add count 1 }
}
(inc) // 1 よくあるクロージャーの例だけど、おかしくないか?
ーーー
複数の変数を簡単に受け取れるようにする
パターンマッチの変数束縛バージョン的なイメージ
パニングとかとても欲しい
1つの処理で複数のデータが変更されることはあるし、それを副作用なしでやると多値で返すしかなく、簡単に多値の受け渡しができるとプログラマがハッピーになれる
モジュールとかパッケージとか名前空間のインポートも、この形式で受け取りたい
ASTと構文を分離して好きにユーザーが構文を弄れるようにする。
エディターでASTを好きなように表示する
疑問:型で仕様を表現してコンパイラにチェックさせるとは?
自分が思いつくのは、
列挙型のパターンマッチによる、変更の検知
データ構造の変更検知
他に知らない表現方法があるのか、上記の機能だけで十分に仕様を表現できるのか?
マジックをしない愚直な構文。書きやすさよりも、読みやすさを優先
1つのことでやることは1つにする。それがシンプル
直交性を高めるためには、インターフェースを絞るだけでは足りない。
データ構造もプリミティブなものを使って共通化する?Clojure的な
言語内の直交性だけでなく、DBやHTTPなどの外界との交わりも考慮するべき
Scala の プレースホルダ構文
無名関数を作る構文じゃなくて、部分適用をする式にする?
コンパイルは重くていい。差分をコンパイルできるようにする
リリースビルドに時間がかかるのは問題としない。
静的ダックタイピング
動機
プログラマのメンタルモデルにあるのはデータ型ではなく振る舞い。つまりダックタイピングなのではないか?
ダックタイピングを支援する
用語
プロトコル
関数に対して、何を渡すと、何が返ってくるかを定めたもの
関数の入力の境界と、出力の境界が定められている
インターフェース
プロトコルの集合
無名インターフェースも作成可能
境界
プロトコルとインターフェースの総称
プロトコルを静的に決定させることでコンパイルできる
OCaml ばりに推論させたい
注釈もできるようにするが、あまりしない想定
動的関数呼び出しなどで必要になりそう
返り値のインターフェースってなんだ?
1個のデータ型に対して、全てのプロトコルを含むインターフェースが1つ必ず生成されるようにする。
これをデータインターフェースとする
多引数のプロトコルはデータインターフェースに含まれない
Number 型を作成すると同時に Number インターフェースが生成される。
プロトコルの集合がインターフェースなので、AというインターフェースがBインターフェース のプロトコルを全て含んでいるならば、AはBとしても扱える。
データ型のインターフェイスも共通化する
レコード型とマップをほぼ同じにする
マップのキーがとあるレコード型と同じなら、それはレコード型としても扱えるし、
レコード型はマップとしても扱える。
Error と Warning
警告があるとコンパイルできない
モジュール単位やブロック単位で、警告をオフにする設定ができる
全てを制限するよりも、警告した上でやりたい放題の方が、対応範囲が広がる
コード上で警告がオフしてあれば作成者でなくても目に入り、そのブロックに注意が必要なことに気づける
データ駆動 (リアクティブ)
データを変更することで、関連したデータも変更される。関連性を定義していく言語
現実からのイベントが、入力型のデータを書き換えることにより、リアクティブに反応して 出力型のデータを出力する。イベントが出力を読み込む。
イベントとデータ。
リアクティブ駆動(PubSub)
関数ではなく Reactor を組み合わせて処理する。
リアクターは他のリアクターを購読し、通知があれば受け取り、定義付けられた処理をする。
値を返したりはしないが、イベントの Publish を行うことで連携が可能。
Reactor は他の Reactor を一切知らない。
どのリアクターがどのリアクターのイベントを購読するのかのマッピングは別途定義する。
PubSubとリアクティブなデータを使ったパラダイムは面白そう
リアクティブなデータはDBにマッピングできそう
Datomic と相性良さそう
PubSub はアクターっぽい?
文脈による分岐処理
code:ruby
# 1つのレコードを更新するクエリを投げるメソッド
one_update
# bulk の文脈にあるので、1回1回でクエリを投げず、まとめてクエリが投げられる
bulk do
one_update
one_update
end
カオスになるか、うまいことなるか
静的に文脈を解析して、静的に処理を分岐できれば、カオスから遠ざかりそう
静的に決定するのは、型じゃなくて他のでもいい。
副作用のある/なしを静的に決定する。型として表現しなくても関数のメタ情報に含めれば良い
メタ情報はもっと活用してもいいと思う
モジューラビリティを極める
GCやスコープ管理など、ありとあらゆる概念をモジュール化する
どうやって作るんだ
実用に耐えうるビジュアルプログラミング
UE4 のブループリントだな
Lisp に UFCS
C言語との互換性が100%
俺言語からCが使えるし、Cから俺言語が使える
Clojure のように Cを透過的に使えると更に良い
LLVMで作って、静的型付け。
C言語でいいのか?Rust? JS? Java? WebAssembly?
開発ツール
C 言語
LLVM
WebAssembly
JVM
Zig
JSXのようなマクロ
テンプレートで、コンポーネントで、それ自体がデータな性質を持たすことで簡単にマクロを書けるようにする
静的に参照を追跡し、安全に解放する
分岐は問題ない
分岐先でそれぞれ解放すればいい。
ムーブも簡単。
読み込み参照すると、追跡対象が増えるが、追跡できるなら、できる
書き込み参照しても同じ。
データを追跡できるかが鍵
分岐
代入
ムーブ
読み込み参照
書き込み参照
繰り返し
並列性