10Q6 〜あるいは TypeScript で Either を do する〜
駅前の喫煙所で、僕は足を止めた。
雨上がりのアスファルトが、街灯の光をぼんやりと反射している。
誰かが吸い終えたばかりの煙草の煙が、夜の冷たい空気に溶けていくのが見えた。
僕は自分のポケットに手を入れた。
それがどういう意味を持つのか、まだ考えないままに。
「そういえば、僕は3年前に禁煙したんだった。」
1096日。
僕は煙草を吸わずに生きてきた。
それは誇るべきことのように思えたし、そうでもないようにも思えた。
彼女のことを思い出した。
彼女は、僕が最後に煙草に火をつけた夜にいた。
そして、それが僕が彼女を最後に見た夜だった。
僕にとってのレゾンデートルとは何だったのだろうか。
彼女の存在があったから僕は禁煙を選んだのか、
僕が禁煙を選んだから彼女の存在が確かだったのか。
TypeScriptの世界にも、こんなふうに 世界に委ねられた選択 を扱う仕組みがある。
それが、F-Box の Either だ。
Eitherという選択
現実の世界では、僕たちはしばしば次のような状況に出会う。
function getCigarette(): string {
if (Math.random() > 0.5) {
return "ラッキーストライク";
}
throw new Error("タバコはなかった。");
}
煙草があるかどうか、それはただの確率の問題ではない。
取り出そうとしたときに 「世界がそれを許すかどうか」 という問題だ。
もし煙草がポケットに残っていれば、
僕は Zippo に火をつけることができるだろう。
しかし、もしなければ――
それは単に「ない」というだけではなく、
僕はもう煙草を持つべきではない、という世界の意志なのかもしれない。
それなら Either
を使うのが適切だ。
煙草があれば、Right
。
もしなければ、それは Left
であり、
単なる「不在」ではなく 「世界に拒絶されるという事象」 なのだと考えられる。
例えば、ポケットに煙草があるかどうかを Either
で表現すると、こうなる。
import { Either } from "f-box-core";
const right = Either.right<string, string>;
const left = Either.left<string, string>;
const checkForCigarette = (): Either<string, string> =>
Math.random() > 0.5
? right("ラッキーストライク")
: left("タバコはなかった。");
僕はZippoを持っていたのだろうか?
いや、本来なら、持っているはずがないのだ。
でも、もしポケットの奥で、Zippoの冷たい金属の感触を確かめてしまったら?
僕はもう3年前の僕になってしまうのだろうか?
const checkForZippo = (): Either<string, string> =>
Math.random() > 0.5
? right("Zippo")
: left("Zippoはなかった。");
彼女は最後に、僕の指からZippoを抜き取って、テーブルの上に置いた。
それがZippoを最後に見た夜だった。
指に伝わる金属の冷たさ、オイルの匂い、キャップを開けるときの軽い抵抗――。
それは、本当にZippoなのか?
それとも、ただの記憶の残骸なのか?
彼女の指先の感触と、Zippoの感触が、どこかで重なるような気がした。
Eitherをdoする
彼女が僕の煙草の火を消したことは覚えているのに。
彼女の声が、確かに「禁煙するんだよね?」と言ったことは覚えているのに。
名前だけが、どうしても思い出せない。
僕の記憶は、本当に確かなものか?
const checkForMemory = (): Either<string, string> =>
Math.random() > 0.5
? right("1096日の禁煙の記憶")
: left("どうやら本当に禁煙していたようだった。");
駅前の喫煙所で、僕は静かに時の流れを待っていた。
夜風が吹き抜ける音に耳を澄ませながら、do
という構文を使って、
Either
の連鎖を処理できることを思い出す。
const checkPocket = () =>
Either.do<string, string, string>(function* () {
const memory = yield checkForMemory();
const cigarette = yield checkForCigarette();
const zippo = yield checkForZippo();
return `僕はポケットから ${cigarette} を取り出し、${zippo} で火をつけた。煙は夜空に静かに溶け、${memory} が胸を締めつけた。`;
});
10Q6の夜
僕は目を開けた。
駅前の喫煙所には、ぼんやりとした煙が漂っていた。
それは誰かのものかもしれないし、僕自身のものだったかもしれない。
const scene = checkPocket();
console.log(
scene.match(
(failure) =>
`僕はポケットを探ったが、${failure} 今夜はただ、静かに駅前を歩き続けることにした。`,
(success) => success
)
);
僕は静かにポケットを探った。
もし何かがそこにあれば、僕は再び彼女の最後の夜に戻る。
もし何もなければ、僕はこのまま夜を歩き続ける。
Zippoの冷たい金属の感触が、
彼女の指先の感触と、どこかで同じものだったような気がした。
そして、夜風が僕の頬を撫でる。
1096日――僕は本当に禁煙していたのだろうか?
あるいは、最初からそんなものは存在しなかったのかもしれない。
僕の禁煙の記憶が、彼女の存在そのものだったのだとしたら?
世界はいつも、Either でできている。
吸うか、吸わないか。記憶があるか、ないか。
彼女がいたか、いなかったか。
やれやれ、
僕は、まだその答えを知らないまま、
10Q6の夜を歩き続ける。
Discussion