嘘をつき始めた地図盤
俺はガロ。この町で、地図を彫って暮らしている。
仕事場には、先代から受け継いだ古い地図盤がある。町じゅうの道と角を写し取った一枚で、その上に標(しるべ)を灯しておくと、配達人や旅人が、それを頼りに歩く。井戸はここ、集会所はここ、薬屋はあの角。配達人は毎朝、この盤を写し取って懐に入れ、町へ散っていく。
地図ってのは、嘘をついちゃいけねえ。道が一本ずれれば、荷も人も迷う。俺はそれだけを守って、四十年、墨と膠(にかわ)の匂いの中で生きてきた。
ところが先日、その地図盤が、嘘をつき始めた。
井戸の位置を、測り直したんだ。古い井戸が涸れて、二歩ばかり東に掘り直された。だから盤の上の井戸の標を、二歩、東へ動かした。それだけのことだ。
翌朝、薬の急ぎ便が、井戸の家じゃなく、集会所へ届いた。井戸の家の婆さんに薬が渡るのが、半日遅れた。
俺は集会所の標になんざ、指一本触れちゃいない。なのに、集会所の標が、二歩、東へずれていた。井戸を動かしたら、集会所まで動いていた。
標が、ひとりでに動く。俺の地図が、俺の知らねえ場所を指す。職人にとって、これ以上気味の悪いことはねえ。明日の朝も、配達人はこの盤を写して出ていく。それまでに直さなけりゃ、誤配が町じゅうに散らばる。だが、何をどう直せばいいのか、俺には見当もつかなかった。
テオの名指しで、丘をのぼる
心当たりは、一つだけあった。
少し前、蔵書庫の司書のテオが、わざわざ俺に言ってきたんだ。「丘の上のアカデミーに、腕のいい見習いがいる。うちの凍りついた検索台を、あの子が直してくれた。地図のことで困ったら、診てもらえ」と。テオは世辞を言う男じゃない。あいつがそう言うなら、と、俺は半信半疑のまま、地図盤を写した符を抱えて丘をのぼった。
アカデミーの工房は、見上げるほどの書架がぐるりと壁を覆い、宙には埃のように光の粒が漂っている。墨と紙の匂いで四十年食ってきた俺には、まるで縁のない眺めだ。それでも、壁にかかった古地図の写しにだけは、つい目が留まった。誰かが手で写し取ったものらしい。線の引き方に、職人の覚えがあった。
工房の奥では、若いのが一人、卓いっぱいに図面を広げて、その上に屈み込んでいた。俺が入ると、顔を上げて立つ。「いらっしゃい」。物言いは丁寧だが、その顔つきには、まだ少年めいた稚さが残っていた。これが、テオの言っていた見習い——ルークか。正直、拍子抜けした。この若さで、本当に直せるのか。
俺は地図盤の符を見せて、症状を話した。井戸を二歩動かしたら、触ってもいない集会所まで動いた、と。
ルークは符を膝に広げ、指で一行ずつ追いながら、まず盤に出てくる「もの」が何かを、俺に教えてくれた。
| |
「Point が座標——盤の上の、どこか一点です。Landmark が標(しるべ)。名前と、位置(location)を持っています」。ルークは符の一語を指でなぞった。「この var という印は、『あとから書き換えられる』という意味です。位置が var——だから、立てたあとでも、座標を差し替えられる。名前のほうは val、こっちは『一度決めたら書き換えられない』印です」。
名前は変わらねえが、位置は動かせる。まあ、地図の標なんだから、動かせて当たり前だ。俺はそう思った。
「井戸の座標を、元にして、集会所をこしらえた。そうですね」。ルークが俺に確かめる。図星だった。井戸と集会所は隣同士だ。一から測り直すのも面倒で、井戸の座標を元にして、集会所の標を立てた。
「これが、おそらく原因です」。ルークは、井戸と集会所が、同じ一つの座標を「使い回している」ことを、俺にも分かるように言い換えてくれた。「同じ一本の杭に、二枚の名札を下げたようなものなんです。井戸の名札と、集会所の名札。けれど杭は、一本きり。だから、杭を動かせば——二枚とも、動く」。
ひとつの実体を、二つの名前で指している。こういう状態を エイリアシング(一つの同じものを、複数の名前で共有していて、片方をいじると、もう片方まで一緒に変わってしまう状態)と呼ぶのだと、ルークは言い添えた。
俺は、苦い気持ちで腕を組んだ。「写したつもりが……同じもんを、二度、名付けただけか」。集会所は、初めから井戸の影法師だったわけだ。
二段構えの大魔法
ところが、だ。
ルークの目に、ふっと火が点った。覚えたばかりの大層な魔法を、披露できる好機とでも思ったのか、袖をまくり、杖を一振りした。
「お任せください、ガロさん。二度と勝手に動かぬよう、座標に見張りをつけ、変化を全員に知らせる仕掛けを編みます。二段構えの、大魔法です」
まず一段目。ルークは、座標を勝手に書き換えられないよう、検証の関門で囲い始めた。値を入れるときに必ずこの関門を通し、おかしな値——盤の外を指すような数——を、その場で撥ねる。そういう見張りだ。ルークが宙に描いた符は、こんな具合だった。細かい綴りは、追わなくていい。盛ったのは、二つの仕掛けだ。
| |
値を見張るのは、まあ、分かる。おかしな座標が紛れ込まねえように、入り口で検める。俺の仕事でも、刷る前に版の傷を検める。それと同じだ。俺は一旦、頷いた。
だが、二段目で、雲行きが怪しくなった。ルークは、それぞれの座標に「この座標を使っている標」の控え(followers)を持たせ、座標が動いたら、その控えの全員に「追従せよ」と触れ回る伝令の網を、宙に張り巡らせ始めたんだ。盤の上に、光の糸が、見ているそばから増えていく。
「俺が頼んだのは」と、俺は口を挟んだ。「地図が嘘をつかねえようにすること、それだけだ。なんで、こんな、糸だらけになる」。
生き物みたいに暴れる盤
ルークは伝令の網を組み上げ、盤に魔力を通した。さあどうぞ、という顔だ。俺は、井戸を二歩、東へ動かしてみた。
とたんに、盤が、ざわめいた。
井戸が東へ動く。すると伝令が走り、集会所が井戸を追って、東へ滑った。検証の関門は、値そのものはまともだから、何も撥ねない。集会所が動くと、その動きがまた次の伝令を呼び、近くの標が身じろぎする。それがまた、次を呼ぶ。盤じゅうの標が、生き物みたいに、次々と位置を変えてはちらつき始めた。
俺は背筋が寒くなった。見張りも、伝令も、座標が「まともな値か」は検めている。だが、井戸と集会所が同じ一本の杭を共有している、その一点には、誰も触れちゃいない。だから集会所は、相変わらず井戸にくっついて動く。伝令の網は、そのくっつきを、せっせと盤じゅうに広げているだけだった。元の不具合は、直るどころか、増幅して暴れている。
そのとき、工房の奥から、低い声がした。作業台で、白髪の老人が一人、古い地図を、手で別の紙に写し取っている。顔も上げずに、独り言のように、こう呟いた。
「写しは、別の紙にとるものでな。同じ一枚に二度書けば……滲むぞ」
ルークは、もつれた伝令の糸を手繰るのに必死で、聞いちゃいない。俺も、奥の古参の職人が、自分の仕事に呟いただけだろうと、気に留めなかった。
「ルークさん」と俺は焦れて言った。「明日の朝は、どうなる」。
ルークは、自分の張った糸を手繰り寄せようとして、どれが井戸の糸で、どれが集会所の糸か、分からなくなっていた。「あれ……この糸は井戸の……いや、集会所の追従が、こっちの糸を引いて……」。指先が、宙で迷子になっている。最初に俺が持ち込んだ符より、よっぽど厄介な代物になっていた。ルークは、とうとう頭を抱えてしまった。これじゃ、明日どころじゃねえ。
写しを取る老人
その奥の老人が、写し取りの筆を、ことりと置いた。
そして、俺にでも、盤にでもなく、糸に絡まったルークに向かって、穏やかに声をかけた。ルークが弾かれたように振り返り、ばつ悪そうに、ちいさく「……お師匠さま」と呼んだ。それで俺は、ようやく察した。奥で黙々と古地図を写していたあの老人は、ただの古参職人じゃない。この見習いの、師匠だ。大魔術師の、マグナス様。
マグナス様は、もつれた伝令の網には、目もくれなかった。ただ、ルークに一つ、問いを置いた。
「見習いや。お前さんはさっきから、動いてしまう印を、必死で見張っておるな。伝令を立て、関門を立て。だが——そもそも、二度と書き換えられぬ印だったら、追いかけて、狂いようが、あるかね?」
そして、短く言い足した。
「壊れない器とは、書き換えられない器のことだよ」
マグナス様は、それきり何も教えず、また筆を取って、古地図の写しに戻ってしまった。
ルークは、しばらく、宙の伝令の糸を見つめていた。それから、片端から、その糸を手繰って、消し始めた。さっきまで、足す一方だった手が、削ぐ手に変わっている。
「書き換えられないなら……見張る相手が、いない」。糸を消しながら、ルークは、自分に言い聞かせるように続けた。「動いてしまうから、見張る。違う。動かせなく、すればいい。そうすれば、見張りも、伝令も、要らないんだ」。
書き換えられない器
俺は、思わず口を出した。
「待ってくれ。書き換えられねえ地図なんて、使い物になるか。井戸は、本当に動いたんだ。店も道も、町は変わる。直せねえ地図に、何の値打ちがある」。
職人が、動かせねえ道具を渡されて、喜ぶわけがねえ。
ルークは、首を横に振った。
「動かせない、んじゃないんです。動かすときに、元の座標を書き換えるんじゃなく、新しい座標を作って、標に差し替える。やり方を、変えるだけです」。
彼は、盤の符を、こう書き直した。
| |
コードの頭にある init { ... } の囲みは、いったん措いておいていい。あとでルークが触れてくれる。まず彼が指したのは、座標に付いた var と val のほうだった。
「var だった座標を、val に変えました。val は『一度決めたら、もう書き換えられない』印。これで、Point は生まれたあと、誰にも書き換えられません」。
頭についた data class は、値をひとまとめに持つための器を手早く作る書き方で、こうして作った器は「中身が同じなら、同じものとして扱える」のだという。座標が同じ二つの点は、別々に作っても、等しい点と見なされる。実体が同じかどうかではなく、持っている値で等しさを決めるこの仕組みを、構造的等値(同じ実体かどうかではなく、中に持っている値が同じかどうかで「等しい」を決めること)と呼ぶ、とルークは言い添えた。後の答え合わせで、これが効いてくるらしい。
「では、井戸を動かすには」。ルークは指を一本立てた。「data class には、copy という道具が付いてきます。それを使うんです」。
井戸の標を well、その座標を wellSpot(いまは (10, 20))と呼ぶことにする。
| |
「copy は、元の器はそのままに、一部だけ変えた新しい器を作る道具です。wellSpot.copy(x = 12) は、『元の (10, 20) を下敷きに、x だけ 12 にした、新しい点 (12, 20)』。元の (10, 20) には、指一本、触れていません」。動かした後の井戸が、この movedWell だ。元の well と、それを使い回した集会所のほうは、まだ (10, 20) の点を指したまま——どちらにも、指は触れていない。
ここで、ルークは符から顔を上げた。
「ガロさんが、井戸を『写したつもり』だったのは——実は、写しじゃなかったんです。同じ点に、二枚目の名札を下げただけ。こっちが」と、彼は copy を指した。「本物の写しです。別の紙に、別の点として、写し取る」。
奥で、マグナス様の筆が、紙の上を滑る音がした。写しは、別の紙にとるもの。さっきの呟きが、今になって、俺の腑に落ちた。
「だから」とルークは続けた。「同じ点を共有していても、もう怖くありません。誰も書き換えられないんですから。動かしたい標だけ、写しを作って差し替える。残りの標は、元の点を指したまま——勝手には、動きません」。
ルークは、卓の上の羊皮紙に、さらさらと簡単な図を描いて見せた。 俺たちの古い地図盤で起きていた怪現象と、今ルークが組み立て直した魔導式の違いが、その図を見ると一目で分かった。

なるほどな。前のやり方じゃ、井戸と集会所という二つの標が、同じ一つの座標という杭に無理やり結びつけられていた。だから杭を動かせば両方動く。 だが新しいやり方では、杭(座標)そのものは地面に固く打ち付けられて動かない。井戸を動かしたいなら、元の杭はそのままにして、新しく別の場所に杭を打ち直し、井戸の標だけをそっちに結び変える。集会所は元の杭に結ばれたままだから、知らぬ間に動くことなんてあり得ない。
俺は、ゆっくり呑み込んだ。共有が悪さをしたのは、誰かが書き換えたからだ。書き換えがなけりゃ、いくつの標が同じ点を指してようと、何も起きやしない。
だが、俺はもう一つ、引っかかっていた。
「だったら、最初から、気をつけて、集会所には別の点を立てときゃ、良かっただけじゃねえのか。なんでわざわざ、書き換えられねえようにする」。
ルークは、少し考えて、こう答えた。
「気をつけて、毎回、別に写す——それでも、防げます。でも、一度でも写し忘れたら、今朝の誤配が、また起きる。さっき僕が立てた見張りや伝令も、結局は『ずっと気を張り続ける』仕掛けでした。各所で見張るのも、毎回写し忘れないのも、同じことなんです。一度でも気を抜いたら、狂う」。
気を抜いたら狂う。その一言が、俺の臓腑に刺さった。それは、俺たち職人が、一番恐れることだ。
「だから」とルークは言った。「気を抜いても狂わないように、そもそも書き換えられなくするんです。見張りを増やすんじゃなく、見張る必要を、無くす」。
最後に、ルークは符の頭の一行を指した。座標を作るときの、関門だ。
| |
「値を見張る、という最初の考えは、間違いじゃなかったんです。ただ、書き込むたびに、あちこちで見張るから、絡まった。生まれる一瞬に、一度だけ検めればいい。一度生まれたら、二度と変わらないんですから、それで永く、正しいままです」。頭についた init は、器が生まれる一瞬に一度だけ走る囲みだ。その中の require は、決めた条件を満たさなけりゃ、その場で声を上げて——IllegalArgumentException(引数が不正だ、という報せ)を投げて——止める見張りで、ここでは「地図の外(負の歩)には立てられない」を検めている。これは座標を直す話とは別で、「その点が、そもそもまともな点か」という、値そのものの作法なのだという。
書き換えられない器にして、変えるときは写しを作る。この作りを、不変オブジェクト(一度作ったら中身が変わらないオブジェクト。値そのものを表し、変更は新しいインスタンスを作って表す。値オブジェクトとも呼ぶ)と呼ぶのだと、ルークは教えてくれた。
結界で一度、刷ってみる
「盤に彫り込む前に、結界の中で一度、刷ってみます。図面どおりか、検めるんです」。
ルークはそう言って、模擬詠唱に取りかかった。盤に本彫りする前に、結界の中で幾度も刷り試し、狂いがないかを検めておく段取りらしい。俺の目の前で、刷り上がりを一枚ずつ並べてみせながら、ゆっくりと確かめていった。
| |
頭の import は、Kotlin に備え付けの、刷り上がりを検める道具を呼び出す宣言だ。assertEquals は「刷り上がりが、図面どおりか」を照らし合わせる。assertFailsWith は「狙ったとおり、ちゃんと撥ねるか」を検める。
井戸と集会所は、最初、同じ点を共有している。そのうえで、井戸だけを copy で二歩東へ動かす。井戸は (12, 20) になり——集会所は、(10, 20) のまま、動かない。先日、勝手に動いて誤配を生んだ、あの一点だ。今度は、びくともしない。
同じ (10, 20) は、別々に作っても、等しい点と見なされる。けれど、それは「同じ実体」という意味じゃない。中身が同じ、というだけで、実体は別物だ。assertNotSame が、その別物であること——同じ値でも、別に作れば別の実体になること——を、わざわざ確かめている。値が同じでも、実体は分かれる。だから、井戸を copy で動かしても、同じ点を指していたはずの集会所は、びくともしなかった。copy が、影法師じゃなく、本物の写しを別に作っている証だ。
最後の一つは、わざと壊れた点を作ろうとする試しだった。盤の外を指す (-1, 0) を渡すと、座標は、その場で作られることを拒んだ。おかしな点は、生まれる入り口で撥ねられる。
一つずつ、俺の目の前で、図面どおりの刷り上がりが出た。最後に、結界の中へ「ALL TESTS PASSED」——みな、狙いどおり——と灯った。
動かない、という安心
整え直された地図盤を、俺は受け取った。さっきまで盤を覆っていた伝令の糸も、検証の関門の囲いも、跡形もなく消えている。盤は、元の素っ気ない静けさに戻っていた。標は、もう、ひとりでに動かない。
俺は、節くれだった指で、盤の縁をそっとなぞった。動かないってことが、こんなに有り難えとはな。地図ってのは、こっちが動かさねえ限り、動いちゃならねえんだ。道具ってのは、置いた場所に、置いたまま在る。それが、信用ってもんだ。四十年やってきて、今さら、若い見習いに教わるとは思わなかった。
俺はもう一度、自分の手で、井戸を二歩、東へ動かしてみた。井戸の標だけが、すっと東へ移る。集会所は——寸分も、動かない。先日とは、まるで逆だ。荷は、ちゃんと、井戸の家へ行くだろう。
盤を抱えて、ふと、俺は思いついて尋ねた。
「じゃあ、これは。同じ井戸を、何枚もの配達人の地図で使い回しても、もう、狂わねえのか」。
ルークは頷いた。「はい。誰も書き換えられないなら、いくつの地図で共有しても、安全です」。
俺は、少し笑った。使い回しは、つい先刻まで、誤配を生んだ元凶だった。それが今は、楽をするための、安全な道に変わっている。井戸の点は、本物を一つ作っておけば、何枚の地図にでも、安心して配れる。壊れる元だったものが、そっくりそのまま、手間を省く味方になった。
奥で、マグナス様が、写しの筆を止めずに、ぽつりと言った。
「動かせぬ印は、狂わぬ印だ。動かしたくば、書き換えるな。写しを一枚、新たに描けばよい」。
俺は短く礼を言って、盤を抱え、丘を下りた。明日の朝、配達人は、いつものようにこの盤を写し取って、町へ出る。標は、もう嘘をつかない。職人の地図が、職人の知らねえ場所を指すことは——もう、ない。
📖 見習い魔術師ルークの魔導書メモ(Luke’s Spellbook Notes)
- 修復した呪文(クラス/モジュール名):
Point(座標)とLandmark(地図上の標) - 乱れの要因(アンチパターンの課題):
- 可変な
Point(var)を、井戸と集会所の二つの標で「使い回し」ていた。同じ一つの実体を二つの名前で共有していたため(エイリアシング)、井戸の座標を書き換えると、触れていない集会所まで一緒に動き、誤配を生んだ。「写したつもり」が、実は同じ点への二枚目の名札だった。
- 可変な
- 過剰設計の反省(背伸びの過ち):
- 値を守ろうと、検証の関門と「変化を全員に知らせる」伝令網を盛った。だが関門は値の妥当性しか見ず、二つの標が同じ点を共有している根本に触れない。通知は移動を両方へ波及させ、共有をかえって全体へ広げ、盤じゅうの標が連鎖して飛んだ。症状を増幅し、原因に触れていなかった。各所で見張り続けるのは、「毎回コピーを忘れない」のと同じ、終わらない警戒だった。
- 魔術の定石(学んだ設計パターン):
- 不変オブジェクト(値オブジェクト)。
data class Point(val x, val y)で生成後は書き換え不可にし、変更はcopy()で新しい器を生む。共有しても誰も書き換えないので安全(共有が危険なのは、書き換えが起きるときだけ)。不変なら「気を抜いても狂わない」——警戒そのものが要らなくなる。値の妥当性はinit { require(...) }で生成の一瞬に一度だけ検める(値オブジェクト本来の作法。不変だから一度で永く効く)。==は中身(構造的等値)、===は実体の同一性。なおcopy()は浅い写しで、器の中にさらに書き換えられる入れ物(可変リスト等)を抱えると、そこは写されず共有が残る(本話のPointは中身がIntだけなので該当しない)。深い不変には、中身も読み取り専用にする。
- 不変オブジェクト(値オブジェクト)。
- 模擬詠唱の記録(検証のポイント):
- 同じ点を共有しても、片方を
copyで動かして他方が不変であること(Before で勝手に動いていた一点)、同じ座標は別々に作っても==で等しい場所と見なせ、しかしassertNotSameで別実体であること、地図の外(負の歩)は生成の入り口で撥ねること(assertFailsWith)を確認した。標の位置と移動の意味は Before から After まで変えていない。直したのは「書き換えられること」だけだ。
- 同じ点を共有しても、片方を
- 師匠マグナスの格言:
- 「壊れない器とは、書き換えられない器のことだよ。動かしたくば、書き換えるな――写しを一枚、新たに描けばよい。」
