プログラミング
Ante: ボローチェッキングと参照カウンティングを融合する新しい方法
Ante: A new way to blend borrow checking and reference counting (verdagon.dev)
要約
Anteは、参照カウンティングとボローチェッキングをランタイムクラッシュなしで融合させる、これまで不可能とされてきた新しいアプローチを提案するプログラミング言語です。RustやSwiftなどの既存言語が抱える参照管理の問題を解決し、柔軟性とパフォーマンスを両立させることを目指しています。特に、シェイプ安定性(Shape-Stability)という概念と、共有ミュータビリティ(shared mutability)のサポートにより、安全かつ効率的なメモリ管理を実現します。
全文翻訳
言語 ∩ アーキテクチャ RSS Github r/Vale Twitter Discord Ante: ボローチェッキングと参照カウンティングを融合する新しい方法 クラッシュなしでボローと参照カウンティングを融合! 2026年6月28日 — Evan Ovadia Anteは、これまで不可能だと思われていたことへの第一歩を踏み出しました。それは、ランタイムクラッシュなしで参照カウンティングとボローチェッキングを融合することです。0 これは非常に有望です。なぜなら、いつの日か、クラッシュのリスクなしに、それぞれの方法が意味をなすときに、より簡単にそれらを使用できるようになることを意味するからです。そのような言語があれば、私は参照カウンティングで柔軟な方法でゲームをプロトタイプし、より高速なボローチェックされたコードに徐々に移行できるでしょう。1 多くの言語が試みてきたにもかかわらず、主流の言語はそれらをシームレスに結合する方法を見つけ出せていません。Rustも試みましたが、Rustの参照カウンティングのRc型を使用しようとすると、しばしばRefCell(Rc<RefCell<Spaceship>>のように)が必要となり、誤って使用するとランタイムでクラッシュする可能性があります。2 3 rustcがコンパイル時に適切なRcの使用をチェックできればと願っています!4 5 Swiftも新しいボローイングシステムで試みましたが、高価なランタイムチェックがあり、誤って使用するとランタイムでクラッシュします。参照カウンティングとボローチェッキングの融合は難しいということが判明しており、その理由は以下で説明します。では、Anteについて話しましょう! Anteは開発中です! この一部は実装されており、一部はまだ理論段階であり、設計はまだ流動的です。これは現在活発に開発中ですので、AnteのサイトとDiscordで進捗を追ってください! サイドノート(興味深い関連する考察) ノート[–] ノート[+] 0 1 2 3 4 5 0 より具体的に言うと、Anteは可変オブジェクトの参照カウンティングとボローチェッキングを、RustのRefCellやSwiftの排他性チェックによるランタイムパニックやオーバーヘッドなしで融合する方法を見つけました。1 これは私がValeに取り組む理由とほぼ同じです! しかし、Valeは世代参照を使用しており、しばしばこのような種類のプログラム停止のリスクがあります。2 例えば、2人が同時にユニークな(読み書き可能な)参照を取得するとクラッシュします。3 また、パニックを起こさないtry_borrow()とtry_borrow_mut()メソッドもありますが、問題が別の場所に移動するだけです。4 そして残念ながら、GhostCell / QCellは当てはまりません。それらは他の制限を伴い、RefCellのドロップイン代替品ではありません。5 Cellはありますが、その内部への参照を取得できません。例えば、&Cell<(T, U)>から&Cell<U>に移行することはできません。Rustは一般的に内部ミュータビリティを使用すると多くの摩擦が生じる傾向があり、多くのユーザーがそれを避けています。
Ante
Anteは、Rustよりもシンプルなシステムプログラミング言語を目指しており、メモリ安全性とスレッド安全性を備えています。単一所有権とボローチェッキングがあり、値はインライン(スタック上、または包含する構造体/配列内)に配置されます。そして、ユーザーがシンプルさを優先したい場合、型にsharedキーワードを使って参照カウンティングを選択できます。例えば、このスニペットは赤黒木をバランスさせます。
ante
// Color can be either an R or a B
shared type Color = | R | B
// RbTree can either be an Empty or a Tree
shared type RbTree t = | Empty | Tree Color (RbTree t) t (RbTree t)
// A balance function, for RbTree of any element type t
balance (tree: RbTree t) {Copy t}: RbTree t =
match tree
| Tree B (Tree R (Tree R a x b) y c) z d
| Tree B (Tree R a x (Tree R b y c)) z d
| Tree B a x (Tree R (Tree R b y c) z d)
| Tree B a x (Tree R b y (Tree R c z d))
-> Tree R (Tree B a x b) y (Tree B c z d)
| other -> other
私は通常Cスタイルの構文を好みますが、これには美しさがあることを認めざるを得ません。そして、Python版と同じくらい小さく、C++版やRust版よりも小さいという簡潔さも兼ね備えています。しかし、私にとって最も興味深いのは、Anteがメモリ安全性に関して行っていることです。Anteは共有ミュータビリティのスーパーパワーを持っています。参照カウントされたデータを可変に借用したい場合でも、ランタイムエラーのリスクを冒す必要はありません。これは主流のどの言語でもできません。RustやSwiftでさえもです。
共有ミュータビリティのスーパーパワーについて話す前に、基本的なことから始めましょう。まずボローチェッキングがどのように行われるかを見て、それから参照カウンティングを組み合わせていきます。
シェイプ安定性
Anteにはシェイプ安定性(shape-stability)という概念があります。これは「安定したシェイプの何かへの参照は、どこでどのような変更が行われても常に有効である6」という意味です。これにより、Anteのコードは、同じ構造体に対して同時に複数の可変ボロー参照を安全に持つことができます。例として、2つのEntityへの可変参照を取るheal関数を示します。
ante
type Entity = energy: I32 health: I32
heal (healer: mut Entity) (target: mut Entity) =
healer.energy -= 10
target.health += 10
Anteでは、healを両方のパラメータに同じEntityで呼び出すことができます。例えば、エンティティが自分自身を回復する場合です。
ante
self_heal (entity: mut Entity) = heal entity entity
ヒーラーを変更しても、Entityへの共有参照を無効にすることは一切できないため7、コンパイラはこのコードを有効とします。言い換えれば、ヒーラーとターゲットが同じEntityを指している可能性があっても、これはメモリ安全です。ここではEntityを破壊するものは何もないため、両方の参照は有効なままです。
次に、もう少し複雑な例を見てみましょう。Anteのコードは、同じ構造体、またはその構造体のいずれかのフィールド、またはそれらの構造体のいずれかのフィールドに対して、同時に複数の可変ボロー参照を持つことができます。例えば、ここではシップを指す可変ボロー参照と、シップのエンジンを指す別の可変ボロー参照を同時に持っています。
ante
type Engine = fuel: I32
type Spaceship = engine: Engine name: String
refuel (ship: mut Spaceship) =
engine_alias: mut Engine = ship.engine
// Can still use original `ship`
ship.engine.fuel := 200
engine_alias.fuel := 100
Anteはこれが完全にメモリ安全であることを知っています。なぜなら、この関数実行中、誰もシップを破壊できず、したがって誰もそのエンジンや燃料を破壊できないからです。言い換えれば、これらの参照はシェイプ安定なデータへのものです。RustとSwiftに詳しい方なら驚くでしょう。Rustでは、同じデータを指す複数の&mut参照を持つことはできません8。Swiftでも、同じデータを指す複数の&mut参照を持つことはできません。
では、ここで参照カウンティングを組み合わせてみましょう! ノート[–] ノート[+] 6 7 8 6 ここで「有効」とは、デリファレンス可能であること、つまりメモリ安全性や未定義動作のリスクなしに参照をデリファレンスできることを意味します。7 Rustで&mut Spaceshipを使って、それが指しているSpaceshipを破壊できないのと似ています。Rustでさえ、同じSpaceshipローカル変数を指す複数の&mut Spaceship参照を持つことは完全に安全です。8 Cellで近いことができる場合もありますが、AnteがRustのCellよりも優れている理由については付録セクションを参照してください。
参照カウンティング
上記の能力は参照カウンティングに完全に適合します。以下で説明します。Anteでは、型定義の前にsharedを置くと、その型が自動的に参照カウントされます9。そして、shared mut型がある場合、set_fuelがSpaceshipのエンジンに対して行っているように、ロックなしでそのフィールドを変更できます。
Engineフィールド:
ante
type Engine = fuel: I32
shared mut type Spaceship = engine: Engine name: String
launch (var ship: Spaceship) =
set_fuel (mut ship.engine)
set_fuel (engine: mut Engine) =
engine.fuel := 100
このスニペットについては後で詳しく説明しますが、今のところは次のとおりです。これはRust版やSwift版と似ていますが、Rustの.borrow_mut()やSwiftの自動ランタイム排他性チェックによるクラッシュのリスクはありません。launchはmut Engineボロー参照を作成できます。なぜならlaunchは、包含するSpaceshipを生存させているため、エンジンが生き続けることを知っているからです。より一般的に言うと、shared mut型のフィールドに対しては、常にmutボロー参照を作成できます。10
ノート[–] ノート[+] 9 10 9 少なくとも今のところは。最終的には、アプリケーション(コードではなく)が、RC、GC、またはカスタムメカニズムなど、メモリ管理方法を設定できるようになります。10 ただし、それらのフィールド内のものに対して常にmutボロー参照を取得できるわけではありません。それには別のメカニズムがあります。さらに