2023 03 19 - qnighy/umo GitHub Wiki
近況
let-inを導入した。また、以下の最適化を導入した:
- 変数名の事前解決
- なるべくmoveを使うようにする
当面はこれくらい最適化されていれば常識的な速度では動くと思う。
もしこれでも足りなかったらVM化してTCOあたりを入れるなどもありそうだけど、本命はコンパイラなのでここではあんまり本気を出す必要はない。
今後のプラン
ビルトイン関数とlambdaを実装して、チューリング完全な状態にする。入力をバイト列として受け取れるようにする。
非決定論とMap, Set, floatの同一性
非決定論的な挙動はしばしば意図せず発生し問題を起こすので、明示的に必要な場合を除けば避けるようにしたい。
非決定論的な挙動とはつまり、挙動がpowerset monadであらわされるということで、これは典型的な副作用のひとつといえる。逆に非決定論的な副作用を持つシステムのなかで、ある関数が決定論的であるというのは、全ての入力に対して出力が一意になることとして定義できる。
この決定論性を議論するには、値の同一性を先に定義する必要がある。
値の同一性には色々な基準がある。ここでは型に対して標準的な同一性をひとつ定めることにしたい。標準的な同一性は反射推移対称的である必要があり、標準的な比較演算子は標準的な同一性に準拠している必要がある。すると以下のようなケースが問題になる:
- Map, Setの順序
- floatの-0.0とNaN
- 関数の同一性
Map, Setについては以下の3つを分けて提供したい:
- キーに固有の全順序でソートされているもの
{ foo: 1, bar: 2 } == { bar: 2, foo: 1 }
になる- 必ず
["bar", "foo"]
の順序で返される
- 順序をもたないもの
{ foo: 1, bar: 2 } == { bar: 2, foo: 1 }
になる- 列挙は非決定論の副作用を持ち、順序は保証されない
- 順序をもつもの
{ foo: 1, bar: 2 } != { bar: 2, foo: 1 }
になる- 前者の列挙は
["foo", "bar"]
を返し、後者の列挙は["bar", "foo"]
を返す
既存の言語の多くは2または2と3の中間になっている。2と3の中間になっているものは、同等のオブジェクトだと判定されるにもかかわらず内部状態により処理結果が違うということがありえる。
たとえば、package.jsonのexportsフィールドはキーの順序依存性がある。このことが比較においては明示的に扱われておらず、ここに微妙な非一貫性がある。
そして、これはちゃんと明示的に扱えるほうがいいと思っている。
……とはいえ、 { foo: 1, bar: 2 } != { bar: 2, foo: 1 }
は多くのプログラマにとって非直感的な挙動で多くのトラブルを生みそうな面もある。名前のつけ方が大事かもしれない。 KeyedArray
とか?
floatについてはML系言語で見られるように ==
と .==
を別で提供し、floatを ==
で比較しようとしたときはlinterで警告を出すのが良いだろうか。
(あとで関数とuseCallbackについて書く)