このブログは、旧・はてなダイアリー「檜山正幸のキマイラ飼育記 メモ編」(http://d.hatena.ne.jp/m-hiyama-memo/)のデータを移行・保存したものであり、今後(2019年1月以降)更新の予定はありません。

今後の更新は、新しいブログ http://m-hiyama-memo.hatenablog.com/ で行います。

letマップ付きグラフ

式を名前に束縛することは極めてよくある。letマップとは、この束縛機構を表すデータ構造だとする。ラムダ計算のlet式に相当する。再帰(自己言及)向けのletをletrecとして分けることが多いが、ここでは、letのなかにletrecも含めて考える。

letマップのひとつの項目をletエントリーと呼ぶことにする。letエントリーは、名前とその名前が指す対象物へのポインターリンクから構成される。

例題として次のスキーマを考える。


type Friend = {
"name": string,
"email": string?,
"addr" : string?,
"bestFriend": bestFriendChain
};

type bestFriendChain = {
"name": string,
"bestFriend": bestFriendChain,
}?;

名前 Friend, bestFriendChain に対応する2つのletエントリーも含めて図示すると次のようになる。

ピンクと赤が、名前による参照に関わるのノードと辺。ピンク楕円のノードは、名前参照ノードで、構文的には「単なる名前の出現」に相当する。出現した名前は、いったんletエントリーを経由して他の式を参照することになる。letマップは名前空間と言っても同じである。

インライン・ネーミング

fetch問い合わせ式では、より簡略な表現ができる。


{
"name": string,
"email": string?,
"addr" : string?,
"bestFriend": @= bestFriendChain {
"name": string,
"bestFriend": bestFriendChain,
}?
}

bestFriendChainという名前による参照があるが、参照される先の名前をインラインで定義できる。インライン・ネーミングの構文が @= で、直後の式に名前を与える。この例では、次の部分式に名前bestFriendChainが与えられている。


{
"name": string,
"bestFriend": bestFriendChain,
}?

この部分式内に名前参照bestFriendChainが出てくるので、結果的に再帰構造となる。

インライン・ネーミングの場合も、暗黙のletマップを備えたグラフ構造なのだと考えることができる。式全体には名前が与えられてないが、空白の(あるいはデフォルトの)letエントリーが式全体を指しているとする。以下の図となる。

サイクリックグラフ

空白のletエントリーは省略して、letマップの枠線も省略すると次の図になる。

bestFriendChainという名前により2つのノードが結ばれている。bestFriendChainという名前は、ローカルに定義したもので、別の名前、例えば foo にリネームしてもかまわない。論理の限量子束縛変数や、ラムダ計算のラムダ変数と同じく、名前の綴りはなんでもよくて自由にリネーム(アルファ変換)できる。なので、名前の綴りも無視してしまうと次の図になる。

図のピンクと赤の部分は、自分自身に戻る辺=サイクルを表現するための方便に過ぎない。テキスト構文でサイクル(やシェアリング)を表現するためには、中間に名前を挟む必要がある。しかし、この一時的な名前は単なる便宜的な手段なので、図では不要なのだ。

結局、letマップと名前参照ノードを使って表現したかったスキーマのデータ構造は、次のようなサイクリックグラフだったわけだ。

テキスト構文として書き下す都合で、名前はどうしても必要だ。名前のメカニズムがletマップ(名前の定義)と参照ノード(名前の出現)だが、図=グラフとしては不要であり、中間的・一時的な名前を排除すると事情が単純になる。



以下にグラフ描画のソース。ほとんどコピペで作ったが全部載せておく。

/* -*- coding: utf-8 -*- */
/* letMapppedGraph-1.caty */

[

  [
     gv:node --shape=box --style=filled --fillcolor=pink --label="bestFriendChain" e_bestFriendChain,
     gv:node --shape=box --style=filled --fillcolor=pink --label="Friend" e_Friend,

  ] | gv:cluster  --label="let map" letMap,

  gv:edge --color=red e_bestFriendChain bestFriendChain,
  gv:edge --color=red e_Friend Friend,

/*

type Friend = {
  "name":  string,
  "email": string?,
  "addr" : string?,
  "bestFriend": bestFriendChain
};
*/

  gv:node --label="{}" Friend,
  gv:node --label="string" Friend_name,
  gv:node --label="string?" Friend_email,
  gv:node --label="string?" Friend_addr,
  gv:node --style=filled --fillcolor=pink --label="→" Friend_bestFriend,

  gv:edge --label="name"  Friend Friend_name,
  gv:edge --label="email"  Friend Friend_email,
  gv:edge --label="addr"  Friend Friend_addr,
  gv:edge --label="bestFriend"  Friend Friend_bestFriend,

  gv:edge --color=red Friend_bestFriend e_bestFriendChain,

/*

type bestFriendChain = {
  "name": string,
  "bestFriend": bestFriendChain,
}?;

*/
  gv:node --label="{}?" bestFriendChain,
  gv:node --label="string" bestFriendChain_name,
  gv:node --style=filled --fillcolor=pink --label="→" bestFriendChain_bestFriend,

  gv:edge --label="name"  bestFriendChain bestFriendChain_name,
  gv:edge --label="bestFriend"  bestFriendChain bestFriendChain_bestFriend,
  gv:edge --color=red bestFriendChain_bestFriend e_bestFriendChain,


] | gv:graph letMappedGraph-1
/* -*- coding: utf-8 -*- */
/* letMappedGraph-2.caty */

[

  [
     gv:node --shape=box --style=filled --fillcolor=pink --label="bestFriendChain" e_bestFriendChain,
     gv:node --shape=box --style=filled --fillcolor=pink --label=" " e_Friend,

  ] | gv:cluster  --label="let map" letMap,

  gv:edge --color=red e_bestFriendChain bestFriendChain,
  gv:edge --color=red e_Friend Friend,

/*

{
  "name":  string,
  "email": string?,
  "addr" : string?,
  "bestFriend": @= bestFriendChain {
      "name": string,
      "bestFriend": bestFriendChain,
   }?
}

*/

  gv:node --label="{}" Friend,
  gv:node --label="string" Friend_name,
  gv:node --label="string?" Friend_email,
  gv:node --label="string?" Friend_addr,

  gv:edge --label="name"  Friend Friend_name,
  gv:edge --label="email"  Friend Friend_email,
  gv:edge --label="addr"  Friend Friend_addr,
  gv:edge --label="bestFriend"  Friend bestFriendChain,


  gv:node --label="{}?" bestFriendChain,
  gv:node --label="string" bestFriendChain_name,
  gv:node --style=filled --fillcolor=pink --label="→" bestFriendChain_bestFriend,

  gv:edge --label="name"  bestFriendChain bestFriendChain_name,
  gv:edge --label="bestFriend"  bestFriendChain bestFriendChain_bestFriend,
  gv:edge --color=red bestFriendChain_bestFriend e_bestFriendChain,


] | gv:graph letMappedGraph-2
/* -*- coding: utf-8 -*- */
/* letMappedGraph-3.caty */

[

  gv:node --shape=box --style=filled --fillcolor=pink --label="bestFriendChain" e_bestFriendChain,
  gv:edge --color=red e_bestFriendChain bestFriendChain,

/*

{
  "name":  string,
  "email": string?,
  "addr" : string?,
  "bestFriend": @= bestFriendChain {
      "name": string,
      "bestFriend": bestFriendChain,
   }?
}

*/

  gv:node --label="{}" Friend,
  gv:node --label="string" Friend_name,
  gv:node --label="string?" Friend_email,
  gv:node --label="string?" Friend_addr,

  gv:edge --label="name"  Friend Friend_name,
  gv:edge --label="email"  Friend Friend_email,
  gv:edge --label="addr"  Friend Friend_addr,
  gv:edge --label="bestFriend"  Friend bestFriendChain,


  gv:node --label="{}?" bestFriendChain,
  gv:node --label="string" bestFriendChain_name,
  gv:node --style=filled --fillcolor=pink --label="→" bestFriendChain_bestFriend,

  gv:edge --label="name"  bestFriendChain bestFriendChain_name,
  gv:edge --label="bestFriend"  bestFriendChain bestFriendChain_bestFriend,
  gv:edge --color=red bestFriendChain_bestFriend e_bestFriendChain,


] | gv:graph letMappedGraph-3
/* -*- coding: utf-8 -*- */
/* letMappedGraph-4.caty */

[
  gv:node --shape=point --color=pink e_bestFriendChain,
  gv:edge --color=red e_bestFriendChain bestFriendChain,

/*

{
  "name":  string,
  "email": string?,
  "addr" : string?,
  "bestFriend": @= bestFriendChain {
      "name": string,
      "bestFriend": bestFriendChain,
   }?
}

*/

  gv:node --label="{}" Friend,
  gv:node --label="string" Friend_name,
  gv:node --label="string?" Friend_email,
  gv:node --label="string?" Friend_addr,

  gv:edge --label="name"  Friend Friend_name,
  gv:edge --label="email"  Friend Friend_email,
  gv:edge --label="addr"  Friend Friend_addr,
  gv:edge --label="bestFriend"  Friend bestFriendChain,


  gv:node --label="{}?" bestFriendChain,
  gv:node --label="string" bestFriendChain_name,
  gv:node --style=filled --fillcolor=pink --label="→" bestFriendChain_bestFriend,

  gv:edge --label="name"  bestFriendChain bestFriendChain_name,
  gv:edge --label="bestFriend"  bestFriendChain bestFriendChain_bestFriend,
  gv:edge --color=red bestFriendChain_bestFriend e_bestFriendChain,


] | gv:graph letMappedGraph-4
/* -*- coding: utf-8 -*- */
/* letMappedGraph-5.caty */

[

/*

{
  "name":  string,
  "email": string?,
  "addr" : string?,
  "bestFriend": @= bestFriendChain {
      "name": string,
      "bestFriend": bestFriendChain,
   }?
}

*/

  gv:node --label="{}" Friend,
  gv:node --label="string" Friend_name,
  gv:node --label="string?" Friend_email,
  gv:node --label="string?" Friend_addr,

  gv:edge --label="name"  Friend Friend_name,
  gv:edge --label="email"  Friend Friend_email,
  gv:edge --label="addr"  Friend Friend_addr,
  gv:edge --label="bestFriend"  Friend bestFriendChain,

  gv:node --label="{}?" bestFriendChain,
  gv:node --label="string" bestFriendChain_name,

  gv:edge --label="name"  bestFriendChain bestFriendChain_name,
  gv:edge --label="bestFriend"  bestFriendChain bestFriendChain,


] | gv:graph letMappedGraph-5