設計
金額を書くと Decimal になります
ソースに 1234.50 と書けば、それは Decimal です。二進の浮動小数点ではありません。Float を使いたいときは 1234.50f と書きます。
(0.1 + 0.2) printNl. "0.3"
(1.00 / 3) printNl. "割り切れないのでエラー"
((1.00 / 3) roundedTo: 2) printNl. "0.33"
割り切れない除算はエラーになります。 丸めるときは、小数第何位で、どちら向きに丸めるかを書きます。roundedTo: truncatedTo: floorTo: ceilingTo: awayFromZeroTo: があります。割り算と丸めを一度に書く dividedBy:roundedTo: もあります。
Int は BigInt です。受注番号が 253 を超えても桁は落ちません。
日付は暦の日付です
Date は時刻も時間帯も持ちません。new Date('2026-08-10').getDate() が東京で 9 を返すような事故は起きません。
計算は暦のとおりです。1月31日の一か月後は 2月28日(閏年なら29日)になります。
時刻が要るときは Instant を使います。Date との行き来には時間帯を書かせます。時間帯のデータベースは持っていないので、任意の地域名は扱えません。
JSON の数値は桁が落ちません
JSON.parse は 1234567890123456789.12 を 1234567890123456800 にします。金額を Decimal にした意味が、通信一往復で消えます。
そこで JSON は自前で読みます。数値は、型を聞かれるまで届いた文字のままです。
(j at: 'total') asDecimal "Some(1234567890123456789.12)"
at: は例外を投げません。オブジェクトでないものに送ると null の Json が返るので、(j at: 'a') at: 'b' のように途中を確かめずに辿れます。型を決めるのは葉だけで、そこで Maybe が返ります。読めなければ none です。
型注釈は書かなくても構いません
型推論は Hindley-Milner です。注釈は書けますが、必要ではありません。サブタイピングはありません。
メッセージ送信の型付けは三段階です。受信者の型が分かっていれば、その場でセレクタを引いて検査します。まだ決まっていなければ保留し、他の場所でその型が決まった時点で検査します。最後まで決まらないものは、そのまま通します。
三段目が、意図して残してある穴です。ただし、ここを通るのは実行できないコードだけです。 その送信に到達するには型の決まらない値が要りますが、値を作れば型が決まり、決まった時点で検査されます。
クラスは名前で区別します。継承はありません。 共通の振る舞いは、プロトコルを組み合わせて持たせます。
実装の書き漏らしはコンパイラが止めます
case はありません。「このどれか」を書くときは、種類ごとにクラスを作り、プロトコルで答えるべきものを並べます。
Protocol subclass: #Status requires: (#label #badge #next).
名乗ったクラスが一つでも書き忘れると、組み立てが止まります。
Cancelled claims protocol Status but does not implement "colour"
直和型と case のある言語は、種類を足したときに分岐の書き漏らしを教えます。この言語は、種類を足したときに実装の書き漏らしを教えます。 数える向きが違うだけで、どちらもコンパイラが数えます。
スレッドを跨げる値、跨げない値
ActorRef には、そのアクタがどこで動くかが書かれていません。同じスレッドにいても、Web Worker の上にいても、書き方は同じです。だからメッセージが運ぶ値は複製できなければなりません。
ブロックは複製できません。作られた場所を閉じ込めているからです。ブロックを持った状態を Worker に置こうとすると、原因の項目まで示してコンパイルが止まります。
Decimal を JavaScript の関数に渡す宣言も、コンパイルエラーです。JavaScript に小数がないので、桁を捨てるか計算を捨てるかを、呼ぶ側が asString か asFloat で選ぶ必要があります。
検査は毎回、全モジュールをまとめて行います
imports: で繋がったモジュールは、まとめて解析して、まとめて推論します。分けて検査する仕組みはありません。
理由は速さではなく、三つの検査が全体を見ないと成り立たないからです。プロトコルの署名は実装クラス全部から作ります。become: で何を保つべきかは、その参照がどこで作られたかを全部集めて決めます。スレッドを跨げるかどうかは、メッセージが運ぶ値の内側まで辿ります。
速さは問題になりません。 一つの画面は一つのファイルとその部品です。223行の受注画面では、検査にかかった時間が 0.000 秒と出ました。全社の画面を一本にまとめた 81,000行でも 1 秒で、行数に対して線形です。