【ドラフト】PDL1.0運用上の懸念事項についての問い合わせ文面

以下の文面はChatGPTが生成したものを元に一部修正したもの

動画コンテンツにおけるPDL1.0適用範囲の確認について

内閣官房内閣広報室 御中

首相官邸のコピーライトポリシーに関して確認させてください。

首相官邸のソーシャルメディアアカウントに掲載・発信された動画コンテンツ(下記URLに掲載のもの)に、「©内閣広報室」と表記されているものがありました。

一方、首相官邸のコピーライトポリシーでは、権利表記の記載がない限り、「公共データ利用規約(第1.0版)」(PDL1.0)が適用される旨が示されていると理解しています。

この点について、以下を確認させてください。

  1. 動画内に「©内閣広報室」と表記されている場合、その表記はコピーライトポリシー上の「権利表記」に該当し、PDL1.0の適用対象外となるのでしょうか。
  2. それとも、「©内閣広報室」は単に著作権の帰属または管理主体を示す表示であり、当該動画コンテンツにもPDL1.0が適用されるのでしょうか。
  3. 当該動画コンテンツにもPDL1.0が適用される場合、利用者の誤解を避けるため、投稿本文、または動画内表示等において「PDL1.0に基づき利用可」である旨を明記することは可能でしょうか。

政府の記者会見等の動画コンテンツは、報道、検証、引用、翻訳、アーカイブ等の公共的利用に関わるため、利用条件が明確に示されていることが重要だと考えています。

お手数ですが、ご確認のほどよろしくお願いいたします。

URL: https://x.com/kantei/status/2051271730547900493

政府機関等のコンテンツにおけるPDL1.0適用表示の明確化に関するお願い

デジタル庁 御中

公共データ利用規約(第1.0版)(PDL1.0)の運用に関して、政府機関等における表示方法の明確化についてお願いがあります。

現在、政府機関等がウェブサイトやソーシャルメディア上で公開している画像・動画等のコンテンツにおいて、「© ○○」のような著作権表示のみが付されている例があります。

一方で、各府省庁のコピーライトポリシー等により、当該コンテンツがPDL1.0の適用対象である場合、単に「© ○○」とだけ表示されていると、利用者からは「無断利用を禁止している」「All rights reservedである」と受け取られやすく、PDL1.0に基づく利用可能性が十分に伝わらないおそれがあります。

特に、政府の記者会見、政策説明、災害情報、広報動画等は、報道、検証、引用、翻訳、アーカイブ、教育利用等の公共的利用に関わるため、利用条件が明確に示されていることが重要だと考えます。

つきましては、PDL1.0の適用対象であるコンテンツについて著作権表示を行う場合には、例えば以下のように、著作権の帰属表示と利用規約の表示を併記することを、政府機関等に対して周知・推奨していただけないでしょうか。

例:

「© ○○省 / PDL1.0」

「© ○○庁 / 公共データ利用規約(第1.0版)適用」

「© ○○局 / PDL1.0に基づき利用可」

このような表記であれば、著作権の帰属を示しつつ、PDL1.0に基づく再利用が可能であることも明確になります。単なる「© ○○」表記と比べて、利用者の萎縮や誤解を避けやすくなると思われます。

また、動画コンテンツについては、動画内のクレジット表示、投稿本文、動画説明欄、ウェブページ上の注記等において、PDL1.0適用対象である旨を明示することも有効だと考えます。

PDL1.0は、公共データの利活用を促進するための重要な仕組みであり、その趣旨を実効的なものにするためには、法的な適用関係だけでなく、利用者にとっての分かりやすさも重要です。

政府機関等におけるコンテンツ表示の運用として、著作権表示とPDL1.0適用表示の併記を推奨するよう、ご検討いただけますと幸いです。

よろしくお願いいたします。

形骸化について

※ 本文はChatGPTを使って生成した草稿をもとにしているが、問題設定・修正方針は筆者が与えた。この記事は完成した理論ではなく、形骸化を記号システムの設計問題として捉えるための作業仮説である。

制度やシステムには、よく「形だけになる」瞬間がある。

セキュリティ研修を受けたが、行動は変わっていない。 アクセシビリティ基準は満たしているが、実際には使いにくい。 研究評価のための指標が、研究そのものより重視される。 漢字テストで、本来は許容されるはずの字形が、模範字形と違うという理由で減点される。 SNSでは、有害投稿を減らすためのモデレーションが、正当な投稿まで削除する。 行政の支援制度では、本来支援対象である人が、必要書類を揃えられずに排除される。

こうしたものは、まとめて「形骸化」と呼べそうに見える。ただ、形骸化という言葉はかなり雑に使われる。「本質を見失っている」「形だけになっている」「現場が真面目にやっていない」といった道徳的な批判になりやすい。

しかし、形骸化は単なる不真面目の問題ではない。むしろ現場が合理的にリスクを避け、評価される形式に合わせ、説明可能な証拠を残そうとすることで生じる場合が多い。

つまり、形骸化は精神論ではなく、設計の問題として扱える。

この記事では、形骸化を「記号化」と「損失」、そして「記号システムの振る舞い」の問題として捉え直す。

現実、規範システム、記号システム

まず、三つのものを分けて考える。

X = 現実の対象・状況・行為の空間
N = 上位規範システム
Σ = 下位実務で使われる記号システム

X は、制度やシステムが扱おうとしている現実そのものだ。

たとえば、次のようなものが X に含まれる。

取調べ状況
手書き漢字
実際のアクセシビリティ利用状況
研究活動
行政支援を必要とする生活状況
SNS投稿とその文脈
医療判断の状況

N は、その現実に対して、どの操作が許され、禁止され、義務づけられ、どんな手続きや救済が必要かを定める上位規範システムである。

自己負罪を強制されない権利
公平に採点する
誰もが実際に使えるようにする
有害投稿を減らしつつ正当表現を守る
必要な人に支援を届ける
研究の質を評価する

N はデータではない。X の属性でもない。N は、現実に対してどのような規範的振る舞いが要求されるかを与える文脈である。

Σ は、下位実務が実際に扱う記号システムである。

供述調書
採点対象の字形
アクセシビリティ基準への適合結果
論文数・被引用数・JIF
申請書類
違反カテゴリ・分類スコア
警告メッセージ

正確には、記号システム Σ は、記号表現の集合だけではない。記号がどのように読まれ、どの操作を可能にし、どんな証跡を残し、どんな手続きや復元経路を持つかまで含む。

記号表現の空間を、次のように書く。

Rep_Σ = Σ における記号表現の空間

制度やシステムは、現実 x ∈ X をそのまま扱えない。そこで、記号システム Σ における表現へ変換する。

encode_Σ : X → Rep_Σ
s = encode_Σ(x)

この encode_Σ によって、現実は採点できるもの、審査できるもの、記録できるもの、検索できるもの、監査できるもの、自動処理できるものになる。

記号化は必要である。記号化しなければ、制度は動かない。

しかし、記号化はほとんどの場合、不可逆である。

decode_Σ(encode_Σ(x)) ≠ x

つまり、記号システム Σ における復元・解釈 decode_Σ を使っても、記号表現から元の現実を完全には復元できない。

ここで失われるものがある。文脈、条件、揺れ、例外、背景、理解、心理的圧力、当事者の困難、沈黙、時間的経緯、判断の不確実性。

この損失は、下流の判断主体から直接見えるものではない。見えるなら、それは損失ではなく、単に追加情報として保持されているだけである。

だから、損失は判断関数の入力ではなく、理論側から見た「失われたもの」として扱う必要がある。

L_Σ(x) = encode_Σ によって下流から見えなくなった差異・文脈・条件

意味論は、真偽値やスコアではなく振る舞いとして考える

ここで注意したいのは、記号システムの意味論を、単なる Bool への関数として考えないことだ。

記号システムの意味は、単に真偽値やスコアを返すことではない。その記号が制度内でどのような観測、操作、遷移、証跡、ガード、復元経路、不確実性を持つかという、より広い振る舞いとして考えたほうがよい。

記号システム Σ の意味論を、次のように置く。

⟦-⟧_Σ : Rep_Σ → B_Σ

ここで B_Σ は、記号システム側の振る舞いの空間である。

たとえば、⟦s⟧_Σ は次のようなものを含む。

観測可能な情報
可能な操作
操作による状態遷移
証跡・由来
不確実性・信頼度
元文脈へ戻る経路
操作前に必要な手続き的ガード

つまり、記号 s の意味は、「それが何を表すか」だけではなく、「それが制度内でどう振る舞うか」にある。

同様に、規範システム N の意味論も、単なる許可/拒否の判定関数ではない。

⟦-⟧_N : X → B_N

ここで B_N は、規範側の振る舞いの空間である。

たとえば、⟦x⟧_N は次のようなものを含む。

許可される操作
禁止される操作
義務づけられる操作
操作前に必要な手続き
誤操作時の救済
操作の望ましさ・危険度・優先度
操作後の規範状態の変化

このように見ると、記号システムと規範システムは、それぞれ別の意味論を持つ。

⟦encode_Σ(x)⟧_Σ = 記号システム側の振る舞い
⟦x⟧_N = 規範システム側の振る舞い

形骸化を考えるには、この二つの振る舞いがどれだけ対応しているかを見る必要がある。

その対応を、次のように書く。

κ_{Σ,N} : B_Σ → Approx(B_N)

これは、記号システム Σ の振る舞いを、規範システム N の振る舞いにどの程度対応づけられるかを表す。

写像ではなく、関係として考えてもよい。

R_{Σ,N} ⊆ B_Σ × B_N

重要なのは、⟦encode_Σ(x)⟧_Σ が、⟦x⟧_N を十分に保存・近似・補償しているかである。

κ_{Σ,N}(⟦encode_Σ(x)⟧_Σ) ≈ ⟦x⟧_N

この対応が壊れているのに、記号システム側の振る舞いだけで操作を進めると、形骸化が起きる。

形骸化の定義

このモデルでは、形骸化を次のように定義できる。

形骸化とは、記号システム Σ の振る舞い ⟦encode_Σ(x)⟧_Σ が、上位規範システム N のもとで必要な規範的振る舞い ⟦x⟧_N を十分に保存・近似・補償していないにもかかわらず、Σ 側の振る舞いだけに基づいて下流操作を実行することである。

短く言えば、こうなる。

形骸化とは、記号システムが上位規範に必要な振る舞いを支えきれていないのに、その記号システムだけで操作を進めることである。

さらに圧縮すれば、こうも言える。

形骸化 = normative behavior を保存しない symbolic behavior による操作

ただし、これは別に「記号システムが悪い」という話ではない。記号システムは必要である。制度は現実をそのまま扱えない。

問題は、記号システムの粗さに対して、下流の操作が強すぎる場合である。参考情報として使うには十分な記号でも、拒否・減点・停止・証拠化の根拠にするには足りないことがある。

だから問うべきなのは、「この記号システムは正しいか」ではなく、「この記号システムで、この操作をしてよいか」である。

2値判断とスコアリング

形骸化は、accept/reject の2値判断だけで起こるわけではない。

実際の社会では、多くの場合、スコアリングが行われる。

信用スコア
リスクスコア
採用スコア
研究評価スコア
違反確率
優先度
重要度
予測損失

そして、そのスコアに閾値を設けたり、何らかの目的関数を最大化・最小化したりする。

この場合も、モデルは同じである。

規範側の評価を、意味論への問い合わせとして書く。

V^N_op(x) = value(op, ⟦x⟧_N)

記号系側の評価を、次のように書く。

V^Σ_op(s) = score(op, ⟦s⟧_Σ)

下位実務では、現実 x ではなく、記号表現 s = encode_Σ(x) を見るので、実際には次が使われる。

V^Σ_op(encode_Σ(x))

このとき形骸化は、次のズレとして表せる。

V^Σ_op(encode_Σ(x)) ≠ V^N_op(x)

2値判断は、このスコアリングモデルの特殊ケースである。

たとえば、閾値 τ を設けて、

C^Σ_op(s) = [ V^Σ_op(s) ≥ τ ]

のようにすれば、スコアから accept / reject が導かれる。

このモデルでは、問題は単に「誤って受け入れる」「誤って拒否する」だけではない。次のような歪みも扱える。

スコアの順位が歪む
閾値付近の境界事例が不安定になる
本来は小さな差が、操作上は大きな差になる
損失関数が、制度側の損失だけを最小化する
測りやすい要素だけがスコアに反映される
スコアが高くなるように対象側が自己整形する

たとえば、採用スコアでは、実際の職務適性 V^N_op(x) ではなく、履歴書上のキーワードや経歴パターンから計算される V^Σ_op(encode_Σ(x)) が使われる。ここでは、非定型な経験、潜在能力、チームとの相性、過去の環境差などが失われる。

モデレーションでは、投稿の実際の文脈における有害性 V^N_op(x) ではなく、危険語や分類モデルによる違反確率 V^Σ_op(encode_Σ(x)) が使われる。閾値を少し変えるだけで、正当な投稿が大量に削除されたり、有害投稿が残ったりする。

研究評価では、研究の実質的貢献 V^N_op(x) ではなく、論文数や引用数などから作られた V^Σ_op(encode_Σ(x)) が使われる。すると、スコアに乗る活動が増え、スコアに乗りにくい活動は痩せていく。

このようなスコアリングも、記号システムの振る舞いの一部として扱える。

逆像と損失

encode_Σ : X → Rep_Σ は、多くの場合、単射ではない。

x ≠ y でも encode_Σ(x) = encode_Σ(y)

つまり、違う現実が同じ記号表現に潰れる。

このとき、encode_ΣX 上に同値関係を作る。

x ~_Σ y  ⇔  encode_Σ(x) = encode_Σ(y)

同じ s に写る現実たちは、記号システム Σ からは区別できない。

各記号表現 s について、その逆像を考える。

F_s = encode_Σ^{-1}(s)

これは、同じ記号 s に潰れた現実の集合である。

問題は、同じ s に潰れた現実たちが、規範システム N のもとでは違う振る舞いを要求される場合である。

encode_Σ(x) = encode_Σ(y)
but
⟦x⟧_N と ⟦y⟧_N が異なる

このとき、Σ はその違いを失っている。

これは、操作依存・規範依存の損失である。

Loss is relative to (N, op)

同じ損失でも、ある操作では問題にならないが、別の操作では致命的になる。

例を挙げる。

模範字形との差分
→ 漢字練習の参考には使える
→ 不正解判定には危険

AI分類スコア
→ 優先レビューには使える
→ アカウント停止には危険

JIF
→ 雑誌の大まかな影響を見るには使える
→ 研究者の採用判断には危険

だから重要なのは、

Σ が悪い

ではなく、

その Σ で、その op をしてよいのか

である。

類型1: 代理指標化

代理指標化は、もっとも分かりやすい形骸化である。

X = 研究活動
N = 研究の質を評価する規範システム
Σ = 論文数・被引用数・JIFによる評価システム
op = 採用・昇進・助成採択

記号化によって失われるものは多い。

長期的貢献
再現性
教育
共同研究
ソフトウェア
失敗知
分野差
研究文化への貢献

規範側の振る舞いでは、研究の実質的貢献や文脈が重要になる。

⟦x⟧_N = 研究の実質的貢献、再現性、長期的価値、教育的貢献など

記号システム側の振る舞いでは、指標が中心になる。

⟦encode_Σ(x)⟧_Σ = 論文数、引用数、JIF、ランキング上の位置

形骸化は、次の短絡として現れる。

指標が高い = 良い研究

指標は完全に無意味ではない。むしろ、部分的には有意味である。だから危険である。部分的に正しい記号が、全体を代表する顔をし始める。

類型2: 最低基準の十分条件化

アクセシビリティの例を考える。

X = 実際の利用状況・設計・利用者の身体条件
N = 誰もが実際に使えるようにする規範システム
Σ = アクセシビリティ基準への適合を扱う記号システム
op = アクセシブルと認定する

規範側の振る舞いは、実際の利用可能性、疲労、認知負荷、障害種別ごとの差を含む。

⟦x⟧_N = 実利用可能性、独立利用可能性、負荷、文脈依存の困難

記号システム側の振る舞いは、基準適合、チェック項目、監査証跡を含む。

⟦encode_Σ(x)⟧_Σ = 基準適合、チェック項目、合格/不合格、監査証跡

失われるものは、たとえば次のようなものだ。

実利用上の困難
疲労
認知負荷
障害種別ごとの差
支援なしで使えるか
代替経路の質
状況依存の困難

形骸化は、次の短絡として現れる。

基準を満たした = 実際に使える

ここで起きているのは、基準が厳しすぎることではない。むしろ、基準は本来最低条件に近い。それが十分条件のように扱われる。

minimum condition ≠ sufficient condition

最低基準は床であって、天井ではない。ところが実務では、「基準を満たしたから完了」と扱われる。これを 床の天井化 と呼べる。

類型3: 許容集合の代表元化

漢字字形の例を考える。

X = 実際の手書き字形
N = 同一漢字として許容できるものは正解にする規範システム
Σ = 模範字形との差分・採点表現を扱う記号システム
op = 正解/不正解の判定

規範側の振る舞いでは、字体と字形の違い、手書きと印刷文字の違い、許容される揺れが重要になる。

⟦x⟧_N = 同一漢字としての許容可能性、字体/字形の区別、教育上の妥当性

記号システム側の振る舞いでは、模範字形との差、採点者が安全とみなす形、減点リスクが中心になる。

⟦encode_Σ(x)⟧_Σ = 模範字形との差、採点上の安全性、減点可能性

下流の採点では、次の差異が失われやすい。

許容される字形差
字体と字形の区別
手書きと印刷文字の違い
採点者間の判断揺れ
教育上の段階差

形骸化は、次の短絡として現れる。

模範字形と違う = 不正解

本来は、同一漢字として許容できない場合だけ不正解にするべきである。しかし、採点リスクを避けるために、安全な代表字形だけが実質的な正解になっていく。

許容集合 A
→ 安全集合 S_safe

S_safe ⊂ A

類型4: 証拠短絡

ミランダ警告の例を考える。

X = 供述が生じた取調べ状況
N = 自己負罪を強制されない権利を守る規範システム
Σ₀ = 供述記録・署名・調書だけを扱う記号システム
op = 供述を証拠として使う

この例では、供述記録そのものよりも、供述が生じた状況から何が抜け落ちるかが重要になる。

規範側の振る舞いは、権利理解、任意性、弁護士アクセス可能性、心理的圧力を含む。

⟦x⟧_N = 権利理解、任意性、弁護士権、心理的圧力、場の非対称性

記号システム Σ₀ 側の振る舞いは、供述内容、署名、日時、調書、記録形式を含む。

⟦encode_{Σ₀}(x)⟧_{Σ₀} = 供述内容、署名、日時、調書、記録形式

ここでは、次のようなものが失われる。

心理的圧力
権利理解
弁護士へのアクセス可能性
黙秘できると理解していたか
任意性
場の非対称性

形骸化は、次の短絡として現れる。

供述がある = 任意に話した
署名がある = 理解して同意した

これは、証拠の外形が、操作の正当条件を十分に示しているかのように扱われる問題である。

ミランダ警告は、この損失を補償する手続き的ガードになる。

Σ₊ = 供述記録
   + 権利告知
   + 理解確認
   + waiver
   + 弁護士権に関する記録

つまり、記号システムを Σ₀ から Σ₊ に拡張することで、

κ_{Σ₊,N}(⟦encode_{Σ₊}(x)⟧_{Σ₊})

⟦x⟧_N

に近づける。

ミランダ警告は、単なる親切な説明ではない。上位権利を下位手続きで形骸化させないための記号システムの拡張である。

類型5: リスク非対称による過剰拒否

金融 de-risking、コンテンツモデレーション、AIの過剰拒否には共通した構造がある。

X = 顧客・投稿・質問・商品
N = リスクを管理しつつ正当な対象を扱う規範システム
Σ = リスクカテゴリ・分類スコア・危険語を扱う記号システム
op = 拒否・削除・停止・追加審査

記号化によって失われるものは、たとえば次のようなものだ。

個別事情
文脈
合法性
正当性
低リスク要素
境界事例
当事者の説明可能性

形骸化は、次の短絡として現れる。

危険そう = 拒否

ここでは、損失だけでなく、誤判定コストの非対称性が重要になる。

Cost(false accept) >> Cost(false reject)

危険なものを通してしまった場合のコストは、制度や企業にとって大きく見える。一方で、本来許容されるものを拒否した場合のコストは、利用者や事業者に押し出されやすい。

その結果、判断主体は安全側へ倒れる。

怪しいなら拒否
危なそうなら削除
問題になりそうなら扱わない

安全のための判断が、排除の仕組みに変わる。

類型6: 警告過多

alert fatigue の例を考える。

X = 危険事象
N = 安全を確保する規範システム
Σ = 警告システム
op = 人間の注意を割り込ませる

警告は、危険を知らせるための記号である。しかし、警告は単に表示されるだけではない。人間の注意資源を消費する。

警告を出しすぎると、次のものが失われる。

警告の信頼性
注意資源
重要警告への反応性
通常業務の集中

形骸化は、次の短絡として現れる。

警告を出した = 安全対策をした

しかし、警告が多すぎると注意予算を超える。

alerts > attention_budget

すると警告は機能しなくなる。安全のための記号が、安全を壊す。

これは、単に「情報を増やせばよい」という発想の限界でもある。

類型7: 証明負担化

行政手続きの例を考える。

X = 支援対象である生活状況
N = 必要な人に支援を届ける規範システム
Σ = 申請書類・証明書・本人確認・期限内提出を扱う記号システム
op = 給付・拒否・審査通過

記号化によって失われるものは、たとえば次のようなものだ。

制度を知る能力の差
書類収集能力
言語能力
心理的負担
デジタルアクセス
支援が必要だからこそ申請が難しい事情

形骸化は、次の短絡として現れる。

支援対象である = 所定形式で支援対象であることを証明できる

しかし、支援を受ける資格があることと、それを制度が要求する形式で証明できることは違う。

eligibility ≠ provability_by_required_form

ここでは、支援のための制度が、支援を必要とする人に対して証明負担を押し付ける。制度は存在するのに、制度へアクセスできない。

対策1: 記号システムを豊かにする

一つ目の対策は、記号システム Σ を拡張することだ。

Σ → Σ⁺

例を挙げる。

供述記録
→ 供述記録 + 権利告知 + waiver確認

AI要約
→ 要約 + 原文リンク + 信頼度 + 省略箇所

採点基準
→ 採点基準 + 許容字形DB + 境界事例

モデレーション判定
→ 判定結果 + 理由 + 自動判定か人手か + 信頼度

Σ を豊かにすることで、記号システム側の振る舞いを、規範システム側の振る舞いに近づける。

ただし、Σ⁺ もまた記号システムである。損失が完全に消えるわけではない。

対策2: 粗い記号システムでは強い操作を許さない

これが最も重要な原則である。

OperationStrength(op) ≤ Fidelity(Σ) + Strength(guard)

言い換えると、次のようになる。

強い操作には、低損失な記号システムか、強い手続き的ガードが必要である。

例を挙げる。

低信頼AI分類 → アカウント停止不可
模範字形との差分 → 重大減点不可
JIFだけ → 採用判断不可
チェックリスト適合だけ → 安全認定不可
署名だけ → 実質的同意とみなさない

記号システムが粗いなら、できる操作も弱くすべきである。

対策3: レビュー・異議申し立て・文脈復元を入れる

同じ記号表現に、操作してよい現実と、してはいけない現実が混ざっている場合、記号システムだけでは判断できない。

その場合に必要なのは、レビュー、異議申し立て、追加情報、文脈復元である。

reject(s)
→ appeal(context(x))
→ reconsider

異議申し立てとは、単なるクレーム処理ではない。圧縮された記号から、失われた文脈を再展開する制度である。

対策4: 誤判定を監査する

形骸化は、個別の判断では見えにくい。だから、統計的・運用的に監査する必要がある。

false reject rate
false satisfaction rate
appeal success rate
inter-rater reliability
alert override rate
drop-off rate

例を挙げる。

漢字採点 → 採点者間一致率
モデレーション → 誤削除率・appeal成功率
行政手続き → 申請途中離脱率
アクセシビリティ → 実利用タスク完了率
医療警告 → override率・有効警告率

基準を満たした件数だけではなく、記号システム側の振る舞いと規範システム側の振る舞いがどれだけズレているかを見る必要がある。

対策5: 誤判定コストの偏りを補正する

判断主体が false accept ばかり恐れると、false reject が増える。

危険な投稿を残すこと、違法取引を通すこと、有害回答を返すことは、制度側にとって大きなリスクに見える。一方で、正当な投稿を削除すること、合法な事業者を排除すること、無害な質問を拒否することのコストは、利用者側に押し出されやすい。

だから、拒否された側の損失も制度側で見る必要がある。

金融 → 正当な顧客の排除率
AI安全 → 無害質問の拒否率
モデレーション → 正当投稿の削除率
行政 → 支援対象者の未申請・未受給率

安全側に倒すことの害も測らなければならない。

対策6: 代表例ではなく許容集合を示す

模範例は必要である。しかし、模範例だけを提示すると、それが唯一の正解になる。

だから、代表例だけでなく、許容集合と境界例を示す必要がある。

代表例 + 許容集合 + 境界例

これは漢字字形だけでなく、英作文、デザインガイドライン、AI安全、ブランド表現、アクセシビリティにも使える。

AI安全でも、単に「それには答えられません」と拒否するだけでは、利用者は拒否されない言い方を探し始める。拒否例だけでなく、許容される問い方・扱える範囲・安全に議論できる条件を示す必要がある。

対策7: 最低基準を最低基準として表示する

最低基準は、最低基準として扱わなければならない。

この基準は必要条件に近いが、十分条件ではない

アクセシビリティ、建築、安全、品質、セキュリティでは、最低基準が到達目標になりやすい。基準文書には、それが十分条件ではないことを明示し、実利用評価を別系統で設けるべきである。

分野間で対策を転移する

このモデルの利点は、ある分野で見つかった対策を、別の分野へ転移できることにある。ただし、転移すべきなのは表面的な制度ではない。転移するのは、「記号化による損失をどう補償するか」という設計パターンである。

コンテンツモデレーションへ、医療診断の発想を転移する

投稿削除は、医療診断に似ている。違反投稿を検出することは、病変を見つけることに似ている。誤って残すことも危険だが、誤って削除することも危険である。

したがって、削除件数だけでなく、誤削除率、見逃し率、カテゴリ別 precision / recall、appeal 成功率を見る必要がある。

AI安全へ、alert fatigue の発想を転移する

拒否を増やせば安全になるわけではない。過剰な拒否は、利用者に「何が危険か」ではなく「どう言えば拒否されないか」を学習させる。

AI安全に必要なのは、拒否の量ではなく拒否の精度である。軽いリスクには軽い注意を返し、重大で確度の高いリスクだけを強く止める。

行政手続きへ、アクセシビリティの発想を転移する

制度があることと、利用できることは違う。申請フォームがあることと、申請できることも違う。

行政手続きには、アクセシビリティテストが必要である。対象者が制度を発見できるか、必要書類を理解できるか、途中で詰まったとき復帰できるか、スマホだけで完了できるかを検証する必要がある。

金融 de-risking へ、行政負担とアクセシビリティの発想を転移する

金融機関にとって、危なそうな顧客を拒否することは安全に見える。しかし、正当な顧客を金融システムから追い出すと、その活動はより見えにくい場所へ移る。

必要なのは、カテゴリ拒否ではなく、リスクに比例した確認である。低リスク・低額・低頻度の取引には簡易確認を使い、境界事例には追加説明と回復経路を用意する。金融包摂は、リスク管理の敵ではなく一部である。

採用AI・ATSへ、研究評価批判を転移する

採用スコアは、候補者そのものを評価しているように見える。しかし実際には、候補者が履歴書や経歴データという記号へ変換された姿を評価している。

研究評価で指標の限界が問題になるのと同じように、採用でも単一スコアで足切りしてはいけない。非定型経歴を補足できる経路、人間レビュー、指標が誰を落としているかの監査が必要である。

防衛医療へ、セキュリティ運用の「非対応の記録」を転移する

防衛医療では、検査することだけが防御可能な記号になりやすい。必要なのは、検査を増やすことではなく、検査しない判断も防御可能な記号として残すことである。

なぜ検査しなかったか、どの症状なら再受診すべきか、患者と何を共有したか、どのガイドラインに基づいたかを記録する。何もしなかったのではなく、根拠を持って介入しなかったことを記号化する。

結論

形骸化は、単なる「形だけになっている」という嘆きではなく、記号化と損失、そして記号システムの振る舞いの問題として記述できる。

制度やシステムは、現実をそのまま扱えない。だから記号化する。基準、スコア、チェックリスト、供述調書、申請書類、警告、ログ、指標、模範例。これらは必要である。

しかし、記号化は損失を伴う。

問題は、損失があることではない。損失を補償しないまま、強い操作を実行することだ。

一文でまとめるなら、こうなる。

形骸化とは、記号システム Σ の振る舞い ⟦encode_Σ(x)⟧_Σ が、上位規範システム N のもとで必要な規範的振る舞い ⟦x⟧_N を十分に保存・近似・補償していないにもかかわらず、Σ 側の振る舞いだけに基づいて下流操作を実行することである。

形式化すれば、こうだ。

X       = 現実の対象・状況・行為
N       = 上位規範システム
Σ       = 下位記号システム
Rep_Σ   = Σ の記号表現空間

encode_Σ : X → Rep_Σ

⟦-⟧_N : X → B_N
⟦-⟧_Σ : Rep_Σ → B_Σ

κ_{Σ,N} : B_Σ → Approx(B_N)

形骸化 = κ_{Σ,N}(⟦encode_Σ(x)⟧_Σ) が ⟦x⟧_N を十分に保存・近似・補償していないまま op すること

形式は必要である。 記号化も必要である。 基準もスコアもチェックリストも手続きも必要である。

問題は、形式が失ったものを忘れることである。

記号は世界を扱えるようにする。 同時に、世界の一部を見えなくする。

形骸化とは、その見えなくなった部分が本当は重要だったのに、見えないまま処理を続けることである。

所在表現と所有表現 2

mandel59.hateblo.jp

具体的に、AWSのセキュリティグループ設定する構成を図示することを考えてみる。

EC2インスタンスには、複数のセキュリティグループを割り当てることができる。これを所在表現で表現しようとするなら、セキュリティグループが枠としてあり、その中にインスタンスが置かれる表現になる。複数のセキュリティグループに属しているインスタンスは、枠を重ねてその中に置くか、インスタンスのノードを複製して表現することになる。Mermaidで枠を重ねるのは難しいので、ここではノードを複製して表現する。

graph TD
  subgraph sg-db-client
    i-app-a-1[i-app-a]
  end
  subgraph sg-web-server
    i-app-a-2[i-app-a]
    i-app-b-2[i-app-b]
  end
  subgraph sg-db-server
    i-db
  end

所有表現で表現するときはその逆で、インスタンスという枠に、セキュリティグループが置かれる。複数のインスタンスが属しているセキュリティグループは、枠を重ねてその中に置くか、セキュリティグループのノードを複製して表現することになる。Mermaidで枠を重ねるのは難しいので、ここではノードを複製して表現する。

graph TD
  subgraph i-app-a
    sg-db-client-1[sg-db-client]
    sg-web-server-1[sg-web-server]
  end
  subgraph i-app-b
    sg-web-server-2[sg-web-server]
  end
  subgraph i-db
    sg-db-server-1[sg-db-server]
  end

ノードの複製を認めてしまえば、所在表現・所有表現どちらでも、インスタンスとセキュリティグループの関係を表現することができる。「属する」という二項関係と、その逆関係を、それぞれ図に落とし込んでいるだけなので、当然ではあるが。

これらの図に通信の情報を付加することを考えてみる。

所在表現

graph LR
  subgraph sg-db-client
    i-app-a-1[i-app-a]
  end
  subgraph sg-web-server
    i-app-a-2[i-app-a]
    i-app-b-2[i-app-b]
  end
  subgraph sg-db-server
    i-db
  end

  web-client(web-client) --> sg-web-server
  sg-db-client --> sg-db-server

所有表現

graph LR
  subgraph i-app-a
    sg-db-client-1[sg-db-client]
    sg-web-server-1[sg-web-server]
  end
  subgraph i-app-b
    sg-web-server-2[sg-web-server]
  end
  subgraph i-db
    sg-db-server-1[sg-db-server]
  end

  web-client(web-client) --> sg-web-server-1
  web-client --> sg-web-server-2
  sg-db-client-1 --> sg-db-server-1

所有表現の方が分かりやすいように思われる。セキュリティグループノードの複製によって、通信を表すグラフの辺も複製されているが、それは表現として自然だ。一方で、所在表現では、インスタンスを表すノードが複製されているため、結局そのインスタンスがどのような通信を行うかが可視化できておらず、識別子を使って目で突合しなければならない。

対称集計ってなんなのか

cloud.google.com

Looker の対称集計という機能が気になっているのだけれども、いまいちよく分かっていない。検索してもあまり関連する文献が見つからないし、なんで対称集計という名前なのかも分からない。データウェアハウスで、非正規化されたデータの上で集計をする時に使える概念なのだとは思うのだけれども。

とりあえず、実験してみる。Lookerのドキュメントに載っているデータ例をErqで取り込む。(後々のため、データを追加している。)

load table orders(order_id integer, user_id integer, total real, order_date text) from
```csv
1   100 50.36   2017-12-01
2   101 24.12   2017-12-02
3   137 50.36   2017-12-02
4   100 59.68   2017-12-03
``` delimiter E'\t';;

load table order_item(order_id integer, item_id integer, quantity integer, unit_price real) from
```csv
1   50  1   23.00
1   63  2   13.68
2   63  1   13.68
2   72  1   5.08
2   79  1   5.36
3   78  1   50.36
4   50  2   23.00
4   63  1   13.68
``` delimiter E'\t';;

table data = orders join order_item using (order_id);;

注文の合計金額は簡単に計算できる。

erq> orders {sum(total)};;
select sum(total) from orders
["sum(total)"]
[184.52]
1 row (0.001s)

一方で、結合済みのデータを使って合計金額を計算しようとすると、正しく計算できない。表が結合されるときに、ordersテーブルの各行が複製されているからだ。

erq> data {sum(total)};;
select sum(total) from data
["sum(total)"]
[342.8]
1 row (0.001s)

そこで、ordersテーブルのキーを使って、うまいことやる必要がある。

ひとつの方法は、合計する値にいい感じにキーの情報を埋め込み、sum distinct関数を使って集約を行うことだ。ここでは、単純に、order_idの値を10000倍したものをtotalに加えることで、キーの情報を埋め込んでいる。最後に、order_idだけのsum distinct結果を引けば、order_idで区別されたtotalの合計になるはずだ。

erq> data {sum(distinct total + order_id * 10000) - sum(distinct order_id * 10000)};;
select sum(distinct total + order_id * 10000) - sum(distinct order_id * 10000) from data
["sum(distinct total + order_id * 10000) - sum(distinct order_id * 10000)"]
[184.52000000000407]
1 row (0.000s)

結果は、惜しいことになってしまった。SQLiteでは浮動小数点数を使っているために、大きい数を使って情報を埋め込むと、計算したいtotalの合計値には大きな誤差が出てしまう。

逆に、小数点以下に埋め込むと、誤差はマシになる。(そもそも、金額の計算に浮動小数点数を使うこと自体を避けるべきだろうが。)

erq> data {sum(distinct total + order_id * 1e-10) - sum(distinct order_id * 1e-10)};;
select sum(distinct total + order_id * 1e-10) - sum(distinct order_id * 1e-10) from data
["sum(distinct total + order_id * 1e-10) - sum(distinct order_id * 1e-10)"]
[184.52]
1 row (0.000s)

idを単純に定数倍して足す代わりに、ハッシュ関数に通すなど、複雑な変換を行うこともできるが、異なるキーの値が偶然衝突しないように埋め込めるなら、方法はどうやってもいい。ただ、先ほどの例のように、誤差やオーバーフロー・アンダーフローには気を付ける必要がある。また、キーを埋め込んだ値を集約したあとで、埋め込んだキーの影響を集約結果から打ち消す計算ができる、ということも必要だ。

グループ化をした場合も、同じ方法が使える。ここでは、user_idでグループ化して、合計と平均を計算してみる。

erq> data {user_id => sum: sum(distinct total + order_id * 1e-8) - sum(distinct order_id * 1e-8), avg: avg(distinct total + order_id * 1e-8) - avg(distinct order_id * 1e-8)};;
select user_id, sum(distinct total + order_id * 1e-8) - sum(distinct order_id * 1e-8) as sum, avg(distinct total + order_id * 1e-8) - avg(distinct order_id * 1e-8) as avg from data group by (user_id)
["user_id","sum","avg"]
[100,110.04,55.02]
[101,24.12,24.12]
[137,50.36,50.36]
3 rows (0.001s)

ところで、サブクエリが使えるのなら、こんな面倒なことをしなくても、何段階かに分けて集計を行えば済む話ではある。集計したいカラムを抜き出してきて

erq> data {user_id, order_id, total};;
select user_id, order_id, total from data
["user_id","order_id","total"]
[100,1,50.36]
[100,1,50.36]
[101,2,24.12]
[101,2,24.12]
[101,2,24.12]
[137,3,50.36]
[100,4,59.68]
[100,4,59.68]
8 rows (0.001s)

重複を除外する

erq> data {user_id, order_id, total} distinct;;
select distinct user_id, order_id, total from data
["user_id","order_id","total"]
[100,1,50.36]
[101,2,24.12]
[137,3,50.36]
[100,4,59.68]
4 rows (0.001s)

あとは、それを集計すればよい。

erq> data {user_id, order_id, total} distinct {user_id => sum: sum(total), avg: avg(total)};;
select user_id, sum(total) as sum, avg(total) as avg from (select distinct user_id, order_id, total from data) group by (user_id)
["user_id","sum","avg"]
[100,110.03999999999999,55.019999999999996]
[101,24.12,24.12]
[137,50.36,50.36]
3 rows (0.000s)

(まあ、なんか誤差が出ているけど、これは浮動小数点数のせいだと思うし、気にしないことにする。)

distinctじゃなくて、group byをつかって重複除外を行なってもいい。こちらの方が、行全体ではなくキーの値だけを使って重複を除外するので、計算がより軽いかもしれない。

erq> data {order_id => user_id, total} {user_id => sum: sum(total), avg: avg(total)};;
select user_id, sum(total) as sum, avg(total) as avg from (select order_id, user_id, total from data group by (order_id)) group by (user_id)
["user_id","sum","avg"]
[100,110.03999999999999,55.019999999999996]
[101,24.12,24.12]
[137,50.36,50.36]
3 rows (0.001s)

SQLiteではgroup by句で指定されていない非集約カラムであるuser_id, totalは、order_idに対して関数従属であるので、select句に出てきても問題がないのだが、これを受け付けないSQLエンジンを使っている場合は、any_value集約関数を使うか、適当にmin集約関数を使うなどの対処をする必要がある。)

とにかく、サブクエリで group by を使って重複を除去しておく方法は素直で分かりやすいし、sumやavgのような集約関数の性質に左右されず汎用的な技法なので、特に制約がなければ、後者の多段階の集約を使った方がいいように思う。BIツールなどでは、集約のフォーマット・段階が固定であるといった制約があるために、前者の方法を使うしかないのかもしれない。

MDNにあるセマンティクスの説明を直したい

developer.mozilla.org

この記事の説明は問題があるので、できれば直したい。直すならGitHubにissueを出すのが筋だと思うが、いきなり英語で書くのは大変なので、とりあえず日本語で問題点をまとめておく。

プログラミングでは、セマンティクスとは、コードの断片の意味を指します。たとえば、「JavaScript でその行を実行すると、どのような効果があるのか?」、「その HTML 要素には、どのような目的や役割があるのか?」 (「どのように見えるのか?」ではなく)。

最初の、この説明は、とりあえずOKだと思う。細かい表現にケチをつけられるかもしれないけど。セマンティクスとはコードの意味のことで、その非形式的な実体としては、プログラムを実行した際に起きるエフェクトだとか、それが繋がっている目的や役割などだと言えるだろう。(言語学なんかでは、コンテキストから独立した意味を考える意味論と、コンテキストと意味の関係を論じる語用論とを分けるかもしれないが、ここでは、特に両者を区別していない。)

しかし、その後につづく、JavaScript, CSS, HTMLのセマンティクスについての個別の説明は、だいぶあやしい。

JavaScript でのセマンティクス

JavaScript において、文字列の引数を取り、その文字列を textContent とする <li> 要素を返す関数を想像してみてください。 build('Peach') または createLiWithContent('Peach') という関数名の場合、何をするのかを理解するためにコードを見る必要があるのはどちらでしょうか?

この説明は、どうやってコードをリーダブルにするか、という話をしている。コードのリーダビリティは、コードのセマンティクスとは関係がない。

最初の説明通り「JavaScript でその行を実行すると、どのような効果があるのか?」というのが、JavaScriptのセマンティクスだ。関数の名前をbuildにしようがcreateLiWithContentにしようがaaにしようが、それはJavaScriptで書かれたプログラムの動作に、ほぼ影響がない1。識別子にどう名づけるか自体はプログラムの動作に影響しないし、だからこそ、minifierのようなツールを使って識別子を短いものに書き換えることで、JavaScriptプログラムを小さくすることができる。

JavaScriptのセマンティクスは、主に次のような文書で規定されている。

HTML でのセマンティクス

HTMLのセマンティクスに関しては、OKな説明であるとは思う。ただ、最新のHTML仕様に合わせた説明に書きかえると、もっといいと思う。

HTML仕様 1.8 HTML vs XML syntax で、文書やアプリケーションを記述する抽象言語と、情報交換用の具体構文として「HTML構文」「XML構文」が規定されている。これらの構文によって記述されたHTML文書は、ファイルに保存されたり、HTTPで送信されたりする。

これらのHTML文書は、ブラウザーで処理される際、メモリ上では、「DOM HTML」というより抽象的な形に変換される。HTML構文については13 The HTML syntaxで、XML構文については14 The XML syntax で、それぞれDOM HTMLに変換する方法が規定されている。

HTMLのセマンティクスは 3 Semantics, structure, and APIs of HTML documents で定められている。それは、厳密に言えば、ファイルに保存されたり送信されたりするHTML構文やXML構文に対してではなく、より抽象的な内部表現であるDOM HTMLに対して、それぞれの要素、属性、属性値のセマンティクスを規定している。

Elements in the DOM represent things; that is, they have intrinsic meaning, also known as semantics.

For example, an ol element represents an ordered list.

https://html.spec.whatwg.org/multipage/dom.html#represents

要素はそれぞれ、なにか物事 (thing) を表現 (represent) している。たとえば、ol要素は、順序付きリストを表現している、ということが、仕様として書かれている。

また、HTMLの各要素にはAPIが定義されていて、そのAPIを介して、要素の機能に関する操作を、JavaScriptから行える。たとえば、<button> 要素はボタン操作に関するAPIを提供しているし、<video> 要素はビデオ再生に関するAPIを提供している。こういったAPIに関する規定は、仕様のいう "intrinsic meaning" や "semantics" というよりは、それをブラウザー上で実現するための機構のようなものといえる。しかし、JavaScriptのようなプログラムの、プログラムの振る舞いがセマンティクスだという考えからすれば、DOM HTMLのAPIも、一種のセマンティクスだと考えられるだろう。言語的な、コードによって表現される物事を指すセマンティクスと、プログラム的な、コードによって実現される機構の振る舞いを指す「セマンティクス」の両方が、仕様に規定されていることになる。

HTMLのセマンティクスは、ブラウザーの画面上での具体的な見た目(プレゼンテーション)を規定していない。HTMLの見た目は、HTML自体ではなく、HTMLに関連づけられたスタイルシートによって決まっている。

CSS でのセマンティクス

CSS において、さまざまな種類の果物を表すために li 要素でリストをスタイル付けすることを想像してみてください。 div > ul > li または .fruits__item で選択された DOM の一部が何であるか分かるのはどちらでしょうか?

本来であれば、「CSSでのセマンティクス」とは、やはり「CSSのコードがどのような意味を持つのか?」といった部分を説明するべきだろうと思う。ここでのMDNの説明は、セマンティクスではなくて、よりよいCSSを書くためのプラクティスの説明でしかない。

div > ul > li という指定は、「div要素の子ul要素のさらに子のli要素」を指定している。一方で、.fruits__itemは「class属性がfruits__itemの要素」を指定している。どちらも意味のある記述であり、どちらの方がどちらもCSSとしての意味がある。要素の意味はHTML仕様としておおむね規定されているが、クラス(class属性の値)の意味に関しては、そのHTMLの書き手が勝手に決めていることだ。class属性はHTML要素の意味を拡張する機構で、たとえばMicroformatのような仕様が、HTML仕様で規定されない意味を表現するために使っている。しかし、そういう仕様で規定されていない、独自クラス fruits__item が厳密にどのような意味をもっているのかを知ることは難しい。人間は(あるいは今日のLLMは)fruits__itemというクラス名に埋め込まれた英単語の意味を解釈できるかもしれないが、それはCSS自体のセマンティクスではなく、英単語の意味を解釈している。CSS意味の先にある人間の意図を説明しているか、という観点では、差があるだろう。ただ、それは、パスを使ったからセマンティックでないとか、クラス名だからセマンティックだ、ということにはならない。これが ul > li というセレクターだったら、「ul要素の子li要素」という意味の先にある「順序なしリストのリストアイテム」という意図は明瞭だろうし、逆に、.aaaa のようなセレクターだったら、クラス名を使っていても意図を読み取ることはできない。また、ulやliのような要素の意味はHTML仕様で規定されているが、fruits__itemクラスの要素がどのような意味であるのかを知るには、どうにか推測するしかない。

ここに書くことは特に個人的な考えになるとは思うのだけれども、CSS自体は、あまり「セマンティック」な仕組みではないかもしれない。というのも、CSSは、DOMに対して構文的にマッチし、マッチした要素に対してCSSプロパティを割り当てる機構だからだ。CSSの処理の仕組み上、HTMLの各要素が本来持っているセマンティクスは、無視して処理することになる。だから、HTMLの意味上は同じ意味になるはずでも、CSSは異なるスタイル付けをしてしまう。たとえば <div> 要素の意味は "The div element has no special meaning at all. It represents its children." のように定まっている。だからといって、CSS<div>要素を無視して処理したりはしない。

HTML のセマンティクスの拡張

先述のように、HTMLではclass属性を使って、HTML自体に規定されていない要素の意味を表現することができる。その他に、意味の拡張を行いたいのであれば、カスタム要素を使うこともできる。カスタム要素は、class属性を使った場合と違って、カスタム要素のAPIの提供が可能になるので、HTML仕様に規定されているプリミティブの要素と近い形で、カスタム要素を規定し、実装し、利用することができる。

その他、HTMLの意味を拡張・変更しているものに ARIA in HTML がある。HTMLのセマンティクスでは、アプリケーション操作中の遷移的なステートを表現しきれていないことがある。たとえば、トグルボタンを実装しようとしたとき、たとえ、HTMLのbutton要素と、JavaScriptCSSによってトグルボタンの視覚表現を実装したとしても、そうした視覚表現が使えないユーザーにとっては、アクセシビリティーの欠如したアプリケーションになってしまう。そういう、HTMLレベルでは欠如している情報をDOM HTML上で表現するため、ARIAではセマンティクスを拡張しており、トグルボタンの例でいえばaria-pressed属性によって、トグルボタンのステートを表現し、支援技術を介して、より多くのユーザーがアプリケーションを利用することができる。


  1. ほぼ、というのは、JavaScriptには関数の名前を取得する方法が用意されていて、そういう名前の取得を行う処理を含んでいれば動作が変わるから。

記号と通信に関する抽象階層モデルのメモ

情報科学・技術と記号論を関連付けようとする試みは、いくらか存在するが、個人的には、いままで提示されているモデルで納得がいくものはあまり多くない。既存のモデルは、断片的だったり不正確だ。ここでは、より正確であるが、詳細になりすぎないモデルをメモしておく。

情報システムは、次の4つの世界観に分けて、考えることができる。低次のレイヤーから順に、

  • 物理界=場の世界
  • 通信界=信号の世界
  • 計算界=表現の世界
  • 人間界=意識の世界

と考える。

物理界は、情報システムをそのように物理的な系として解釈した世界観だ。人間や計算機は、センサーやアクチュエーターなどを介して物理的な外部環境と相互作用している。そうした過程のすべての現象は、計算過程や通信過程も含めて、実際にはすべて物理的な法則に従っている。物理界では、人間の知覚しがたい現象が作用しあい、混沌としていて、高度な数理モデルによって記述される。

通信界は、宇宙のうち、秩序だって信号を伝達できる部分を抜き出した世界観だ。そこには、伝達が望まれるシグナルと、伝達が望まれないノイズの区別が存在している。その区別は、すべてを場として扱う物理界には無かったものだ。ノイズを効果的に除去する変調技術によって通信路は延長され、現代では地球的な通信網が形成されている。

計算界は、通信によって結ばれ、表現を交換するエンティティからなる世界観だ。エンティティどうしは物理的には隔てられているが、通信網によって接続されていることで、計算界においては、直接的に接しているように見える。通信は、表現を信号に変換し、物理場を介して伝達し、また表現に戻す。これによって、通信路自体はほとんど透明であって、路というより面のように見える。これがインターフェースであり、エンティティはインターフェースを介して表現を交換しあっている。エンティティは、表現をなんらかの法則で別の表現に変換し、またインターフェースを介して交換している。

人間界は、エンティティが意識しているオブジェクトからなる世界観だ。人間は意識的に環境と接し、オブジェクトを認識する。計算界より下の世界の多くの現象、たとえばエンティティやインターフェースや表現といった計算界の現象は、無意識の存在になっている。コンピューターが処理する多くの計算は人間に意識されないが、一部の計算は、人間が意識できるオブジェクトとして提示される。

ひとつの情報システムは、こういった4つの世界観によって見ることができる。重要なのは、情報が伝達される過程を、場の世界から論じることも、心の世界から論じることもできるということだ。つまり、情報は、物理的には電場として存在しているが、それが通信上は電圧の高低によって記録・伝達される111001100101011のようなデジタル信号だったりする。これをコンピューターは、29483という数値として処理したり、あるいは"猫"という文字として処理したりする。しかし、コンピューターのそのような計算はプログラムされた機構であって、コンピューター自体は信号を数値や文字として意識することはなく、ただ機械的に計算しているだけだ。29483という数値に大小の感慨を抱くこともないし、"猫"という文字を処理しているからと言って、猫という動物を連想したりはしない。逆に、「猫」という文字を読んだ人間の多くは動物の猫に思いを馳せるのであって、この「猫」という文字自体について考え出す人は少ないという点で、人間は認知機能としては「猫」という表現を確実に処理しているにも関わらず、表現自体は意識にのぼりづらいと言えるだろう。

情報システムの見方をこういった世界観のレイヤーに分けることで、次のようなことが言えるかもしれない。

  • 高次の世界の現象は低次の世界でも解釈が可能だが、その逆は成り立たない。人間も計算機も(還元した結果を計算できるかはともかく)物理現象に還元して解釈できる。しかし、物理現象のなかで通信として解釈できるのは一部の現象だけであり、計算として解釈できるのはさらに少なく、人間の意識に昇ってくる現象はそのほんの一部分だ。

  • ヒトはそれ自体で人間的にも計算的にも通信的にも物理的にも解釈が可能なエンティティだ。人間自体も物理現象に還元されるし、通信現象に還元されるし、計算現象に還元される。人間と機械を区別せず計算を行うエンティティと考えても問題がない。

  • 人間と人間、人間と機械、機械と機械といったインターフェースでやりとりされる表現は異なる。具体的にどのような表現を使うかは、当然、情報技術分野で詳しく検討されているので、情報技術に記号論的な考察を加えるのであれば、そういった表現の体系を適切に説明できるようなものでなければならないはずだ。

  • プログラミングがなぜ難しいのかと言えば、その一因として、ふだん人間が意識しない計算界について意識せざるを得ないから、ということがあるだろう。そして、計算について意識をするようになったプログラマーも、通信に関してはあまり意識をしていないかもしれない。しかし、適切な方法でプログラムを行えば、通信を意識させずに計算をプログラムすることができる。同様に、計算の大部分を隠蔽して、意識する計算を抽象的なものに絞ることで、プログラムは人間の手に負えるものになる。

抽象クラス、インターフェース、Kotlinの記法

抽象クラスは未定義の抽象メソッドがあるクラスで、派生クラスで実装を行うことで、インスタンス化が可能になる。

abstract class AC protected constructor() {
  abstract protected fun amX(): Int
  abstract protected fun amY(): String
  fun cmZ() = "${this.amX()} ${this.amY()}"
}

val ac = object : AC() {
  override fun amX() = 42
  override fun amY() = "abc"
}

この種類の継承は、委譲に置き換えることができる場合がある。抽象メソッドを抜き出してインターフェース化し、そのインターフェースをコンストラクタにとるクラスで具体メソッドを実装する。

interface IC {
  fun amX(): Int
  fun amY(): String
}

class CC(val ic: IC) {
  fun cmZ() = "${ic.amX()} ${ic.amY()}"
}

val cc = CC(object : IC {
  override fun amX() = 42
  override fun amY() = "abc"
})

抽象メソッドを実装する具体メソッドから、スーパークラスの他のメソッドを呼び出すケースでは、こういう種類の変換はできないが、そういう、親クラスのメソッドが子クラスのメソッドに依存しつつ、子クラスのメソッドが親クラスのメソッドに依存するという、相互依存の関係になっているケースは、多くの場合は必要がないし、避けられるなら避けたいケースでもあるので、積極的に継承から委譲へ置き換える動機がある。

しかしながら問題として、継承から委譲へ置き換えたときには、余分な識別子が増えてしまっている。もとのコードでは抽象クラスACだけを定義すればよかったところが、新しいコードでは、インターフェースのICとクラスのCCに分かれてしまっている。もちろん、クラスの内部でインターフェースを定義することで、名前空間に散らからないよう工夫することはできる。

class CC(val ic: IC) {
  interface IC {
    fun amX(): Int
    fun amY(): String
  }
  fun cmZ() = "${ic.amX()} ${ic.amY()}"
}

Kotlinの記法上の問題として、匿名オブジェクトの記法がラムダ式に比べてはるかに不格好という点がある。もし、インターフェースのメソッドが1つだけなら、これは関数インターフェースとすることができる。

class CC1(val ic: IC) {
  fun interface IC {
    fun amX(): Int
  }
  fun cmZ() = "${ic.amX()}"
}

val cc1 = CC1 { 42 }

メソッドが1つの場合と2つの場合で記法上の相違がここまで大きいのはわりと理不尽で、記法上の制約が設計上不自然な選択を取らせる可能性がある。たとえば次のように、関数を複数コンストラクタで取るような形に変えるという手法で、記法の煩雑さを迂回するかもしれない。

class CC2(val amX: () -> Int, val amY: () -> String) {
  fun cmZ() = "${amX()} ${amY()}"
}

val cc2 = CC2(amX = { 42 }, amY = { "abc" })

この方法は、もとの記法より簡潔だが、問題として、評価のタイミングがある。コンストラクタの引数は、呼び出し時点で評価されてしまうから、関数ではなく即値で渡すようなシグネチャにしてしまう可能性がある。

class CC3(val pX: Int, val pY: String) {
  fun cmZ() = "${pX} ${pY}"
}

val cc3 = CC3(pX = 42, pY = "abc")

こう定義してしまうと、あとからゲッターメソッドの実装を変えて、評価タイミングをコントロールすることが厳しくなるから、やはり適切にインターフェースを使うことが望ましい場面も存在するように思われる。

class CC4(val ic: IC) {
  interface IC {
    val pX: Int
    val pY: String
  }
  fun cmZ() = "${ic.pX} ${ic.pY}"
}

val cc4 = CC4(object : CC4.IC {
  override val pX = 42
  override val pY by lazy { println("computed!"); "abc" }
})

これが

val cc4 = CC4 {
  val pX = 42
  val pY by lazy { println("computed!"); "abc" }
}

ぐらい簡単に書けたらいいのだけれども。

それか、メソッド引数のほうをどうにかして、評価戦略を変えられるようにするとか? まあ、メソッド呼び出しの形式で書いたものが正格評価・値渡しになっていないのは、いろいろと言語の前提とかメンタルモデルとか壊していそうではある。 Ceylon言語のLazySpecifierみたいな概念を導入して、もう文法は適当なんだけど、

class CC5(lazy val pX: Int, lazy val pY: String) {
  fun cmZ() = "${pX} ${pY}"
}

val cc5 = CC5(lazy pX = 42, lazy pY = { println("computed!"); "abc" })

とか? まあ、なんかもっとマシな記法でできるといいんだけど

「IPAフォントライセンスを巡って」について思うところ 2

前から時間が空いてしまったけど、サブセットフォントは包摂基準を変えるのか、というところについて書く。

IPAフォントライセンスを巡って | 一般社団法人 文字情報技術促進協議会

フォントがどうだろうと包摂基準には関係しない

包摂基準というのは、文字図形を、符号化にあたって同定するための基準だ。要するに、文字図形→文字コードという変換のルールだ。一方、フォントは、文字を描画するためのプログラムで、文字コード→文字図形という、逆向きの変換を行う。

フォントは描画に関わるプログラムであって、符号化に関わるルールではない。そのようなルールは規格書に書かれているものであって、フォントの中に包摂基準は含まれていない。それゆえに、フォントをサブセット化するなどの改変を行ったからといって、包摂基準が変わるということもありえない。

フォントのサブセット化で包摂基準が変わると思うとしたら、それは文字コードの規格書とフォントをちゃんと区別していないということではないだろうか。

IPAmj明朝フォントと文字情報基盤は違う

フォントは、それ自体が包摂基準ではない。包摂基準のような概念は、フォント実装の外部に存在する。そのような概念を共有するのは、規格書の役割だ。たとえ規格書のコードチャートが特定のフォントで印刷されているとしても、フォントと規格は違うものだ。

実際のところ、文字情報基盤の規格書というものが存在して、正式に公開されているというわけではない。公開されているのは、OpenXML形式のMJ文字一覧表と、IPAmj明朝フォントだ。MJを利用するためには別途符号化のルールを規定した規格書が要るはずだが、簡単な利用ガイド程度の資料しか用意されていない。現状の文字情報基盤がこのような形態だから、文字情報基盤とIPAmj明朝フォントの違いが、よく分からなくなっている。

文字情報基盤を規格として捉えた上で、フォントの規格適合性を保証・認証できるようにしたい、というモチベーションがあるのはわかる。ただ、それを、フォントのライセンスであるIPAフォントライセンスでやろうとするのは、筋が悪すぎる。そもそも、IPAフォントライセンスは文字情報基盤整備事業の前にできたという時点で、派生フォントが規格に適合するかどうかということはIPAフォントライセンスと何も関係のない話だ。

包摂基準などを規定した仕様書が正式に策定されていないというのは、文字情報基盤自体の問題であるはずだ。たとえIPAmj明朝フォントの利用をライセンスで制限しようと、文字情報基盤の問題が解決するわけではない。それよりも、文字情報基盤の、文字コード規格としての不完全さを解消するべきだろう。

「IPAフォントライセンスを巡って」について思うところ 1

IPAフォントライセンスを巡って | 一般社団法人 文字情報技術促進協議会

IPAフォントライセンス1は、Open Source Initiative (OSI) から、The Open Source Definition (OSD) 2に準拠しているという認定を受けているという触れ込みだった。3 だから、ライセンスを理由にMJ明朝体フォントを利用したサービスを差し止めるというのは不可解なことに思える。いったい、これはどういうことなんだろうか。この記事を読んで、次のような疑問が浮かんだ。

  1. IPAmj明朝をWebフォントとして利用することは、IPAフォントライセンス上問題となるのか。
  2. IPAmj明朝をWOFF化するサービスは、IPAフォントライセンス上問題となるのか。
  3. サブセットフォントは包摂基準を変えるのか。

以下では、上記疑問のうち、1と2について考えてみたい。3については、別の記事にする。

以下、IPAフォントライセンスの条項を指す場合は、IPAフォントライセンスの表現に従い、列挙された項目を号ではなく項で表現する。また、OSD条文は八田真行(mhatta)による「オープンソースの定義」の日本語訳4に基づく。

1. IPAmj明朝をWebフォントとして利用することは、IPAフォントライセンス上問題となるのか。

IPAmj明朝が、複製その他の利用をされるケースとして、次の3つがある。

  1. 派生プログラムを再配布する
  2. 許諾プログラムをそのままの状態で改変することなく再配布する
  3. デジタル・ドキュメント・ファイルについて複製その他の利用をする

ケース1は2条4項または2条7項に該当して許諾が付与されるケースで、3条1項の制限を満たす必要がある。ケース2は2条6項に該当するケースで、3条2項の制限を満たす必要がある。ケース3は2条2項、3項および5項に該当するケースで、このケースでは3条1項および2項の制限を満たす必要がない。

Webフォントとしての利用がいずれのケースに該当するにしても、それぞれのケースについて必要な制限を満たしていれば、とうぜんWebフォントとして利用可能だ。

3条3項および4項に示されているのは免責条項なので、以下議論しない。

ケース1 派生プログラムを再配布する。

1のケースに該当する場合は条件が多い。

  • 3条1項(1)「派生プログラムを再配布する際には、下記もまた、当該派生プログラムと一緒に再配布され、オンラインで提供され、または、郵送費・媒体及び取扱手数料の合計を超えない実費と引き換えに媒体を郵送する方法により提供されなければなりません。」該当するデータを提供すればよい。
  • 3条1項(2)「派生プログラムの受領者が、派生プログラムを、このライセンスの下で最初にリリースされた許諾プログラム(以下、「オリジナル・プログラム」といいます。)に置き換えることができる方法を再配布するものとします。かかる方法は、オリジナル・ファイルからの差分ファイルの提供、または、派生プログラムをオリジナル・プログラムに置き換える方法を示す指示の提供などが考えられます。」この条項は、具体的に何を指して「置き換える」と言っているのかがよく分からない。アプリケーション上での表示フォントを置き換える、という意味であれば、サブセット化したWebフォントとローカルにインストールしたIPAmjフォントとで、表示を切り替えることができるようにする機構を入れておけば済むと思われる。
  • 3条1項(3)「派生プログラムを、本契約書に定められた条件の下でライセンスしなければなりません。」ライセンスすればよい。
  • 3条1項(4)「派生プログラムのプログラム名、フォント名またはファイル名として、許諾プログラムが用いているのと同一の名称、またはこれを含む名称を使用してはなりません。」該当箇所を適当にリネームすればよい。
  • 3条1項(5)「本項の要件を満たすためにオンラインで提供し、または媒体を郵送する方法で提供されるものは、その提供を希望するいかなる者によっても提供が可能です。」制約に関する条項なのに、文が「可能です」で終わっているのは不可解だし、意味がとれない。「その提供を希望するいかなる者」というのは、提供を行うことを希望する者なのか、それとも提供を受けることを希望するものなのか、それも分からない。3条1項(1)で示されている提供されなければならないものの提供方法はその提供の主体を問わず、他者から提供を受けることができるようにすればそれで条件を満たしているという但し書きなのだろうか。

3条1項(2), 3条1項(5)の意味に不可解な部分はあるが、満たすことは可能な条件だと思われる。

ケース2 許諾プログラムをそのままの状態で改変することなく再配布する

  • 3条2項(1)「許諾プログラムの名称を変更してはなりません。」
  • 3条2項(2)「許諾プログラムに加工その他の改変を加えてはなりません。」
  • 3条2項(3)「本契約の写しを許諾プログラムに添付しなければなりません。」

「本契約の写しを許諾プログラムに添付」の部分の解釈が微妙だ。これがZIPアーカイブ化であれば、同一アーカイブ内にライセンス本文が含まれていれば、これは確実に添付したと言えるだろう。では、OpenTypeフォントファイルをオリジナルの形式でWebサーバー上にアップロードしてある場合はどうだろうか? フォントファイルをWebフォントとして参照するページ自体に許諾プログラムが記載されていれば、それは添付と言えるだろうか? あるいはライセンス条文へのリンクが記載されている場合はどうか? ここで、アーカイブに含まれているのは添付だが、ライセンス条文へのリンクは添付ではないということになると、それは直感的には不条理な制限だし、OSD 10条「ライセンスは技術中立的でなければならない」に抵触するかもしれない。ここの部分が明確であれば、OpenTypeフォントをサブセット化せずそのままウェブフォントとして使うことは、容量の問題は存在するにせよ、技術的に可能なはずだ。

ケース3 デジタル・ドキュメント・ファイルについて複製その他の利用をする

このケースに該当する場合は、上記3条1項および2項の制限に従う必要がない。

WebアプリケーションにおけるWebフォントとしての利用は、どのケースに該当するのか

ライセンスでは、デジタル・コンテンツおよびデジタル・ドキュメント・ファイルは以下のような定義になっている。

  • 1条4項「「デジタル・コンテンツ」とは、デジタル・データ形式によってエンド・ユーザに提供される制作物のことをいい、動画・静止画等の映像コンテンツおよびテレビ番組等の放送コンテンツ、ならびに文字テキスト、画像、図形等を含んで構成された制作物を含みます。」
  • 1条5項「「デジタル・ドキュメント・ファイル」とは、PDFファイルその他、各種ソフトウェア・プログラムによって製作されたデジタル・コンテンツであって、その中にフォントを表示するために許諾プログラムの全部または一部が埋め込まれた(エンベッドされた)ものをいいます。フォントが「エンベッドされた」とは、当該フォントが埋め込まれた特定の「デジタル・ドキュメント・ファイル」においてのみ表示されるために使用されている状態を指し、その特定の「デジタル・ドキュメント・ファイル」以外でフォントを表示するために使用できるデジタル・フォント・プログラムに含まれている場合と区別されます。」

Webアプリケーションは、定義上は「デジタル・データ形式によってエンド・ユーザに提供される制作物」に該当するから「デジタル・コンテンツ」であるし、それにフォントを埋め込めば、それは「デジタル・ドキュメント・ファイル」となると思われる。しかし、これは本来ライセンスが意図した定義ではないかもしれない。すなわち、Webアプリケーション自体はとうぜん制作物であるにしろ、それによって表示されるテキストはWebアプリケーションに由来するとは限らず、たとえばチャットアプリとして、送信されたテキストをIPAmjフォントで表示するというようなケースでは、アプリに含まれないテキストを表示するためにフォントが使われることになる。そのようなアプリケーションは、制作物でありながらメディアなのだから、IPAフォントライセンス制定者の意図としてはケース3から除外したいもののように推察されるが、それにしては、ライセンス上の定義は不適切だと思われる。

「デジタル・コンテンツ」および「デジタル・ドキュメント・ファイル」の定義がそもそも不適切なので、フォントが埋め込まれたWebアプリケーションはおよそケース3に該当してしまうという解釈はできる。しかし、そう解釈しないにしても、本来想定されていたであろう外部のテキストの表示にフォントを用いない、真に「デジタル・ドキュメント・ファイル」と呼べるWebアプリケーションも存在する。そもそも、HTMLというのはアプリケーション以前に、文書を表現するためのフォーマットであるはずなのだから、Webアプリケーションのうち、真に「デジタル・ドキュメント・ファイル」であるものは存在する。

WebフォントがHTMLファイルの外部に保存されていて、そのファイルにリンクされている場合は、条文中の「エンベッド」に該当しない可能性も残ってはいる。しかし、Webフォントをdata URLの形でHTMLファイル中に埋め込むことも可能であり、この場合は「エンベッド」に該当することに争う余地はないだろう。

(そもそも細かいことを言えば、リンクの場合はエンベッドに該当しないという解釈を取ることは、どうなのだろう? HTMLファイルにdata URLをを埋め込むことと、単なるリンクにすることの間で、フォント抽出の難しさはほとんど差がなく、別の文書での再利用も簡単だ。ここで、data URLの場合は埋め込みで、リンクの場合は埋め込みではないということにする合理性はどれほど存在するだろうか?)

2. IPAmj明朝をWOFF化するサービスは、IPAフォントライセンス上問題となるのか。

IPAmj明朝のWOFF化は、ケース1に該当するのだから、3条1項の条件を満たせば、IPAmj明朝をWOFF化するサービスは可能だと思われる。そのサービスから提供されたWOFFフォントをWeb文書に埋め込んで利用する場合、これはケース3に該当し、3条1項および2項の条件によらず、利用できる。このとき、単にWOFFファイルへのリンクを埋め込む形では文書への埋め込みに該当しないという解釈がありうるが、バックエンドサーバーでWOFFサブセット化サーバーからフォントを取得して文書に埋め込むという処理を行うことでより確実にケース3に該当する形でWebフォントを利用できる。そもそも、今のブラウザーの仕組みではセキュリティ上の問題で、異なるサイト5 6間ではもはやWebフォントのキャッシュは共有されない7 8のだから、リンクを使ったWebフォント提供サービスというのは、もはやキャッシュ効率の点では価値がないので、その文書・そのページで使われるフォントを直接HTMLに埋め込む(インライン化する)という選択を取ることもあるだろうと思う。

MJ文字情報一覧 部首・内画数の誤りについて

MJ文字情報一覧の部首・内画数に誤りがあるので、現時点で気づいているものをここにメモしておく。(部首が異体字の部首になっているもののうち、内画数と総画数が一致しているものを含む。内画数に-が指定されているものは含んでいない。)

MJ000026
㐭
亠部6画
广部8画

MJ000499
㙄
土部7画
阜部10画

MJ000946
㠯󠄁
人部5画
己部2画

MJ001318
㦲
口部8画
戈部4画

MJ003904
䑑
人部18画
臣部12画

MJ003925
䑨
木部11画
舟部5画

MJ006832
倐󠄂
人部9画
犬部11画

MJ007269
兩󠄂
人部6画
入部8画

MJ007496
刦
刀部5画
力部7画

MJ007952
厩󠄊
厂部12画
广部14画

MJ009348
壐󠄂
玉部17画

MJ010288
𡭗
小部2画
爻部1画

MJ010857
帰󠄄
刀部8画
巾部7画
彑部7画
止部10画

MJ013046
斋
文部6画
示部10画

MJ016562
熔󠄃
火部10画
金部14画

MJ016795
牕󠄂
片部11画
穴部15画

MJ019072
稉󠄂
禾部7画
米部12画

MJ019186
穤
禾部14画
米部19画

MJ019768
籠󠄃
竹部16画
而部16画

MJ019851
粦󠄂
火部12画
米部6画

MJ021301
臯
白部12画
自部6画

MJ021531
芔󠄂
十部9画
艸部3画

MJ023019
藁󠄂
禾部18画
艸部14画

MJ024216
覊󠄂
网部25画
襾部19画

MJ024997
貟
口部9画
貝部2画

MJ026090
遡
水部14画
辵部10画

MJ026250
邨󠄃
木部7画
邑部4画

MJ028027
鞸
革部11画
韋部20画

MJ028062
韌󠄂
革部12画
韋部3画

MJ029194
鯖󠄃
宀部16画
魚部8画
鹿部8画

MJ029360
鱒
魚部12画
麻部12画

MJ029862
麿󠄃
魚部7画
麻部7画

MJ030124
龍󠄇
立部0画
龍部0画

MJ033043
𡌛󠄂
里部10画

MJ059404
𭅼
邑部10画

MJ059587
𫝸󠄀
彡部9画
立部4画

MJ059875
𤏁󠄂
火部13画
日部13画

MJ060005
𮄂
水部13画

MJ060014
𮄿
立部30画

MJ060316
𫕅
阜部8画
足部11画

㋿のヒストリー

ハックとデジタル社会

コンピューターで作られたシステムに問題があって、困っているとしよう。システムを作った人はここにいないし、一から作り直す時間はなかったり、そもそも触る権限がなかったりする。そういうシステムを、どうにかうまく使えるようにすることを、ハックという。

ここでいう「ハック」は、コンピューターを壊すとか、システムに侵入するという意味ではないし、単純にコンピューターに精通するというニュアンスでもない。責任の境界を超越して、システムをうまく動くようにしてしまう行為のことを言っている。いいかえれば、他人に依頼して直してもらうめんどくささを回避して、自分でシステムを直してしまうことだ。

たとえば、macOSの標準ブラウザーSafariには「サイト固有のハックを無効にする」という開発者向けの設定項目がある。これは、どういう意味だろうか?

世の中のWebサイトは、コンテンツを配信するWebサーバーと、それを受け取って表示するブラウザーの組み合わせでできている。Webサーバーとブラウザーでどうやってやりとりするかは取り決めがあって、それに従うことで、システムは機能している。だけど、ばあいによっては、Webサーバーとブラウザーのどちらかに問題があって、Webサイトが壊れる場合がある。

もしサーバーに問題があるとしても、Safariを使っている人はSafariが壊れていると思うだろうし、Safariを作っている人たちが世の中のWebサーバーを直してまわるわけにもいかない。手っ取り早く、サイトごとに特殊な取り扱いを追加することで直してしまうことを、ここでは「サイト固有のハック」と呼んでいるのだ。

Safariの開発者向け設定画面。「互換性:サイト固有のハックを無効にする」という設定項目がある。(オレンジ色で囲って強調した部分)

人間社会は責任の境界がある。そういう社会でシステムを直す「まっとうな方法」は、責任をもっている人に対してうまく交渉を行って、直してもらうということだろう。でも、世の中には、誰が責任を持っているのか分からないシステムや、誰も責任を持っていないシステムというものがある。交渉しても、失敗して目的が達成できないかもしれない、という不安がある。でも、そこを迂回して、「ハック」を行ってしまえば話は簡単になる。だれか他人の顔色をうかがうことなく、目的を達成できてしまうので、楽だし、効率的で、安くつく。むかし、コンビニのコーヒーメーカーのデザインが分かりづらすぎて、テプラの修正まみれになったこともあった。つまり、そういうことである。

ソフトウェアのハックには、ソースコードを共有する文化も重要だ。テプラがあるから簡単に製品の文言を修正できるのと同じように、ソースコードがあるから、ソフトウェアを簡単に修正できる。ソースコードがなければないでうまくやる方法はあるけど、難しくなってくる。ソースコードオープンソースの形で公開することは、私の了承など取らずとも自由にハックしてくださいという表明で、このようにしてハックを自由にやらせることが、結果的には有用であることを経験的に知っている。そして、ハックを自由にさせる文化は、いろいろな価値観と結びついている。

ハッカー文化は「私たちは問題に対して、責任の境界を気にせず直接的に取り組むことができる」という感覚に支えられている。その一方で、「問題の解決方法は、元の責任者に対してフィードバックされるべきだ」という感覚も兼ね備えている。無秩序なハックはさらなる追加のハックを困難にしてしまうし、ソフトウェア更新のたびに、ハックを適用するのは大変なので、最終的には、オープンソースソフトウェアの開発で「アップストリーム」と呼ばれる、元の開発責任者に対して、自分の行なった変更を適用してもらうように依頼する。テプラの例えでいえば、テプラを貼ってそれでよしとするのではなく、もとのコーヒーメーカーのデザインに反映してもらうような感じだ。その過程で、結局は交渉が必要になってくるんだろうけど、交渉して他人に直してもらうのと、自分で直した後に反映してもらえるよう交渉するのとだったら、後者の方が簡単な場合も多い。「直して」ではなく、「直しました」という形で、提案を行うのだ。何かを取り決めてから課題に取り組むのではなく、課題に取り組んでから取り決めるという順序の逆転を許容することが、より迅速で効果的な課題解決につながっている。

インターネットの根幹はハックに支えられている。世の中の無数に存在する、責任者の不明なシステムどうしをうまく繋げて通信できるようにする必要があるわけで、その技術は上から下まで、最初から考えられて設計されているわけではなく、テプラの修正のような後付けの技術にあふれている。もちろん、インターネットにもいろいろな標準規格が存在しているけど、規格で決めたこと以上に実際に動くことが大事だから、規格に合わせてシステムを直すという力だけではなく、システムに合わせて規格に直すという力が働くことになる。ASCIIしか想定していない古いシステムを置き換えずに世界中の文字を扱いたいから、UTF-8だとか、パーセントエンコーディングだとか、そういう技術が生み出されてくるし、そういうものが、規格になっていく。インターネットの世界で使われる規格というのは、国や団体が議論を尽くしてキレイな規格を定めたから広まるというよりは、既存のエコシステムに適応した、優秀なハックであるかどうかという点が有利に働くようにみえる。

そういうハックが支える技術や文化が背景知識としてあると、登大遊の「けしからん」「超正統派インチキ」というキーワードや、オードリー・タンがレナード・コーエンのAnthemを引用していう "There is a crack, a crack in everything. That's how the light gets in." というフレーズについての理解も、深まるのではないだろうか。責任境界の超越を問題とせず、自由に課題に取り組めないことを問題とする思想。問題がいつまでも解決されず絶望するのではなく、問題を自分で解決につなげられるという希望。システムを壊さないよう人間を管理するのではなく、人間が自発的にシステムを直せるよう自由にするという思考。そういったことを言っているのであり、そういった、ハックに親和的な考えを企業や社会に取り入れる重要性がそれぞれの考え方に表れているように、私は思う。

デジタル社会の実現には、技術に精通した優秀なハッカーが必要なわけではない。必要なのは、技術への精通ではなく、課題へのアクセシビリティだ。技術への精通は、課題に取り組み、ハックした結果として得られるものであって、ハックする前に必要なのではない。ハックは、最初は身近で簡単なところからはじめていけばよい。

デジタル社会を形成するハックという考え方は、そのままでほかの分野には適用できないかもしれない。リスクが高い領域だから自由にさせられないとか、システムは入札で調達するから取り決める前に取り組ませるのは難しいとか、いろいろと課題はあると思う。しかし、そういった課題だって、解決する方法は存在するのではないだろうか? ハックという行為を社会に組み込むというメタな課題について、ハックしてみるべきかもしれない。

所在表現と所有表現

これは、 位置の外延的表現・内包的表現の区別と考察ノート の続きのような話で、数学とプログラミングは、ときに、あるいみでは、物事をまったくあべこべに捉えているのではないだろうか? という話を書く。

前回の話について簡単に説明すると、プログラミングでは、位置を表現する方法には「外延的表現」と「内包的表現」があるという話をした。JSONで書けば { "8": { "4": "王将" } } が「外延的表現」、 { "王将": [4, 8] } が「内包的表現」で、その間にどういう違いがあるか簡単に考察を行なっている。

話をより一般的にすると、位置の表現に限らず、情報の表現全般には、位置を使った「所在表現(外延的表現)」と、属性を使った「所有表現(内包的表現)」が考えられる。コンピューターであれば、メモリアドレスによって表現される位置と、その位置であらわされるセルに格納された電荷などの属性の組み合わせを使って情報を表現しているのだから、表したい情報のいくらかは位置によって、残りの情報は属性によって、表現されている。

情報の表現には所在表現と所有表現がある考えると、次の図を2通りに、所在と所有それぞれの解釈で読み解けることに気づく。

flowchart LR subgraph A direction TB subgraph B direction RL C end end

この図は、オイラー図であるように見える。それは「*Xは*Bである」という命題を「XがBにある」図形で表す、所在表現だ。そして、Aの内部にBを、Bの内部にCを配置することで「*Cならば*Bであり、*Bならば*Aである」ことを表現する。(ここで、Aと書いたのは図の領域のことで、*Aと書いたのは、領域が表す物事だ。ここでのアスタリスク記号「*」は、記号のデノテーションを表すものだと考えてほしい。)

しかし、この図を逆さまに所有表現として読んで、「XがBをもつ」図形が「*Xは*Bである」という命題を表すと見ればどうか。Aの内部にBを、Bの内部にCを配置することで「*Aならば*Bであり、*Bならば*Cである」ことを表現している、と読める。これでも、つじつまはあっている。また、これは、プログラミングにおけるオブジェクトの表現によく似ている。

type A = { b: B }
type B = { c: C }
type C = {}
const p: (a: A) => B = (a) => a.b
const q: (b: B) => C = (b) => b.c
const r: (a: A) => C = (a) => a.b.c

日常生活で考えれば、「*Xである」という情報を「Xがある」という形で表現することは多い。「私は弁護士である」という情報を「私は弁護士バッジを持っている」という形で示すことがあるだろうし、「きのこに投票する」という情報を「投票用紙に『きのこ』と書く」という形で示すことがあるだろう。もちろん、「*Xである」という情報を「Xにある」という形で表現することも多くあり、「私の名前は弁護士会の名簿にある」とか「『きのこ』と書いてある枠にシールを貼る」という例が考えられる。

ここまでの話は抽象的だが、UIデザインやデータの可視化においては、考慮すべきことに思える。たとえば散布図などはまさに属性を位置に変換して表現するものだと言える。人間にとって理解しやすい表現を行うためには、どのように両者の変換を行うのかの考察は重要になるだろう。

漢字データベースを使って漢字ベン図を作問する

漢字ベン図は、QuizKnockがやっていた漢字クイズです。条件が3つ与えられるので、複数の条件に当てはまる漢字を答えていきます。

www.youtube.com

この記事では、漢字情報データベース Mojidata を活用して、漢字ベン図を作問してみようと思います。

github.com

MojidataはSQLiteというデータベースエンジンで使うことができるデータベースになっていて、情報をSQLで取得することができます。

データベースを使う準備

Mojidataを使うには、Node.jsとSQLiteをインストールしてあると楽です。

その後、ターミナルで次のコマンドを実行して、moji.dbをダウンロードし、sqlite3を起動してください。

# 作業用のディレクトリを作る
mkdir kanji-venn
# カレントディレクトリを変更する
cd kanji-venn
# npm パッケージの初期化(node_modulesを作業用ディレクトリに作成するため)
npm init -y
# mojidataパッケージのインストール
npm install @mandel59/mojidata
# SQLiteの起動
sqlite3 node_modules/@mandel59/mojidata/dist/moji.db

SQLiteが起動すると、次のようなプロンプトが表示されます。

SQLite version 3.40.0 2022-11-16 12:10:08
Enter ".help" for usage hints.
sqlite> 

個人的にはSQLiteCLIからだと少し使いづらいと思うので、普段は自作のErqというツールを使っています。これは補完機能が使え、SQLより簡単に書けるErqクエリ言語を使って情報を取得できます。開発途中で、マニュアル等はないのですが、Erqを使ってみたい場合は、こちらもnpmでインストールして使うことができます。次のコマンドでErqをインストールします。

# Erqのインストール
npm install github:mandel59/erq
# Erqの起動
npx erq node_modules/@mandel59/mojidata/dist/moji.db

Erqを起動すると、次のようなプロンプトが表示されます。

Connected to node_modules/@mandel59/mojidata/dist/moji.db
erq> 

他にDuckDBを使って読み込む方法や、GUIのツールを使う方法もあります。

漢字情報を取得してみる

作問するにあたって、漢字の次のような情報が取得したいです。

  • 常用漢字の一覧
  • 漢字の読み
  • 漢字の総画数
  • 漢字の部首
  • 漢字の構造

常用漢字の一覧と読みは、常用漢字表のデータを格納した joyo テーブルに保存されています。また、総画数はMJ文字情報一覧のデータを格納した mji テーブルから、部首は mji_rsindex テーブルから、漢字の構造は ids テーブルから、それぞれ取得できます。

erq> joyo limit 10;;
select * from joyo limit 10
["漢字","音訓","例","備考"]
["亜","ア","[\"亜流\",\"亜麻\",\"亜熱帯\"]",""]
["哀","アイ","[\"哀愁\",\"哀願\",\"悲哀\"]",""]
["哀","あわれ","[\"哀れ\",\"哀れな話\",\"哀れがる\"]",""]
["哀","あわれむ","[\"哀れむ\",\"哀れみ\"]",""]
["挨","アイ","[\"挨拶\"]",""]
["愛","アイ","[\"愛情\",\"愛読\",\"恋愛\"]","愛媛(えひめ)県"]
["曖","アイ","[\"曖昧\"]",""]
["悪","アク","[\"悪事\",\"悪意\",\"醜悪\"]",""]
["悪","オ","[\"悪寒\",\"好悪\",\"憎悪\"]",""]
["悪","わるい","[\"悪い\",\"悪さ\",\"悪者\"]",""]
10 rows (0.011s)

読み情報ビュー kanji_reading を定義して、必要な情報だけを使いやすくします。

erq> view temp.kanji_reading = joyo {k: 漢字, r: 音訓};;
create view `temp`.kanji_reading as with kanji_reading as (select 漢字 as k, 音訓 as r from joyo) select * from kanji_reading
ok (0.002s)
erq> kanji_reading limit 10;;
select * from kanji_reading limit 10
["k","r"]
["一","ひと"]
["一","ひとつ"]
["一","イチ"]
["一","イツ"]
["丁","チョウ"]
["丁","テイ"]
["七","なな"]
["七","ななつ"]
["七","なの"]
["七","シチ"]
10 rows (0.000s)

総画数と部首、構造を取得するビューも定義します。

総画数情報ビュー kanji_strokes の定義

erq> view temp.kanji_strokes = mji[漢字施策='常用漢字']{k: 実装したUCS, s: 総画数};;
create view `temp`.kanji_strokes as with kanji_strokes as (select 実装したUCS as k, 総画数 as s from mji where (漢字施策 = '常用漢字')) select * from kanji_strokes
ok (0.000s)
erq> kanji_strokes limit 10;;
select * from kanji_strokes limit 10
["k","s"]
["一",1]
["丁",2]
["七",2]
["万",3]
["丈",3]
["三",3]
["上",3]
["下",3]
["不",4]
["与",3]
10 rows (0.001s)

部首情報ビュー kanji_radical の定義

erq> view temp.kanji_radical = mji[漢字施策='常用漢字'] -:MJ文字図形名:> mji_rsindex -:部首:> radicals {k: 対応するUCS, rad: 部首漢字};;
create view `temp`.kanji_radical as with kanji_radical as (select 対応するUCS as k, 部首漢字 as rad from mji join mji_rsindex on mji.MJ文字図形名 = mji_rsindex.MJ文字図形名 join radicals on mji_rsindex.部首 = radicals.部首 where (漢字施策 = '常用漢字')) select * from kanji_radical
ok (0.000s)
erq> kanji_radical limit 10;;
select * from kanji_radical limit 10
["k","rad"]
["一","一"]
["丁","一"]
["七","一"]
["万","一"]
["丈","一"]
["三","一"]
["上","一"]
["下","一"]
["不","一"]
["与","一"]
10 rows (0.001s)

構造情報ビュー kanji_ids の定義

erq> view temp.kanji_ids = ids[UCS in joyo{漢字}]{k: UCS, ids: IDS} distinct;;
create view `temp`.kanji_ids as with kanji_ids as (select distinct UCS as k, IDS as ids from ids where (UCS in (select 漢字 from joyo))) select * from kanji_ids
ok (0.000s)
erq> kanji_ids limit 10;;
select * from kanji_ids limit 10
["k","ids"]
["一","一"]
["丁","⿱一亅"]
["七","〾⿻乚一"]
["万","⿸丆𠃌"]
["丈","⿻𠂇乀"]
["三","三"]
["上","⿱⺊一"]
["下","⿱一卜"]
["不","⿸丆⿰丨丶"]
["不","⿻丆卜"]
10 rows (0.003s)

クイズを作問する

ここまでできれば、あとは、条件に当てはまる漢字を取得するクエリを作るだけです。先の動画の例題で言えば、「さんずい」「9画」「「せ」から始まる」といった条件は、漢字をxとすれば、SQLとErqではそれぞれ次のように表現できます。

  • さんずい
    • SQL: x in (select k from kanji_ids where ids glob '⿰氵*')
    • Erq: x in kanji_ids[ids glob '⿰氵*']{k}
  • 9画
    • SQL: x in (select k from kanji_strokes where s = 9)
    • Erq: x in kanji_strokes[s = 9]{k}
  • 「せ」から始まる
    • SQL: x in (select k from kanji_reading where r glob 'せ*' or r glob 'セ*')
    • Erq: x in kanji_reading[r glob 'せ*' or r glob 'セ*']{k}

常用漢字 x についてそれぞれ判定し、複数の条件に当てはまるものを表示すれば作問ができそうです。Erqでクエリを作ってみます。

/* 常用漢字を x という名前で取り出す */
joyo {x: 漢字} distinct
/* 各漢字について、条件を判定する */
{
  x,
  `さんずい`: x in kanji_ids[ids glob '⿰氵*']{k},
  `9画`: x in kanji_strokes[s = 9]{k},
  `「せ」から始まる`: x in kanji_reading[r glob 'せ*' or r glob 'セ*']{k}
}
/* 複数の条件にあてはまる漢字のみ残す */
[`さんずい` + `9画` + `「せ」から始まる` >= 2]
/* あてはまる条件でグループ化 */
{ `さんずい`, `9画`, `「せ」から始まる` => group_concat(x) }
;;

これを入力すると:

erq> /* 常用漢字を x という名前で取り出す */
joyo {x: 漢字} distinct
...> /* 各漢字について、条件を判定する */
...> {
...>   x,
...>   `さんずい`: x in kanji_ids[ids glob '⿰氵*']{k},
...>   `9画`: x in kanji_strokes[s = 9]{k},
...>   `「せ」から始まる`: x in kanji_reading[r glob 'せ*' or r glob 'セ*']{k}
...> }
...> /* 複数の条件にあてはまる漢字のみ残す */
...> [`さんずい` + `9画` + `「せ」から始まる` >= 2]
...> /* あてはまる条件でグループ化 */
...> { `さんずい`, `9画`, `「せ」から始まる` => group_concat(x) }
...> ;;
select `さんずい`, `9画`, `「せ」から始まる`, group_concat(x) from (select x, x in (select k from kanji_ids where (ids glob '⿰氵*')) as `さんずい`, x in (select k from kanji_strokes where (s = 9)) as `9画`, x in (select k from kanji_reading where (r glob 'せ*' or r glob 'セ*')) as `「せ」から始まる` from (select distinct 漢字 as x from joyo) where (`さんずい` + `9画` + `「せ」から始まる` >= 2)) group by (`さんずい`), (`9画`), (`「せ」から始まる`)
["さんずい","9画","「せ」から始まる","group_concat(x)"]
[0,1,1,"宣,専,政,施,星,染,泉,牲,狭,省,窃,背"]
[1,0,1,"清,潜,瀬"]
[1,1,0,"洋,洞,津,洪,活,派,浄,海"]
[1,1,1,"洗,浅"]
4 rows (0.017s)

コマンドラインからクエリを実行する

先ほどは手でクエリを入力していましたが、毎回同じように手で入力するのは面倒なので、次のクエリを kanji-venn.erq ファイルに保存しておき、コマンドで実行してみます。

view temp.kanji_reading = joyo {k: 漢字, r: 音訓};;
view temp.kanji_strokes = mji[漢字施策='常用漢字']{k: 実装したUCS, s: 総画数};;
view temp.kanji_radical = mji[漢字施策='常用漢字'] -:MJ文字図形名:> mji_rsindex -:部首:> radicals {k: 対応するUCS, rad: 部首漢字};;
view temp.kanji_ids = ids[UCS in joyo{漢字}]{k: UCS, ids: IDS} distinct;;
/* 常用漢字を x という名前で取り出す */
joyo {x: 漢字} distinct
/* 各漢字について、条件を判定する */
{
  x,
  p1: x in kanji_ids[ids glob '⿰氵*']{k},
  p2: x in kanji_strokes[s = 9]{k},
  p3: x in kanji_reading[r glob 'せ*' or r glob 'セ*']{k}
}
/* 複数の条件にあてはまる漢字のみ残す */
[p1 + p2 + p3 >= 2]
/* あてはまる条件でグループ化 */
{p1, p2, p3 => group_concat(x)}
;;
npx erq node_modules/@mandel59/mojidata/dist/moji.db < kanji-venn.erq
$ npx erq node_modules/@mandel59/mojidata/dist/moji.db < kanji-venn.erq            
Connected to node_modules/@mandel59/mojidata/dist/moji.db
view temp.kanji_reading = joyo {k: 漢字, r: 音訓};;
view temp.kanji_strokes = mji[漢字施策='常用漢字']{k: 実装したUCS, s: 総画数};;
view temp.kanji_radical = mji[漢字施策='常用漢字'] -:MJ文字図形名:> mji_rsindex -:部首:> radicals {k: 対応するUCS, rad: 部首漢字};;
view temp.kanji_ids = ids[UCS in joyo{漢字}]{k: UCS, ids: IDS} distinct;;
/* 常用漢字を x という名前で取り出す */
joyo {x: 漢字} distinct
/* 各漢字について、条件を判定する */
{
  x,
  p1: x in kanji_ids[ids glob '⿰氵*']{k},
  p2: x in kanji_strokes[s = 9]{k},
  p3: x in kanji_reading[r glob 'せ*' or r glob 'セ*']{k}
}
/* 複数の条件にあてはまる漢字のみ残す */
[p1 + p2 + p3 >= 2]
/* あてはまる条件でグループ化 */
{p1, p2, p3 => group_concat(x)}
;;
create view `temp`.kanji_reading as with kanji_reading as (select 漢字 as k, 音訓 as r from joyo) select * from kanji_reading
ok (0.024s)
create view `temp`.kanji_strokes as with kanji_strokes as (select 実装したUCS as k, 総画数 as s from mji where (漢字施策 = '常用漢字')) select * from kanji_strokes
ok (0.000s)
create view `temp`.kanji_radical as with kanji_radical as (select 対応するUCS as k, 部首漢字 as rad from mji join mji_rsindex on mji.MJ文字図形名 = mji_rsindex.MJ文字図形名 join radicals on mji_rsindex.部首 = radicals.部首 where (漢字施策 = '常用漢字')) select * from kanji_radical
ok (0.000s)
create view `temp`.kanji_ids as with kanji_ids as (select distinct UCS as k, IDS as ids from ids where (UCS in (select 漢字 from joyo))) select * from kanji_ids
ok (0.000s)
select p1, p2, p3, group_concat(x) from (select x, x in (select k from kanji_ids where (ids glob '⿰氵*')) as p1, x in (select k from kanji_strokes where (s = 9)) as p2, x in (select k from kanji_reading where (r glob 'せ*' or r glob 'セ*')) as p3 from (select distinct 漢字 as x from joyo) where (p1 + p2 + p3 >= 2)) group by (p1), (p2), (p3)
["p1","p2","p3","group_concat(x)"]
[0,1,1,"宣,専,政,施,星,染,泉,牲,狭,省,窃,背"]
[1,0,1,"清,潜,瀬"]
[1,1,0,"洋,洞,津,洪,活,派,浄,海"]
[1,1,1,"洗,浅"]
4 rows (0.032s)

数列の内包的記法について

公理的集合論についてほとんどなにも知らないからこれから書くことは間違っているのかもしれないけど、古典的なクラスや集合の記法は公理的集合論の元では複数の公理に対応していると理解している。ZF公理系の分出公理に対応する記法として

\left\lbrace x \in X \,\middle|\, P(x) \right\rbrace

というような記法が考えられる。これがふつうは内包的記法と呼ばれているんだけど、別の置換公理に根拠を持つ記法も考えられて、

\left\lbrace f(x) \,\middle|\, x \in X \right\rbrace

のような書き方ができる。この両者を内包的記法と呼ぶのは紛らわしいから、分出公理に基づく記法を分出記法、置換公理に基づく記法を置換記法を置換記法と呼ぶことにする。(x \in Xは命題であると考えれば、命題が左側に来る分出記法は元来のクラスの記法のルールを破っているように思えるが、左辺に来るx \in Xは分出記法の一部として扱われる。それと同様に、置換記法の右辺に来るx \in Xも命題ではなく、置換記法の一部として扱われる。)

ところで、数列があったとして、その数列の各項を2倍した数列をどう書けばいいだろうか。

まあふつうに

a = \left( a_n \right)_{n \in \mathbb{N}}

b = \left( 2 a_n \right)_{n \in \mathbb{N}}

でもよいのだが、内包的記法のように、簡単に書きたい。Pythonであれば、

a = [1, 2, 3]
b = [2*x for x in a]

と書くのだから、数式でも

b = \left( 2 x \,\middle|\, x \in a \right)

とするかとも思うが、しかし、x \in aというのは気持ちが悪いと言うか、aは数列であり、数列は写像なのだから、x \in aと書いたときのxは、aの数列としての項ではなくaの集合としての元であり、aの集合としての元とは添え字と項のペアであるのでは、という気持ちがある。そうすると

b = \left( 2 x \,\middle|\, (k, x) \in a \right)

と書くのか。ここまでするのなら、いっそ

b = \left\lbrace (k, 2 x) \,\middle|\, (k, x) \in a \right\rbrace

と書いてしまって、ふつうに集合の置換記法で書いてしまってもいい気がする。しかし、操作したいのは数列なのに、これでは数列を表す写像を表す集合を操作していることになってしまう。x \in aの気持ち悪さが問題であれば、矢印にしてしまって

b = \left( 2 x \,\middle|\, x \leftarrow a \right)

でもいいかなあ。わかりづらいか。