とある微分幾何の教科書のなかで使われている関手の表現が曖昧でタメ息が出ました。が、曖昧表現の使用はこの教科書に限らないわけで、いたる所で使われています。一般的な(特定の予備知識を仮定しない)事例で、関数/関手の曖昧表現とその解釈方法を説明します*1。
この説明から、証明支援系などがエラボレーション(「多様体とバンドルのボキャブラリ (1) 基本記法 // 構造と台構造、エラボレーション」参照)機能を持たなくてはならない理由も分かると思います。
内容:
タプル/シーケンスと引数渡し
関数/関手への引数〈argument | 入力〉が複数あるときに、ここでは、タプル引数とシーケンス引数を区別します。タプルは直積の要素を表すとして、 のように、カンマ区切り・丸括弧囲みの構文にします。シーケンスは のように、空白区切り・囲み記号なしにします。囲み記号なしにすると困るのが、空シーケンスの表現です。この記事では、省略可能な(むしろほとんどの場合省略する)シーケンス囲み記号として '' と '' があるとして、空シーケンスは で表します。必要があれば、シーケンスを と囲み付きで書いてもいいです。
関数/関手は、プログラミング言語Haskellのように、カリー化されているとして、引数としてシーケンスを渡します。 のようです。カリー化された関数/関手には部分適用(引数シーケンスの一部を渡すこと)が可能なので、次の表現も意味を持ちます。
- または
- または
- または
それぞれの型が で、 の型が になるとき、 のプロファイル(引数型=域 と 戻り値型=余域 の仕様)を次の形で書きます。
アンド記号を使う理由は後で説明します。
多引数〈多変数 | 多項〉の関数/関手は、原則的にはカリー化した形で扱います。つまり、 が適用〈引数渡し〉された形は ではなく または と書きます。しかし、一緒に渡さないと意味がない引数達はタプルにまとめます。例えば、 。タプル引数を含む関数/関手のプロファイルは、例えば次のようになります。
恒等写像の例
プログラミングで多相関数(ここで定義はしませんが)と呼ばれるもので、おそらく一番簡単な事例は恒等写像 でしょう。'' とだけ書いても、どの集合の恒等写像か分からないので のように下付き添字で集合を明示します。今出てきた は集合(と写像の)圏です。添字の情報から次が分かります。
に を渡した形は です。ここで、下付き添字を第一引数と考えて と書きます。これは、関数 にシーケンス を渡した形です。この形の のプロファイルは次のようになります。
プロファイルがこんな形をしているので、 は通常の関数とは違うものです。なので多相関数と呼ばれています。プロファイルがイカツイ形をしてますが、これでも引数〈入力〉と戻り値〈出力 | 結果〉の状況を十分には表現できていません。次のような補足説明が必要です。
- は、すべての集合からなる“集合”(集合論では集合とは認めてないが)。
- は、すべての集合のすべての要素を寄せ集めた“集合”(集合論では集合とは認めてないが)。ただし、異なる集合(例えば と )に属する要素は別物として区別できるとする。
- の第一引数は自由に選べる。
- の第ニ引数は、第一引数で指定した集合の要素でなくてはならない。第二引数は自由ではなくて、第一引数に依存した制約がある。
- の戻り値は、第一引数で指定した集合の要素になる。これも、第一引数に依存した制約。
プロファイルを次の形にする(依存型の意味でカリー化する)と、制約が少し分かりやすくなります。
次なら、もっと分かりやすいでしょう。
依存型理論に基づくプログラミング言語や証明支援系では、引数間の依存関係や引数と戻り値の依存関係を表現する手段がありますが、非形式的コミュニケーションでは、最初に出したような曖昧なプロファイルが想定されるだけで、依存関係は暗黙になります。この記事では、暗黙の依存関係による曖昧な表現が主題なので、 のプロファイルは最初の形(下に再掲)で考えます。
はシーケンスの集合ですが、依存シーケンス〈dependent sequence〉を許すとします。 のとき、 は自由に選べますが、 は第一成分に選んだ に依存する(制約される)かも知れません。依存性・制約がまったくない場合は (通常の直積)と書きます。
のような場合は、後(右)に出現する成分が前(左)に出現する成分に依存する可能性を示しています。 を使った書き方だけでは、依存性の詳細は分かりません。別に依存性の明示的記述や依存性の推論ルールが必要です。が、実際には暗黙に済まされる場合が多いのです。
暗黙の依存性があるだけならまだしも、引数の省略が絡むと事情はさらに複雑化します。
引数の省略: 暗黙引数と引数デフォルト値
ハイフン、マイナス、アンダースコア()は、引数の出現位置・場所を指定するプレースホルダに使われます。この記事では、目立つという理由でマイナス記号をプレースホルダに使います*3。例えば、添字と丸括弧を使った の引数構文は と書けます。カリー化した引数構文は となります。
通常の引数はマイナス記号で表すとして、省略可能引数は疑問符 '' またはピリオド '' で示します。疑問符とピリオドでは意味が違います。まずは疑問符から。 と書いた場合は、第一引数が省略可能なことを意味します。第一引数が省略された場合、指定された他の引数から自動的に推論〈infer〉するとします。プログラミング言語/証明支援系では、推論で埋める省略可能引数は暗黙引数〈implicit argument〉と呼びます。
例えば、引数省略仕様が の状況で と書いたら、第一引数は自動的に推論されて のように補完〈解決〉されます。ただし、 から が自動推論できるためには前提が必要です。リテラル〈定数記号〉 '' が整数の集合 の要素を表すと前もって確定しているとします。 や の可能性はない、ということです。プログラミング言語/証明支援系では、こういう前提を人為的に作り出していますが、一般的には(非形式的コミュニケーションでは)通用しないですね。
リテラルから型を推論するのは無理だとしても、文脈内で文字の型を約束している場合はあります。例えば、 とかです。この約束のもとで を と補完〈解決〉することはできます。
疑問符で表す暗黙引数は上述のような方法で補完〈解決〉しますが、ピリオドはデフォルト値〈default value〉が決まっている省略可能引数を表します。例えば、引数省略仕様が ならば、第一引数の省略時値〈デフォルト値〉はひとつに決まっています。例えば、デフォルト値が だとすると、 ということになります。 は問題なく解釈できますが はエラーになります。明示的指定はデフォルト値より優先されるので はOKです。
省略された引数を可能なら推論で補い、無理ならデフォルト値を使うことを で表すとしましょう。引数省略仕様が の状況で、第一引数デフォルト値が なら次のような補完〈解決〉がされます。
型の持ち上げ〈リフト〉
足し算を のように中置演算子記号プラスで書きます。これを関数記号〈関数名〉 を使って と書くことにします。様々な足し算があるので、単に では曖昧です。いま、足し算は可換モノイド〈commutative monoid〉の二項演算の意味で使うと約束したとします。そうすると、 の第一暗黙引数には可換モノイドが入ることになります。
可換モノイドの圏を として、関数 のプロファイルは次のようです。
これも補足説明が必要ですね。
- は、すべての可換モノイドからなる“集合”(集合論では集合とは認めてないが)。
- は、すべての可換モノイドの台集合〈underlying set〉のすべての要素を寄せ集めた“集合”(集合論では集合とは認めてないが)。ただし、異なる可換モノイドの台集合に属する要素は別物として区別できるとする。
- の第一引数は自由に選べる。
- の第ニ引数・第三引数は、第一引数で指定した可換モノイドの台集合の要素でなくてはならない。第二引数・第三引数は自由ではなくて、第一引数に依存した制約がある。
- の戻り値は、第一引数で指定した可換モノイドの台集合の要素になる。これも、第一引数に依存した制約。
さらに、 の引数省略仕様は だとします。第一引数は暗黙引数で、省略されれば第二引数から推論されます。
以上の話は、引数の個数が増えた以外は前節と同じことです。プログラミング言語/証明支援系、そして非形式的コミュニケーションでは、別なエラボレーション(「多様体とバンドルのボキャブラリ (1) 基本記法 // 構造と台構造、エラボレーション」参照)メカニズムがあります。「型クラス」「標準構造/標準インスタンス」などと呼ばれるメカニズムです。これは、 の第一引数に「可換モノイドではなくて(足し算を持たない)集合を入れれば良い」とする方式です。
集合として と (ブール値の集合 )を考えましょう。集合 に載っている可換モノイド構造はひとつには決まりません。例えば、 上では、足し算、掛け算、max(大きいほう)は可換モノイド演算です。しかし、我々の常識から言えば、 上の“足し算”は一意に決まります。その一意対応を次の関数としてハッキリと定義します。
ここ出てきた記号 '' は、みんなが知っているアノ“自然数の足し算”です。'' もみんなが知っているアノ“論理オア”です。
今定義した を前提にして、例えば を次のように補完〈解決〉します。
- 見た目上の第一引数が型ではないので、実際は第二引数だと判断する。
- 第二引数 の型が なので、暗黙の第一引数は だと判断。
- の第一引数に を適用して と再解釈。
- 関数記号 の意味は論理オア と解釈。
- 実際の表現 が得られる。
最終的に得られた関数は、
です。
上記の手順では、暗黙引数の推論だけではなくて、 も使っています。 は集合圏の対象〈集合型インスタンス〉を可換モノイド圏の対象〈可換モノイド型インスタンス〉へと“持ち上げ”ています。持ち上げ〈lift〉は忘却関手 の部分的逆であり、次が成立しています。
持ち上げも多くの場合暗黙に使われます。持ち上げの逆方向である忘却はもっと暗黙に多用されます。忘却については「多様体とバンドルのボキャブラリ (1) 基本記法 // 構造と台構造、エラボレーション」を参照してください。
関手の例
を位相空間の圏、 を(可換な)環の圏として、反変関手 を次のように定義します。
はホントは だし、 が環の準同型写像になることを示す必要があるとか、不備はありますがだいたいはこれでいいでしょう。
次に第一引数を固定して、反変関手 を次のように定義します。以下で、 は開集合の集合を包含順序により圏とみなしたものです。 は関数の制限です。
これは、位相空間 上の連続関数環の前層(層にもなる)の定義なので、人為的な例ではありません。
と の両方をまとめて、シーケンス引数の関手 を考えましょう。とりあえず対象パートだけを考えれば、そのプロファイルは次のようです。
引数省略仕様は とします。この引数省略仕様は分かりにくいので、節を改めて説明します。
関手における引数省略仕様
残念ながら という引数省略仕様だけでは情報が不足しています。実際には次のような状況だとします。
- 暗黙引数である第一引数を推論するには、第二引数が指定されている必要がある。
- 第二引数のデフォルト値は、第一引数に依存する。
つまり、第一引数も第二引数も省略すると、エラボレーションのメカニズムは働きません。具体的な推論とデフォルト値は次のようです。
これで、引数がひとつだけ指定されたときの解釈はできます。しかし、問題があります。“とある開集合 ”が引数に指定された をどう解決すべきでしょう。もし、 なら、暗黙引数の推論のルールから
と補完されます。しかし、開集合はそれ自体を位相空間だとみなすことができます。そうなるとデフォルト値のルールから
となります。
この場合は、環としてみた場合 なので、「どっちでもいい」ということになります。が、たまたま結果オーライなだけでスッキリしません。
この問題は、「親の位相空間 を持つ開集合 」と「それ自体で位相空間とみた開集合 」の区別ができない限り解決できそうにありません。なにかしらの目印が必要なので、「親の位相空間 を持つ開集合 」を例えば と書くことにします。すると:
のプロファイルの域〈入力〉の部分が なので、 か かの(排他的)区別ができないと、本来の引数位置(第一引数か第二引数か)の判断もできません。
非曖昧化と解決
関数/関手に引数の一部が指定された曖昧表現があったとき、次の操作を非曖昧化〈disambiguation〉と呼びましょう。
- 指定された引数が本来何番目の位置のものかを判断する。
- 指定されてない引数が暗黙引数かデフォルト値を持つ引数か、それとも通常の引数(明示的指定を要求する)かを判断する。
非曖昧化により次のような書き換えができます。
この後で、疑問符やピリオドの部分を推論値/デフォルト値で埋める作業は解決〈resolution〉と呼びましょう。解決には、先に述べた持ち上げ〈リフト〉の適用が必要かも知れません。
曖昧表現があっても、非曖昧化と解決の手順がハッキリしていれば、それは解消可能な曖昧さです。手順がハッキリしてないときはホントの曖昧さで、どうにもなりません。過度に暗黙の前提に頼ると、どうにもならない曖昧さが生じます -- 非形式的コミュニケーションでは気をつけるべき点です。