単位ミスが招く重大事故
「マーズ・クライメイト・オービター」をご存知でしょうか。火星の気象調査をミッションとして、NASAが1998年に打ち上げた探査機です。1億2500万ドルを費やして作られたこの探査機は、本務に就く前に火星周回軌道内で炎上し、そのまま消息を絶ちました。原因はヤード・ポンド法単位とメートル法単位の取り違えミス。
かの有名な「もう助からないゾ♡」の事故もやはり単位系間の変換ミスが発端でした。このように、不適切な単位の取り扱いが悲惨な事故に繋がる例は枚挙にいとまがありません。
プログラミングにおいても同じことが言えます。接頭辞の変換を忘れたり、numpyの三角関数にradianではなくdegreeで角度を与えて時間を溶かした人は私だけではないでしょう1。人類が安全に数字を取り扱うためには、それをサポートする仕組みが必要です。
......そんな感じの問題意識から生まれたプログラミング言語、それが「Numbat」です。
静的な物理次元型付け言語「Numbat」
Numbatは、科学技術計算を安全かつ効率的に行うことを目的する静的型付け言語です。この言語は、物理次元(長さ、時間、質量など)をファーストクラスの型として扱うことで、単位の不適切な取扱いを型エラーとして検出できます。単位変換はすべて自動で行われ、書き手が自分で行う必要はありません。
使用例
オンラインPlaygroundで、インタラクティブにNumbatの挙動を試すことが出来ます。
上記の例から、いくつかの特徴が読み取れます。
-
8 km
のように、数字 単位名
の形で値と次元を定義 -
let ω: Frequency
のように、let x: 型
の形で変数の次元型を定義(型推論も使える) -
1 h + 25 min
のように、単位変換を行うことなく演算が可能 -
ħ ω -> eV
のように、-> 単位
の形で出力単位を指定できる
まず嬉しいのは3と4だと思います。値を与える時と値を取り出す時にさえ単位を指定すれば、その間の変換について一切考えることなく演算結果を得ることが出来ます。
「それって○○と同じでは?」
物理単位の換算を自動で行ってくれるようなライブラリ自体は多数存在します。PythonのPintとか、JulicaのUnitful.jlあたりが有名でしょうか。言語標準の型システム内で物理単位を扱えるという点では、F#
のUnits of Measureも近いコンセプトです。これらと比較した際のNumbatの特徴は以下の通りです。
1. 言語全体が静的な物理次元型を前提としている
単に物理単位を便利に扱えるだけでなく、「数字を扱う際に常に単位を意識させる」ことがNumbatの際立った特徴です。もちろん無次元型(Scalar型)もありますが、その場合も無次元量であることを意識させられる設計になっています。
2. 「単位」ではなく「次元」を型としている
「km/h」のような具体的な単位ではなく「長さ/時間」のような次元として型を定義することで、より汎用性が高く使いやすい型設計となっています。
3. 扱える単位の種類が豊富
SI単位系をはじめとした科学的な単位はもちろん、通貨・画素・人数など、数字にまつわる様々な次元と単位を言語標準の型システム内で扱うことができます。
その他の文法的特徴
F#
とRust
を混ぜたようなシンタックスに、Julia
のような科学ファーストな要素を加えた感じの文法です。
演算子記号
プログラミング言語としては珍しく、×
(乗算記号)や²
(累乗記号)といった、人間にとって自然な演算子記号を使うことが出来ます。もちろん、*
や**
といった一般的なプログラミング言語表現も使えます。
let x: Length = 3 m
let y: Area = 0.5 mm ÷ x⁻¹
print(y ➞ cm²)
# -> 15 cm²
let E_pot: Energy = 80 kg × 9.8 m/s² × 5 m
fn max_distance(v: Velocity, θ: Angle) -> Length = v² · sin(2 θ) / g0
定数
様々な物理・数学定数が標準で使えます。
print(π)
# -> 3.14159
print(pi)
# -> 3.14159
print(avogadro_constant)
# -> 6.02214e+23 mol⁻¹
数字以外の型
汎用プログラミング言語なので、String
やBool
といった基礎的な型はもちろん一通り扱えます。
print("Hello, World!")
# -> Hello, World!
fn step(x) = if x < 0 then 0 else 1
メソッドチェーン
パイプライン演算子(|>
)を用いたイテレータメソッドチェーンが使えます。関数型言語というかF#
の影響を感じます。
[1 m, 1e4 mm, NaN, -inf]
|> filter(is_finite)
|> sum()
# -> 11 m [Length]
ジェネリクス
様々な型に対して適用可能な関数を定義する場合でも、ジェネリクスを使って引数と返値との関係性を縛ることが出来ます。
fn sqrt<D>(x: D^2) -> D
# 引数は返り値の2乗の次元を持つ
fn sqrt<D>(x: D) -> D^(1/2)
# 同上
カスタム単位
自前で次元や単位を定義できます。
unit TokyoDome: Volume = 1_240_000 m³
エラーメッセージ
エラーメッセージが親切なのもNumbatの特徴です。処理系の実装言語であるRust
の影響を強く感じます。
>>> print(planck_leng)
error: while type checking
┌─ <input:35>:1:7
│
1 │ print(planck_leng)
│ ^^^^^^^^^^^ unknown identifier
│
= Did you mean 'planck_length'?>>> print(planck_leng)
今後の展望
現状、Numbatは発展途上(初版が2023年)かつ超マイナー言語ではありますが、やはり「物理次元型を持つ静的型付け言語」という尖ったコンセプトは魅力的で、他言語との連携等によって今後発展することを期待しています。
Reference
- リポジトリ: https://github.com/sharkdp/numbat
- 公式ドキュメント: https://numbat.dev/doc
-
なおOpenCVの三角関数はdegree ↩