設計

金額を書くと 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.parse1234567890123456789.121234567890123456800 にします。金額を 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 に小数がないので、桁を捨てるか計算を捨てるかを、呼ぶ側が asStringasFloat で選ぶ必要があります。

検査は毎回、全モジュールをまとめて行います

imports: で繋がったモジュールは、まとめて解析して、まとめて推論します。分けて検査する仕組みはありません。

理由は速さではなく、三つの検査が全体を見ないと成り立たないからです。プロトコルの署名は実装クラス全部から作ります。become: で何を保つべきかは、その参照がどこで作られたかを全部集めて決めます。スレッドを跨げるかどうかは、メッセージが運ぶ値の内側まで辿ります。

速さは問題になりません。 一つの画面は一つのファイルとその部品です。223行の受注画面では、検査にかかった時間が 0.000 秒と出ました。全社の画面を一本にまとめた 81,000行でも 1 秒で、行数に対して線形です。