2011年12月19日月曜日

nullの解釈と部分属性

<2012年1月3日 大幅に修正,加筆>

近代的なプログラミング言語ではもはやnull(あるいはnil)は不可欠の定数であり,組み込み定数として用意されている.しかし,その扱いはかなり恣意的で,プログラマを戸惑わせ,ときにバグのもとになる.

nullは「確定しない(与えられていない,定義されていない,未知の、不明の)」なにかを示すものとされる.nullは,まるで中観派仏教のいう「空」のように,否定型でしか表現されない.非常にわかりにくいが,計算モデルの基幹をなす概念である.

素朴なモデルでは,集合圏 Set において,nullをどの対象(集合)も持つ特別な要素と見なし,あらゆる写像でnullはnullに写像されるとして,圏 1/Set を構成する.つまり,不明なものはどんな処理を受けても結果は不明だと考える."Garbage in, garbage out"である(E.W.Lawvere and S.H.Schanuel : "Conceptual Mathematics - A first introduction to categories,  Cambridge University Press, 1995, p.296参照).しかし,この圏は,始対象 0と終対象1が同型になり,応用がきわめて限られてしまう.

次に,部分射(集合圏では部分写像あるいは部分関数と呼ばれる)によるモデル化の方法がある(Robert Goldbratt著 "Topoi" Dover 1984, p268参照).

集合圏 Set において,写像(射)は定義域の全要素に適用されるが,部分写像はその部分集合にしか適用されない.この部分写像 f を拡張した写像 f を設定し,その定義域 D は f の定義域 D を含むとする,拡張された写像 f  は,集合 D に 対し(1)の定義域にはいる要素 x については,{f(x)}を値とし,(2) それ以外の要素については, 0(空集合)を値とする,というものである.この一般化写像 f は f と圏論的に等価であり,その意味で同等であると見なせる.部分写像 は定義域の要素に対し(1)の場合は「確定」,(2)の場合は「未確定」ということにする.圏の対象の要素とは終対象 1 からその対象への射であるので,その部分射を部分要素と呼び,「不確定」な場合,その値をnullと呼ぶことにする.これが部分射によるnullのモデルである.

しかし,これは「定義されていない(定義域にない)」ゆえの値の不明を示しているものであって,nullのもう一面である値そのものが「未知,未定」のため不明であることを示していない.また,仕組みがかなり作為的である.そこで,より自然で一般的な可変集合の圏をモデルにする考え方を以下に述べる.

2段階の変化を取り入れた「可変集合」を対象とする圏を考える(前掲書Sets For Mathematics p.114参照.この本を以降は略してSFMということにする).SFMでは,段階を「以前previous」と「現在present」と呼んでいる.われわれの場合は,「認識前」と「認識後」の段階を考える.この2段階を対象(Uと1とする)とする圏を 2 とする.から集合圏 Set への反変関手(contravariant functor)を対象として構成する関手圏をSet↑2↑opとする.この対象である可変集合 X は,認識後の段階の集合 Xから認識前の段階の集合 Xへの射である.XX1  は,それぞれ「所与」と「所識」としても良いし,ノエシスによって切り出された対象領域とそれによって得られるノエマとしてもよいだろう.XからXへの射ξは認識されたものが認識対象領域のもののどれに対応するのか,それを与える.すると,xXのある要素とすると,
(1)x =ξx(x1)なるxがある.つまり,xは認識されている
(2)そういうx はない.つまり,xU  は認識されていない.
の二通りがある.

可変集合として,集合1から集合1への射は,Set↑2↑op の終対象 1 であり,1 から可変集合 への射は の「要素」である.Xの要素 xに対しξ(x1) はXU の要素になるから任意のx1に対応するもの,つまり,認識されたものはいずれもの「要素」である.

また,可変集合として,0から0への射は,Set↑2↑op の始対象 0 である.ここへの1 からの射はないので,「要素」を持たない.

では,0から1への射 である U  はどうか,この U 自体は始対象 0 と同様「要素」を持たない.しかし,U から X への射は,X1  の要素の如何に拘わらず,XU  の要素を与える.これは前述の(2)にあたり,認識対象のなにかはあるのにそれが認識されたもののなにかとして得られない(未知),逆に,認識されたものの集合(X)の要素(0からの射で決まる.つまり,特定できない)がなんであろうと,認識対象の集合(X)のいずれかの要素が指定されるため,認識結果の要素が認識対象の要素のいずれに対応するのかが特定できず,その意味で不確定である.

対象 X の要素 x がX の「部分(part)」 A のメンバーであるかどうかを判定するものを特性関数(characteristic function)といい,その値は「真理値」と呼ばれる.通常の集合圏では,この値は 0と1で,0への射をfalse, 1への射をtrueというのが慣わしである.Set↑2↑op では,真理値は 認識前の段階では 01の2値,認識後の段階では 01に加え,がある(SFM p.117参照).認識後の段階で になるのは,x1 が A1 の要素ではないのにX1の要素であり,それが変換された xUAUの要素である場合である.つまり,x はAに入るメンバーなのかどうかなんとも言えないことを示す.この場合,真理値をnullということにする.真理値がtrueの場合,A のメンバーであるとするのに対し,nullの場合を「nullメンバー」であると言うことにする.

可変集合圏での属性について考える.X の属性 p を Y への射とする.X の要素とすると,Y の「部分」である p(X ) に対し,p(x)がそのメンバーであるかどうかを特性関数で見る  .すると,値がtrueならばp(x)が属性値だが,nullならば,属性値は不明であると言うことになる.一方,p の定義域 X がある対象Xの「部分」であるかどうかを見るxの真理値がtrueならば対象 Xを定義域とする属性p の適用対象となるが,nullならば,p がxに適用可能かどうかが不明となり,その意味で属性値は不明,ないし不確定と見なされる.


Rubyの組み込みクラスにNilClassがある.これは特異な可変集合U に相当する.インスタンス(「要素」)はないものの,インスタンス属性は与えることが出来る.属性 を U からX への射 としたとき,は(前述のようにXの要素に関わりなく) Xの要素を値とする.NilClassの属性として便利なのは型変換である.たとえば.to_s(文字列に変換)など.この属性 to_s を X1 = 0, XU = {""} なる X への射とすれば,xの値をnull(つまり,部分要素)としたとき,x.to_s は ""(長さ0の文字列)となる.R3ではコーディングの便宜を図るため,NilClassにto_a, to_hash, to_s,などのインスタンス属性(メソッド)を追加しているが,このモデル化のなかでは体系としての一貫性を壊さない.Railsでは実際,Viewの作成時にnullを "" に変換している.

クラスの属性の「継承」についてどう考えるか.OOPのクラスはノエシスによる対象領域の切り出し(関心の的となる領域)と認識対象とするものの集まり(ノエマ)の2重構造を持つと考えるならば,圏 Set↑2↑op の「対象」に対応付けすることが出来る.上位クラス(に相当する対象)の認識後段階の集合をX10 ,認識前段階の集合をXU0 + ΣXUi ( i = 1...n),下位クラス(に相当する対象)の認識後段階の集合を X10X1ii = 1...n),認識前段階の集合を XU0XUii = 1...n) とする.(Σは集合の総直和を,+ は直和を示す).上位,下位クラス全体を包摂する拡大対象 X として,X1 = ΣX1i ( = 0..n),XU = XUi( = 0..n)とする.各クラス(対象)の属性はいずれもその定義域を拡大対象 X とみなす.各クラス(に相当する対象)いずれも X の部分であるから,X1 の部分の真メンバーならばそのクラスに定義された属性は適用可能であるが,nullメンバーならば,(上述のように)その属性値は不明ないし不確定とされる.ただし,上乗り(overriding)した属性は上位クラスの属性とは別物とみなす.このように考えれば,上位クラスが複数の場合も,また,階層が多段の場合も同じである.

これまではインスタンス属性の扱いについて述べてきたが,クラス属性(メソッド)についてはどう考えるのか.Rubyの場合,各クラスは,Classという名のクラスのインスタンスである.したがって,クラス属性はClassの属性の一つであり,ただし,そのクラス(Classのインスタンス)についてのみ適用可能で,他のクラスには適用不能である.一般的にあるクラスのあるひとつのインスタンスのみに定義される属性(メソッド)をRubyではsingleton属性(メソッド)と呼び,クラス属性は,各クラスをクラスClassのインスタンスとして,その一種である.要するに,認識後段階の集合がひとつの要素からなる拡大クラスの「部分」に対して定義された属性である,とみなせる.

以上のような議論は,素朴な集合の上では十分に進めることが出来ない.圏 Set↑2↑op は比較的簡単なトポスであるが,
  • 真理値は二つではない
  • 選択公理(axiom of choice)を満たさない
という通常の集合とは大分異なる性質を持つ.

R3の体系では,このような可変集合の圏によるモデル化によってnullを解釈する.これから述べるように,このモデル化の利点はほかにも多い.

2011年12月14日水曜日

実体についての事実を読み出す

われわれの圏はトポスであると仮定する.そこでは,始対象initial object終対象terminal objectを持つ.集合圏では始対象は空集合,終対象は要素が一個の集合である.記号でそれぞれ,01で表す.あるモデルクラスCのインスタンスは,1からC(クラスCをモデル化した圏の対象)へのある射で表される.これをx としたとき,1.x Cでのxの値を表記するものとする.クラスCのある属性をattrとすれば,表記 1.x.attr はその値を表すものとする.これがOOP表記での x.attr に対応することは明らかであろう.属性がメソッドの場合は,attrの部分を関数表記するだけである.通常の圏論では射を矢印で表し,その名前を矢印の上に書くが,表現力の弱いHTMLでそれを表すのは面倒なので,以降,このようなドット記法を使うことにする.

圏では射の合成は射である(という約束)から,属性をたどることでクラスを渡り歩くことが出来る.また,トポスであることを仮定しているから,属性の直和,直積を作ること,および部分対象,ベキ対象を作ること,などが出来,さらに分配則が成り立つ.トポスを仮定することによって,したいこと(たとえば関数を使うメソッドの導入など)がかなり自由に出来るだけでなく,集合概念をよりダイナミックなものにしてくれるのである(後述).

先述したように,あるモデルクラスの属性すべてを各属性射の直積として表すことが出来る(より一般的には,直和の直積として表せるが,詳細は後述予定).それをAttrとしたとき,その値は,組tupleで表される.モデルクラスの属性のうち,(計算の都合により設けた属性ではなく)実世界での実体の属性と見なされるものは,事実属性(fact attributes)と呼び,それらの直積をFactとする.FactのグラフがDBのテーブルであるから,テーブルは,<x, x.Fact>の表記の集合である.ここでxは変動する実体のある時点での状態であり,x.Factは事実属性の値の組である.実体自体の識別子run_idや状態の時点の指定はメタデータで行われる.

このようにして,ある実体のある時点における状態の事実をDBのテーブルを読み出すことによって知ることが出来る.

R3は,この考えに基づき実体の事実を記録し,読み出す機構としてRDBを利用する.伝統的RDBは,実体の属性を表すものという基本的な概念がなく,任意の対象の関係relation(つまり対象の直積)表現として考えられたため,適用の自由度が大きすぎ,その結果,実装の場でさまざまな問題を生じ,それを避ける(苦肉の?)手段として,いくつもの正規形条件と呼ぶ制約が考えられている.上述のようにテーブルをFactのグラフとして考えるならば,これらの条件の本質部分はおのずから満たされるため,実装者が考慮すべき条件としては無意味になってしまう.しかも,モデルクラスとの自然な対応があるため,いわゆるO/R マッピングの不整合問題はほとんど生じない(継承などでは不自然で技巧的な処理が必要).

2011年12月6日火曜日

互いに関係し合う実体

ある対象世界を想定するとき,そのメンバーである実体は互いに何らかの形でつながり合っている.このつながりをどう考え,どう表現するか.OOPでは,あまり明示的に指摘されたことはないが,オブジェクトの属性attributeがそのオブジェクトと他のオブジェクトとのつながりを示している.

OOPではメソッドmethodは属性とは別物として扱われるが,属性を値が定まったメソッドの縮退と考えれば,両者は本質的には同じで,われわれはこの二つを合わせて属性と見なす.

このように見なしてもやはりOOPの「属性」とは何なのか,実は判然としない.というよりどのOOP論でも「属性」とはなにかについて,多くを語っていない.OOPのもどかしさは,属性に限らず,このような概念定義の曖昧さにある.たとえば,オブジェクト,クラス,そのインスタンスなど,そして特に属性について明確な数学的モデルを与える議論があるのだろうか(ご存じの読者は是非教えてください).多くのOOP論はいきなりそれらの計算機構について述べるだけである.Haskellなど関数型言語が組み合わせ論理combinatorial logic と表示意味論denotational semanticsによって,また,Prologが述語論理によってモデル化されるのとは大違いである.しかし,これらの理論はいずれも計算機構を基礎づけるものであって,実世界との関連付けは希薄である.

このブログの筆者はこの問題に本格的に立ち入る力量に欠けるため,圏論による略式議論にとどめる.圏論は,主として,E. W .Lawvere and R. Roseburgh:  "Sets for Mathematics"(Cambridge Univ. Press 2003)による.

まず,OOPのクラス(またはプロトタイプ)を圏の対象objectとする(OOPのオブジェクトと混同しないこと).イメージとしては集合として良いが,じつは後述するように集合をより一般化したTopos対象(別名,「動く集合」)とするのがよりふさわしいと考える.そのインスタンスは(圏用語での)要素element である.そして,属性射(mapping または arrow)とする.ただし,OOPでいうsetter属性(値を代入する属性)は別で,属性射への作用actionである(後述予定).射の行き先は対象であるから,あるインスタンスの属性の値は(その写像である)あるクラスのインスタンスになる.

なおサブクラスはしばしばクラスの部分集合だと思われるが,それは間違い.単に属性を共有する別対象であると考えないと体系にほころびが生じる(詳細は後述予定).

属性をもたないクラスのインスタンスはリテラルで表記する.たとえば,数クラスの 0.リテラルの識別子は自身の表記である.属性を持つクラスのインスタンスはその識別子(代理識別子)で表記する.

これまで実体と呼んできたものはあるクラスのインスタンス(要素)に対応する.属性を持つ実体の場合,その属性値は,上述の定義により,他の実体である.つまり,実体から実体へのつながりを与えるものが属性である.この実体に対応するインスタンスを持つクラスはMVCアーキテクチャのモデル部で定義され,モデルクラスと呼ぶ.リテラルとして表される実体は,明示的には属性を持たず,したがって変化もしない.「定実体」とでも呼ぶべきものである.定実体のクラスとして,たとえば色とか,誕生日などが考えられるが,通常はこれらをモデルクラスとしてあらためて与えることはしない.しかし,それらを表記するリテラルのデータタイプが暗黙のうちに与えられている.たとえば,整数,文字列,時間などである.定実体からなるモデルクラスは,これらに対し単射injective mappingを持ち,それは暗黙の属性であると見なすことが出来る.

モデルクラスごとにそのインスタンスに対応する実体の事実factを記録するDBのテーブルが与えられる.DBの各列columnは属性に対応し,各行が実体(のある時点における状態)の事実として属性値を示す.値表記は,上述のように,リテラルないし識別子(DBでいう外部識別子)である.ここで,重要な注意.われわれの実体は変化する.したがってここでいう実体の識別子はそのrun_idであって,通常のidではない.このrun_idを外部識別子として使うことによって変化するものを一貫性のあるものとして捕らえることが出来る.

これらのデータに加えてsince, tillほか各種のメタデータが与えられる.またテーブルは,属性をまとめた射(直積射)のグラフgraph(前掲書p.63参照)の表記である.

圏によってOOPをモデル化すると,議論がより明確になる.とくに属性が射の値域対象であるとすることにより,属性が実は実体をつなぐものであるという位置づけが明確になる.ギリシャ哲学以来の実体とその属性の意味合いとはずれているかも知れないが...なお,前掲書では,対象Xから対象Yへの射を対象Xの属性(property)と呼び,属性についてわれわれと同じ見方をしている.

メタデータは本来の意味での属性を示すものではないが,計算処理では(一定の注意のもとに)属性の一種として扱って差し支えない.このことは圏論による精密な議論で裏付けられるが,このブログでは詳述を避ける.

このようにして,R3は,実体が互いに関係し合いながら変化する実世界を対象とし,それを描写する.

2011年12月4日日曜日

変化する実体情報を処理する作用とrunオブジェクト

R3(Running Ruby on Rails)は,実世界の記述と情報処理を対象とする情報システムを目指す.そんなことは当然どんな情報システムでもやっていることではないか,と思われるかも知れない.しかし,プログラミング言語のパラダイムとしてあげられる「手続き型」,「関数型」,「論理(関係)型」さらにはOOPでさえ,その実体は,計算のパラダイムであって,実世界をどのように見るか,という世界観としてのパラダイムではない.このような言語に基づいて作られる情報システムは,自ずから計算機構の世界を対象として作られ,そこから実世界への対応を考えるようになる.これでは視座の基点が逆である.まず実世界を見定め,その機構を計算の機構に対応させるのが順当である.

OOPで見てみよう.オブジェクトの概念は計算の機構として考えられる.プログラマは自分が実現したい情報処理を構築するためにこの機構を利用する.従って,そうして作られたオブジェクトは必ずしも対象実世界のオブジェクト(「何か」)に対応しない.しかし,実世界オブジェクト(対象実世界に想定されるオブジェクトに対応する計算機構としてのオブジェクト)の処理・加工が実世界を対象とする情報システムの本来の役割であり,その一貫性が保持されなければならない.一方,他の計算の都合上作られるオブジェクトはいわばヘルパーであるにすぎない.

実世界オブジェクトを,今後,実体entityと呼ぶことにする.「実体」という語は哲学用語として大昔から使われ,その意味合いも曖昧,かつ多義であるが,ここでは,とりあえず,実世界で人によって認識され,想定される「なにか」を指すものとし,後により具体的に述べていく.

情報システムのユーザ(クライアント)は,実体への作用actionを情報システム(サーバ)に要求する.情報システムはそれに応えて応答する.

この作用は,実体についての情報を,「得る(Read)」,「新たに作成する(Create)」,「更新する(Update)」,「消去する(Delete)」の4種に分類できる.しかし,これを総称してCRUDと呼んだDBでは,作用の対象はDBのデータであって,RDBのようにオブジェクト指向性が乏しいシステムでは本来的に実体との結びつきはない.実世界を対象とする立場に立つとき,DBのデータは実体についての事実を示すものと考えるから,そこではじめて作用対象が実体情報であるとして,実体と結びつく.

このようにCRUDを見直したとき,それぞれの意味合いをより明確にする必要がある.実体は実世界のなかで生まれ,変化し,そして消滅する.情報システムのモデル部(+DB)はこの生涯を反映するものでなければならない.そのうえで,各作用の意味合いを次のように定める.
  • Read・・・・指定された時点での実体情報を得る.
  • Create・・・実体情報をあらたに作る.
  • Update・・・実体の変化を反映するべく,実体情報を更新する.
  • Delete・・・実体の消滅を反映するべく.実体情報を更新する.
すなわち,単なるデータの読み取り,作成,変更,削除,ではなく,実世界の現象に対応する情報作用であるとする.

じつは,この考え方はなんら新しいものではなく,昔から体系が整備された文書では当然のように行われている.たとえば,戸籍謄本.誕生,結婚,死去が記録され,取り寄せればその時点での状態情報を得ることが出来る(それに対し,現在の多くの情報システムは,Updateでは上書きし,Deleteでは文字通り削除してしまう.こうして無数の貴重な記録が消されている).

Create, Update, Deleteはいずれも新情報を送り込むものであり,これをまとめてPutと呼ぶことにする.従って,R3での実体に対する作用はGet(Read)とPutのみであり,PutはCUDに再分類される.

HTTP1.1では,主として通信の効率性の観点から,Get, Put, Delete, Post の4種の作用に整理しているが,R3のPutはこのPutではなく,むしろPostである.すなわち,HTTPのPutやDeleteとことなり,われわれのCUDはいずれもベキ等(idempotent)作用ではなく,2度以上繰り返せば,一回の場合と結果が異なる.ついでにいえば,HTMLにはHTTPのPutとDeleteに対応するメソッドがないから,このベキ等性を活かそうとしても活かせない.さらに,HTMLのGetメソッド自体にはHTTPのGetの規制,つまりシステムに副作用を与えないという規制,はないから,それに付随させるパラメータの解釈次第でたとえば,deleteさえ行わせることがある.実際,初期の(多分現在でも)多くのウェブサイトで同様のことが行われた.このような非一貫性は,効率はおろか,セキュリティなどにも悪影響を及ぼし,寄せ木細工のウェブシステムが起こす混乱の一例となっている.

作用の対象として,個別実体と個別実体の集まりcollectionの2種がある.Getは両者を対象としていて,検索結果の一覧を得る場合は集まりを,詳細を見る場合は個別実体を対象とするのに対し,通常のウェブシステムでは,Putは個別実体のみ対象とする.Railsも例外ではない.そのために,複数個の実体データを追加や更新,削除する場合など,一貫した方式が与えられず,プログラマのアドホック処理に任されている.プログラミング初心者の多くがつまずくところである.R3のPutは両者を対象とすることが出来る.

しかし,話を簡単にするために,個別実体のみを対象とすることにする.個別実体をPutするということは,個別実体の変化をシステムに告知し,その記録を残すことを要求することである.ここで2種類の時間間隔のデータ(メタデータ)が必要になる.ひとつは,実体について事実データが妥当(通用)する期間であり,ひとつはその記録がいつ作られ,いつ(見かけ上)削除されたか,その期間である.前者を[since, till]で表し,後者を[created_at, deleted_at]で表し,事実データと横並びで記録する.R3では,前者をvalid_time, 後者をrecord_time(通常の時変動データベースではtransaction_time)と呼んでいる.

実体の各状態(についての事実データ)をすべて記録として残すと言うことは,自ずから追記型データベース(append only database)にするということである.状態を示す事実データと2種の時間データ(および後述するさまざまなメタデータ),それに行の識別子としてid(これもメタデータ)をつけてRDBでの行columnとする.つまり行はある個別実体のある状態を表す.さらに重要なメタデータとして,肝心の個別実体の識別子を与えなければならない.R3ではこのように変化する個別実体をrunと呼び,その識別子をrun_idと呼ぶ.runはR3が用意するモデル部のRunクラスのインスタンスである.個別実体はこうして,同一のrun_idを持つ一連(英語で言えばrun)の行によって記録され,その各行はその実体の状態をあらわす.

R3が実装する基本的な作用として他に,

  • Merge ・・・実体を合併して新しい実体とする.
  • Split    ・・・実体を分割して複数の実体を作る.

がある.これらもPut作用であるが,先のCUDと異なり,複数の個別実体が関係し,作用を記録するための特殊なメタデータが必要である.それぞれ,merged_toとsplit_fromという名前のメタデータに対し,作用後,作用前の実体の識別子を値とすることでこの作用の記録とする.

Putによる副作用によってシステムの状態自体が変化するが,これらの個別実体の記録によってその変化はすべて記録として残される.したがって,任意の時点におけるシステム記録を見ることが出来.R3のGetの大きな特長となっている.この見る時間をshow_timeと呼ぶ.デフォールトは実時間の現在に設定されるが,それを変更する仕組みが作られていて,設定変更されるとそれ以降は実時間に比例してshow_timeが変動する.比例係数は任意に与えられ,負でも良い.この場合時間の逆戻しになる.しかし,その応用はいまのところ,思い付かない.

show_timeとは別に,個別実体のいつの時点での状態を見たいのか,その時間をassert_timeと呼ぶ.assert_timeは時間間隔としても与えることが出来る.assert_timeはグローバルにも,また局所的にも与えることが出来る.

もひとつ,action_timeがあり,これは作用を実行する時点の時間である.通常は実時間の現在であるが,後述するシミュレーションモードでshow__timeを変更した場合はそのshow_timeに連動する時間がaction_timeとなる.

ここで,時刻を示す定数Past,, Present, Futureを導入しておきたい.Pastは任意の時刻より小さく,Futureはその逆,Presentは実時間現在の時刻で絶えず変動する.プログラミング言語やSQLには時間型があるが,この定数定義に相当するものはいずれもない(そもそも無限大や無限小を定数として持つプログラミング言語はあるのだろうか.あれば非常に便利なのだが).そのため,PastとFutureに人為的な時刻(たとえば,9999年12月31日23時59分59秒)を無理矢理与えなければならず,そうすればそれに伴うトラブルが発生する.こんなところにも実世界を記述しようとせずに計算(数値)の世界のみを見ている現代情報処理の欠点が現れる.


runに対するCUDの各作用に対してDBでは次の操作を行う.

  • Create ・・・最初の状態の事実データを記録する行(状態行)を新たに作る.idは行識別子.run_idは便宜上このidに等しくする.sinceとtillはクライアントによって与えられるが,デフォールトとしてsinceはcreated_atと同じ,tillはFuture.created_atはaction_time,deleted_atは未定(null.便宜上Futureを使用している).
  • Update ・・・既存のrun記録(同じrun_idを持つ行)に対し,与えられたsince以降の区間(tillをFutureとする)の状態行をDelete(後述)する.続いて,新状態の事実データを持つ行をこのrunに追加する.すなわち,idは新しく,run_idは同じ,sinceとtill, created_atとdeleted_atはCreateと同じ.
  • Delete  ・・・実行時間(action_time)以降の状態が消滅したことを記録する.すなわち,action_timeからFutureの区間にかかる状態について,(1)まるまるその区間に入る状態のdeleted_atをaction_timeとする.(2)sinceとtillの間にaction_timeが入る状態については,その状態のdeleted_atをaction_timeとし,sinceは同じでtillをaction_time-1(量子化時間)にする状態を追加する(削除しすぎた部分を復活させる).
R3では,クラスRunに クラスメソッドcreate_run!, そして,インスタンスメソッドとしてupdate_run!, delete_run!を設けている.

新規に個別実体(entity)が作られたときには,DBに記録される前にビュー,コントローラ,モデルの各部で処理の対象にされる.その扱いをどうするか,この部分も多くのウェブサイト技術では触れられず,Railsでさえ,不十分である.記録される以前の個別実体の識別子として(ユニークでさえあれば何でも良いが)便宜的にUUIDを使用する.この実体の状態を示す事実データは空白,あるいはデフォールト値である.既存の実体を扱うときにはDBでの識別子を使用する.

実体識別子(entity_idと呼ぶ)は,システム全体の処理を通して一貫して参照される.これが前回指摘したオブジェクトを一貫させるための仕組みである.たとえば,ビューでは,対象個別実体のビューをあらわすDOMオブジェクトの属性としてentity_idを持つ(もちろんDOMオブジェクトの標準属性であるid属性を使用してしかるべきだが,CSS表記の技術的問題があって,この非標準の属性を持たせている.これも寄せ集め技術の弊害).

じつは,R3では,個別実体に対し,以上の識別子にさらにそのタイプ(モデルのクラスに対応)を追加して識別子としている.その表記は "#{entity_id}:#{type}"とする.ここで,#{...}は,値を文字列に変換するRubyの表記法.タイプ理論でしばしば使用される表記法である.この表記による実体識別子はR3システム全体を通してユニークであり,様々な処理を通して実体参照に一貫性を持たせることが出来る.