container/adapters の設計

設計目標

container パッケージは、どの具体的な型にも依存せずに能力を定義します。リポジトリ自身の型に対する辞書は誰かが提供しなければならず、そのコードは能力のレコードとすべての具体的パッケージの両方を参照する必要があります。その場所が container/adapters です。適応対象のすべてに依存する末端パッケージであり、これによって他のパッケージはそうした依存を持たずに済みます。

数学的背景

アダプタは、具体的な型がコンテナモデルの抽象的なオブジェクトを 表現 していることの根拠です(container の設計 を参照)。型 V について、アダプタは length と get を通じて表示 ⟦⋅⟧:V→([n]→T)\llbracket\cdot\rrbracket : V \to ([n] \to T) を与え、可能な場合は tabulate を通じてその切断を与えます。

⟦tabulate(n,f)⟧=f∣[n].\llbracket \mathtt{tabulate}(n, f) \rrbracket = f|_{[n]} .

ビューの場合、表示は形状 (r,c)(r, c) の元の行列 AA から計算されます。

⟦rowi(A)⟧(j)=Aij,⟦colj(A)⟧(i)=Aij,⟦AviewT⟧(i,j)=Aji.\llbracket \mathrm{row}_i(A) \rrbracket(j) = A_{ij}, \qquad \llbracket \mathrm{col}_j(A) \rrbracket(i) = A_{ij}, \qquad \llbracket A^{\mathsf T}_{\text{view}} \rrbracket(i, j) = A_{ji} .

ビューには tabulate がありません。構築しようとすると元の行列を作らなければならず、その結果はもはや呼び出し側が持つ何かのビューではなくなるからです。ビューの可変な編集は AA に書き込みます。これこそがビューの目的です。

設計上の判断

独立した末端パッケージ

問題。 container にアダプタを含めると、immut、mutable、backends/default に依存することになり、能力のレコードだけが欲しい外部ライブラリまで、すべての具体的な型を取り込むことになります。

決定。 レコードとアルゴリズムは error にだけ依存する container に置きます。アダプタは container/adapters に置き、これは container、immut、mutable、backends/default に依存します。リポジトリのどのパッケージもアダプタには依存しません。

理由。 こうすると依存は特殊なものから一般的なものへ向かいます。外部ライブラリは 統合ガイド に従い、container だけを相手に独自の辞書を公開します。

型と能力ごとに 1 つのファクトリ

各ファクトリは <type>_<capability>_ops という形の名前を持ち、新しいレコードを返します。自由な型パラメータを持つ総称関数は値として保存できないため、「すべての T に対する @immut.Matrix[T] の読み取り辞書」を提供するにはファクトリ関数を使います。読み取り、構築、編集は要素を調べないので、ファクトリは T に制約を課しません。

編集モデルは所有に従う

不変な型とそのラッパーには永続的な編集を、可変な型・ビュー・可変なラッパーには可変な編集を提供します。可変な行列に永続的な編集を提供すると set ごとに全体のコピーが必要になり、O(1)O(1) に見える呼び出しの裏に O(rc)O(rc) のコストを隠すことになります。不変な行列に可変な編集を提供することは、その値の意味論を壊さない限り不可能です。

委譲する前に検証する

具体的な型は不正なインデックスで中断します。各アダプタはまずインデックスや形状を検査してエラー値を返すので、呼び出すメソッドが中断しうるにもかかわらず、辞書は container の panic しない契約を満たします。

正しさと不変条件

  • すべての読み取りアダプタは、有効なインデックスでは get(v,i)=Ok(v[i])\mathtt{get}(v, i) = \mathrm{Ok}(v[i]) を、それ以外では IndexOutOfBounds を満たします。ビルダーは上記の tabulate/get の法則を満たします。
  • ビルダーは初期化関数を各要素についてちょうど 1 回、行優先順に呼び出します。
  • 永続的な編集は引数を決して変更しません。@immut の更新は永続ベクトルのパスコピーです。
  • ビューの可変な編集は元の行列だけを変更し、それ以外は変更しません。
  • すべてのファクトリは O(1)O(1) で実行されます。辞書の呼び出しのコストは元のメソッドのコストと同じです(@mutable の読み取りは O(1)O(1)、@immut は O(log⁡32n)O(\log_{32} n))。

却下した代替案

  • 各具体的パッケージの中にアダプタを置く。 そうすると immut と mutable が実験的な container 層に依存することになり、安定した具体的 API がその不安定さを受け継いでしまいます。
  • 外部の型に対するアダプタ。 このリポジトリには自身が保守する型のアダプタだけを含めます。外部ライブラリは自身のアダプタを所有します。

境界

このパッケージは能力もアルゴリズムも追加せず、既存の型に対する根拠だけを提供します。ビューに対する構築辞書、可変な型に対する永続的な編集、このリポジトリ外の型に対するアダプタは提供しません。取り下げられた OpenBLAS バックエンドのアダプタは、そのバックエンドとともに contrib/openblas_backend に保存されており、このパッケージには含まれません。