グレッグ・イーガン『祈りの海』

祈りの海 (ハヤカワ文庫SF)

祈りの海 (ハヤカワ文庫SF)

(以下結末などのクリティカルな部分に言及なし)

初めてのイーガンということで、この短編集を手に取ってみた。ちゃんと面白い。

この作品群の肝として、科学的言説やそれに支えられた技術が、人々の自己や世界に関する認識を逆撫でし撹乱する小道具として機能させるためのセッティングがそれぞれ用意されている。そうした仕掛けの上に、そこまでくせのないサスペンス・ドラマが物語のプロットとして与えられる形になっており、科学そのものよりも人間のリアクションに主眼がおかれているため、その意味では読みやすいものが多いだろうと思う。

例えば『ぼくになることを』では、絶え間ない学習によってその振る舞いを脳と区別できなくなった機械を想像する。素朴に意識をコピーする技術を仮定するのでないのがいい。私が気に入ったのは『繭』と『ミトコンドリア・イブ』。この二作品も、生命科学を題材にした(ディテールを無視すれば)全く有り得ないとはいえない設定を用いていて思考実験としての強度を保っているとともに、それがもたらす政治的狂騒がよく描かれていて楽しい作品だった。

一方『放浪者の軌道』などは、非直観的な性質を示す数理的な対象を引き合いに出すことによって成立する一風変わった寓話という趣き。そうやってアイデンティティや自由意思の問題に言及する『放浪者の軌道』は論理的にはナンセンスだけど、なんだか面白い視点を見つけた気分にさせる。『無限の暗殺者』もこの類型だと思うが、残念ながらこちらはあんまりうまくやれていないように思った。

表題作『祈りの海』はこの短編集の中では少し長めで、章に分かれるなどやや違う性質を持っている。作品に用意されている「トリック」も他作品と比べると込み入ったものではなく、我々とは異なる異星の人々の暮らし、そしてそのうちの一人である主人公の体験とそれを通じた変化について丁寧に描写されている。主人公の信仰と、彼が一学徒として明らかにしていく事実がそれを根底から揺るがしかねないものであること、そのせめぎ合いが見所であるはずだが、それほど私には訴えなかった。

ところで、この短編集で女性のせりふはほとんどが古典的な女性の役割語を伴って訳出されていたが、イーガンのような現代的な作品においてそれは適切なのだろうかと感じてしまった。とりわけ『祈りの海』では、我々の世界とは異なった様相のジェンダーを構築せしめるはずの身体的特徴が人々にあるにもかかわらず、作品とは違う世界のジェンダーの文脈が否応無くついてまわる役割語が無頓着に使用されているのは、読んでいて作品世界から押し戻されるようだった。もっとも上記の点についても原著者によってどれだけ意識的になされたものなのかはわからないし、いずれにしても作品内であまり掘り下げられていないようなのではあるが。

TypeScriptの型システム

実際に使うまで、TypeScriptの型システムについてJavaActionScriptのようなものをイメージしていたけど、いくつか他にはあまり見られない特徴のあるものだということが分かってきたので、まとめる。

Specificationはあまり読んでいなくて、コンパイラ(TypeScript 1.1.0-1)の挙動を見て書いているので、誤解はあるかもしれない。また、ここに書いたことはHandbookでだいたい言及されている。

Parameter type bivariance

TypeScriptにはジェネリクスがある。ジェネリクスを使用したときのsubtyping relationをまず見てみる。

class Base {
    foo(): void {}
}
class Derived extends Base {
    bar(): void {}
}
 
class Box<T> {
    constructor(public value: T) {}
}
 
var a: Box<Base> = new Box<Derived>(null); // ok
var b: Box<Derived> = new Box<Base>(null); // error: Cannot convert 'Box<Base>' to 'Box<Derived>'

Subtyping relation A \le Bとは型ABとしても使える、すなわち型Aの値を型Bの変数に代入することができるということだと思ってもいい。

A \le B \quad \text{iff} \quad \mathrm{Box}(A) \le \mathrm{Box}(B)

つまり、covariantになっているみたいだ。

関数型について見てみると

var c: () => Base = () => new Derived; // ok
var d: () => Derived = () => new Base; // error: Cannot convert '() => Base' to '() => Derived'
var e: (x: Base) => void = (x: Derived) => { x.bar() }; // no error, but 'e(new Base)' causes a runtime error!
var f: (x: Derived) => void = (x: Base) => {}; // ok

返り値の型に関してはcovariantになっていてこれは普通だけど、引数の型ではどちらでも通ってしまう(bivariant)。コメントに書いた通り、これは生じうるruntime errorを見逃してしまう。

このような仕様になっている理由は、JavaScriptで頻出する次のようなパターンを許したいからのようだ。

interface EventTarget {
    addEventListener(type: string, listener: (e: Event) => void): void;
    ...
}
target.addEventListener('keypress', (e: KeyboardEvent) => {
    // use e.keyCode
});

No nominal typing

Nominal typingがないとはどういうことかというと、上の例で代わりに

class Base {
    foo(): void {}
}

class Derived extends Base {}

としたときに、型BaseDerivedは同値になる。実際、こうすると上で見た型エラーも起こらない。つまり、classにしてもinterfaceにしてもstructuralな定義に別名をつけるにすぎないということになる。これを利用して

interface Id extends Number {}
var id: Id = 1;

みたいなことも書ける。

Overloading is not resolved statically

TypeScriptのOverloadingは、シグネチャごとに実装を用意しておいて、呼出側では型によって静的にdispatch先が決まるような、いわゆるOverloadingとは違う。実行時に処理を振り分けるコードを自分で書く。

function ppr(o: string): string;
function ppr(o: { ppr: () => string }): string;
function ppr(o: any): string {
	if (typeof o === 'string') {
		return '"' + o + '"';
	} else if (typeof o === 'object') {
		return o.ppr();
	}
}

ppr('a');
ppr({ ppr: () => '{}' });
ppr({}); // error: Could not select overload for 'call' expression

引数の型がanyにつぶれてしまっているように見えるけど、実際に呼出側が利用できるのは実装のない上2つのシグネチャなので大丈夫。

このようになっているのも、元々あるJavaScriptのコードに型を付与するのにはこうでないといけないというのと、またきっとTypeScriptと変換後のJavaScriptでメソッドの対応が1対1になるようにしたいからなのだろう。


TypeScriptは方針として、既存のJavaScriptライブラリとの相互運用性を重視している、さらにいえばJavaScriptでよくあるプログラミング作法にしたがって書かれたコードに対して、できるだけ型を付けて書けるようにするというところを目指しているのだと思う。逆にいえば、この世界に異なる作法を持ち込むことは想定されていない。

他、型システムと直接関係ないところでは、関数型を書くのに引数の変数名が必須なのがまだるっこしいとか、スコープの扱いがコンパイル後のJavaScriptを考えないと想像しずらいとか、あるけれどこれくらいで。