Skip to content

縦書きやLTR RTL共存のための型の水準での拡張

Takashi Suwa edited this page Dec 13, 2021 · 5 revisions

縦書きやLTR/RTL共存といった複数の進行方向の文字体系を統一的に扱う仕組みを用意する上で言語機能としてはどのような拡張が必要かを考えるために,先行事例である Robust Vertical Text Layout [1] や Omega [2] を見てみると,テキスト配置に於いては方向を表すのに以下の3つのパラメータが使われる定式化になっています(各パラメータは T (top),B (bottom),L (left),R (right) の4値のうちいずれかをとります):

  • block progression(または the beginning of the page
    • 紙面上の絶対的な方向でどちらが行の並びとして手前側か.
    • 例: ラテン文字やアラビア文字は上から下に行が連なるので T (top),縦書きの和文は右から左へ行が並ぶので R (right),モンゴル文字は左から右へ縦書きの行が並ぶので L (left).
  • inline progression(または the beginning of the line
    • 紙面上の絶対的な方向でどちらが行中の文字の並びとして手前側か.
    • 例: ラテン文字は左から右へと文字が並ぶので L (left),アラビア文字は右から左へと文字が並ぶので R (right),縦書きの和文やモンゴル文字は上から下に文字が並ぶので T (top).
    • これはblock progressionと垂直をなす2方向のうちどちらかなので,実際には +/- のような定式化をしてもよい.
  • glyph orientation(または the top of the line
    • 紙面上の絶対的な方向でどちらが行中の文字の上向きか.
    • 通常は T (top) だが,例えば縦書きの和文の中にラテン文字を(縦中横にせずに)入れるときは文字が右を上にして並ぶので,そこでのラテン文字のglyph orientationは R (right).

SATySFiでも基本的にはこれを踏襲した形式化にしたいと考えています.で,組版処理そのものはこの形式に従って根気よく設計・実装すればいいのですが,型の水準ではこれらをどう区別すべきでしょうか? すなわち,組版処理に適切に型をつけて妥当でない操作を弾きたいのですが,上記のパラメータを導入して生じる妥当でない操作にはどんなものがあるでしょうか?

まず,block progressionは型の水準で扱われる必要があります.これは端的に言えば “相異なるblock progressionに準拠してつくられた2つのブロックボックス列は結合できない” といった不整合を扱うために必要です.もし T (top) に準拠して組まれたブロックボックス列と R (right) に準拠して組まれたブロックボックス列が結合可能だったりしたら,行がどんな方向に進むのか,あるいはどう回転して結合させるのかといった制御が複雑で,結果が豫測しにくいものになってしまうのは想像に難くありません.そうではなく,“ブロックボックス列をつくる式は静的にblock progressionが割り当てられていて,T は T 同士,R は R 同士としか +++ で結合できず,もし例えば(横書きの)T のブロックボックス列の中に(そこだけ縦書きになっている)R のブロックボックス列を挟みたい場合は,明示的に R のブロックボックス列から T のブロックボックス列へと埋め込む函数を適用して型を合わせる” といった定式化にした方が筋が良さそうです.

続いて,inline progressionは型の水準で静的に扱う必要はありません.block progressionが相等しければ,inline progressionは2種類しかないわけですが,これら2種はごく普通に原稿のテキスト中で共存します.例えばラテン文字とアラビア文字は通常の状況ではblock progressionが T で等しく,inline progression が L と R で逆向きですが,ラテン文字とアラビア文字の混植は普通に行なわれ,したがってinline progressionの相違は型で弾きたい不整合をきたさず,block progressionが相等しいインラインボックス列は同一の型で扱ってよいというわけです.

最後に残るのはglyph orientationですが,これも型の水準で扱う必要はありません.block progressionが共通のインラインボックス列同士は “各文字がどんな方向を向いているか” によらず互いに結合できたりと共通の操作を施してよく,glyph orientationの違いによって生じる弾かれるべき不整合はないからです.

というわけで,結論としては「block progressionだけ型の水準で区別すると良さそう」ということになります.そうすると,従来の block-boxesinline-boxes に対応するものとしてはblock progressionの T (top),B (bottom),L (left),R (right) ごとに型をつくって block-boxes-{top,bottom,left,right}inline-boxes-{top,bottom,left,right} という具合の4種類ずつの型を用意することになりますが,これはまた大変ですね.++read-inline といった函数もblock progressionごとに4つに区別されねばならず,随分と厄介です.

しかし,インターフェイスとしてはblock progressionによらずかなり共通しているはずなので,適度に共通化されたAPIにしたいですね.そこで上記のようなblock progressionを区別する方法をもうちょっとマシにしようというのが,以下に述べる2階の型システムを使った多相性です.

まず,SATySFi v0.1.x では 列多相 (row polymorphism) [3] が導入され,カインドが以下のような構文になる予定です:

kind ::=
  | base-kind '->' kind
  | base-kind

base-kind ::=
  | 'o'
  | '(' [ label [',' label]*]? ')'

o が型につくカインド,(l_1, …, l_n) が列につくカインドです(詳細は省略しますが,ラベル名の有限集合 {l_1, …, l_n} を伴っているカインドです).ここにもう1種類,block progressionにつくカインド を追加します:

base-kind ::=
  ...
  | 'direction'

型の水準で扱うblock progression自体は,/T/B/L/R という形式で記述します.これらの対象に direction というカインドがつくわけです:

  • /T, /B, /L, /R :: direction

そして,ブロックボックス列の型は以下のようにこのblock progressionをパラメータにとるようにします:

  • block-boxes :: direction -> o

すなわち,block-boxes /T とか block-boxes /R でそのblock progressionをもつブロックボックス列につく型なわけです(注意: SATySFi v0.1.xの具象構文では,型コンストラクタは int list のような引数前置から list int のような引数後置に切り替える豫定です).従来の block-boxes は新しい定式化では block-boxes /T に相当します.

そしてこれらを用いると,以下のように +++ をblock progressionによらず使えつつ左右の引数が同一のblock progressionでないといけないという多相性を表すことができます(型変数とその全称量化は簡単のため数式的に書いています):

  • (+++) : ∀δ :: direction. block-boxes δ → block-boxes δ → block-boxes δ

++ も全く同様です:

  • (++) : ∀δ :: direction. inline-boxes δ → inline-boxes δ → inline-boxes δ

さらに,read-inline は以下のように一般化されます:

  • read-inline : ∀δ :: direction. context → inline-text δ → inline-boxes δ
  • inline-text :: direction -> o

すなわち,インラインテキストもblock progressionの情報をもつようになります.“単なる原稿” であるように見えるインラインテキストが方向をもつのはいささか奇妙な感じがするかもしれませんが,例えば縦書きを想定したテキストなら「ここは縦中横にせよ」という指定 \tate-chu-yoko{…} といったものが入っている場合があるのも自然でしょうし,その意味でインラインテキストとは一般には “どのblock progressionなのかを想定して書かれている原稿” であるというわけです.もしどんなblock progressionでも組めるインラインテキストがあるなら,それは ∀δ :: direction. inline-text δ という型をつけて扱えばよいです(正確に言えば多相型がつくのはletで束縛された場合だけですが).

もしかしたらテキスト処理文脈も context :: direction -> o と一般化すべきかもしれませんが,このあたりは今後検討するつもりです.

また,先に述べた “異なるblock progressionのブロックボックス列を変換して結合する” 処理の定式化としては,次のような2引数のプリミティヴ convert-block-progression を考えています(実際には位置調整のパラメータなどを伴う気がしますが,簡単のため根幹となる2引数にしています):

  • convert-block-progression : ∀δ, ∀δ' :: direction. block-spec δ -> block-boxes δ' -> block-boxes δ
  • block-spec :: direction -> o

第1引数は “どのblock progressionへと変換するか” です.この引数には以下のようなプリミティヴを使います:

  • top : block-spec /T
  • right : block-spec /R

要するに block-spec δ は “block progression δ に対するsingleton type” のようなもので,top とか right は “そのwitnessとなる値” なわけです.例えば,“横書きの段落の連なりの中に縦書きの段落をそのままの向きで入れる” コマンドは以下のように書けるとよいだろう,という算段です:

let block ctx +embed-vertical (vbt : block-text /R) : block-boxes /T =
  let vbb : block-boxes /R = read-block ctx vbt in
  convert-block-progression top vbb

コマンドもやはり “どのblock progressionのブロックテキストまたはインラインテキストの中で使ってよいか” の情報をもち,例えば上記の +embed-vertical には block /T [block-text /R] という型がつきます.裏を返せば,このコマンドが使われたブロックテキストは block-text /T 型がつき,横書きでしか組めないテキストであるということが型の水準で判定されるわけです.

というわけで,ちょっとややこしくなりすぎる感じもしますが,「こんな具合に型の水準でblock progressionを区別する定式化にするとよさそうだな」ということを考えているという話でした.

参考文献

  1. Elika J. Etemad. Unicode Technical Note #22: Robust Vertical Text Layout, 2005.
  2. John Plaice and Yannis Haralambous. Draft documentation for the Ω system, 1999.
  3. Benedict R. Gaster and Mark P. Jones. A polymorphic type system for extensible records and variants, 1996.
Clone this wiki locally