26

したがって、Hask のカテゴリは次のとおりです。

  • タイプはカテゴリのオブジェクトです
  • 関数は、カテゴリ内のオブジェクトからオブジェクトへの射です。

同様に、次のようになりFunctorます。

  • あるカテゴリから別のカテゴリへのオブジェクトのマッピングとしての型コンストラクタ
  • fmapあるカテゴリーから別のカテゴリーへの射のマッピング用。

さて、プログラムを書くとき、基本的に値を変換します (型ではありません)。Hask のカテゴリは値についてはまったく話していないようです。式全体に値を当てはめようとしたところ、次のような結果になりました。

  • 各タイプはカテゴリそのものです。例: Int はすべての整数のカテゴリです。
  • 値から同じ型の別の値への関数は圏の射です。例えば:Int -> Int
  • ある値から異なる型の別の値への関数は、ある型の値を別の型にマッピングするためのファンクターです。

私の質問は、値は Hask の圏 (または一般的な圏論) でも意味があるのでしょうか? はいの場合は、それについて読むための参照、またはそうでない場合は、その理由。

質問が理にかなっていることを願っています:)

4

5 に答える 5

11

ここには他にもいくつかの非常に素晴らしい回答がありますが、それらはすべてあなたの最初の質問をやや見逃しています. 明確にするために、値は単純に存在せず、Hask カテゴリには意味がありません。それはHaskが話していることではありません。

上記は、言うのも感じるのも少しばかげているように思えますが、圏論は、プログラミング言語のように洗練されたもので利用可能なはるかに複雑な相互作用と構造を調べるためのレンズを提供するだけであることに注意することが重要であるため、私はそれを持ち出します. その構造のすべてがカテゴリーというかなり単純な概念に包含されると期待するのは実り多いものではありません。[1]

別の言い方をすれば、私たちは複雑なシステムを分析しようとしており、興味深いパターンを探すために、それをカテゴリとして見ることが役立つ場合があります。Hask を紹介し、それが実際にカテゴリを形成しているかどうかを確認し、それがMaybeFunctor のように動作しているように見えることに気付き、それらすべてのメカニズムを使用してコヒーレンス条件を書き留めることができるのは、この考え方です。

fmap id = id
fmap f . fmap g = fmap (f . g)

これらのルールは、Hask を導入するかどうかに関係なく意味がありますが、Haskell 内で発見できる単純な構造の単純な結果と見なすことで、その重要性を理解できます


技術的な注意として、この回答全体は、Hask が実際には「プラトニックな」Hask であることを前提としundefinedています。それがなければ、ほとんどすべての議論がバラバラになります。


これらの法則は私の最初の声明にほぼ反しているように見えるので、もっと詳しく調べてみましょう---それらは明らかに値レベルで動作していますが、「値はHaskには存在しません」ですよね?

答えの 1 つは、カテゴリ関数とは何かを詳しく調べることです。明示的には、C のオブジェクトを D のオブジェクトにし、C の矢印を D の矢印にする 2 つのカテゴリ (C と D など) 間のマッピングです。一般に、これらの「マッピング」はカテゴリ矢印ではなく、単にカテゴリ間の関係であり、必ずしもカテゴリと構造を共有しているわけではありません。

これは重要です。なぜなら、HaskellFunctorのエンドファンクターである Haskell を考慮しても、注意が必要だからです。Hask では、オブジェクトは Haskellであり、矢印はそれらの型の間のHaskell関数です。

もう一度見てみましょうMaybe。Hask のエンドファンクタになる場合は、Hask のすべてのを Haskの他のにする方法が必要です。このマッピングは Haskell 関数ではないように感じるかもしれませんが、レベルpure :: a -> Maybe aで動作するため、資格がありません。代わりに、私たちのオブジェクト マッピングはそれ自体です。どの型に対しても typeを形成できます。MaybeaMaybe a

これはすでに、値なしで Hask で作業することの価値を強調してFunctorいますpure

Functorエンドファンクターの矢印マッピングを調べることで、残りの部分を開発しますMaybe。ここでは、Hask の矢印を Hask の矢印にマップする方法が必要です。ここでは、これが Haskell 関数ではないと仮定しましょう (そうである必要はありません)。それを強調するために、別の方法で記述します。fが Haskell 関数の場合a -> b、Maybe[ f] は他の Haskell 関数Maybe a -> Maybe bです。

fさて、スキップせずに Maybe[ ] " " の呼び出しを開始するのは難しいfmap fですが、そのジャンプを行う前にもう少し作業を行うことができます。たぶん[ f]には、特定のコヒーレンス条件が必要です。特に、aHask のどの型にも id 矢印があります。私たちのメタ言語では、それを id[ ] と呼ぶかもしれませんがa、たまたまそれが Haskell 名でも呼ばれていることを知っていid :: a -> aます。全体として、これらを使用して、エンドファンクターのコヒーレンス条件を述べることができます。

Hask のすべてのオブジェクトについてa、Maybe[id[ a]] = id[ Maybe a] となります。Haskfgの任意の 2 つの矢印については、 Maybe[ f . g] = Maybe[ f] となります。たぶん[ g]。

最後のステップは、Maybe[_] がたまたま Haskell 関数自体として Hask object の値として実現可能であることに注意することforall a b . (a -> b) -> (Maybe a -> Maybe b)です。それは私たちを与えFunctorます。


上記はかなり技術的で複雑でしたが、Hask とカテゴリカル エンドファンクタの概念を Haskell のインスタンス化からまっすぐに分離しておくことには重要なポイントがあります。fmap特に、実際の Haskell 関数として存在する必要性を導入することなく、この構造のすべてを発見できます。Haskは、価値レベルで何も導入していないカテゴリです。

そこには、Hask をカテゴリとして見る真の鼓動が宿っています。Hask の endofunctor を識別する表記にFunctorは、さらに多くの線のぼかしが必要です。

指数Haskがあるため、この線のぼやけは正当化されます。これは、Hask のカテゴリ矢印の束全体と特定の特別なオブジェクトとの間に統一があると言うには難しい言い方です。

より明確に言うaと、Hask の任意の 2 つのオブジェクト、たとえば と について、Hask( , )bとして示されることが多い、これら 2 つのオブジェクト間の矢印について話すことができることがわかっています。これは単なる数学的集合ですが、Hask( , )に密接に関連する別の型が Hask にあることがわかっています。abab(a -> b)

だからこれは奇妙です。

Haskell の一般的な値は、Hask のカテゴリ表示ではまったく表現されないと最初に宣言しました。次に、Haskell の内部にそれらの部分を実際に値として貼り付けずに、カテゴリの概念のみを使用することで、Hask で多くのことができることを示しました。

しかし今、私は、 のような型の値がa -> b、メタ言語セット Hask( a, b) 内のすべての矢印として実際に存在することに注目しています。これはかなりのトリックであり、指数関数を含むカテゴリを非常に興味深いものにしているのは、まさにこのメタ言語的なぼかしです。

ただし、もう少し改善できます。Hask には端末オブジェクトもあります。これをメタ言語的には 0 と呼ぶことができますが、Haskell 型としても知られてい()ます。Hask オブジェクトを見ると、Hask( , )aにカテゴリ矢印のセット全体があることがわかります。さらに、これらは type の値に対応することがわかっています。最後に、与えられた関数を適用することですぐに を取得できることがわかっているので、Hask( , ) のカテゴリカル矢印は正確に typeの Haskellであると言いたいかもしれません。()a() -> af :: () -> aa()()aa

これは、まったく混乱するか、信じられないほど驚異的なものになるはずです。


私の最初の声明に固執することで、これをいくぶん哲学的に終わらせようと思います。それは本当に純粋なカテゴリとしてではありません---カテゴリが興味深いのは、それらが非常に単純であり、タイプ、値、およびtypeOf包含などのこれらのカテゴリ外の概念をすべて必要としないためです。

しかし、私はまた、厳密にはただのカテゴリ()であっても、Haskには、Haskell のすべての値に非常に非常によく似たものがあるaことも示しましたa

哲学的には、これらの矢印は実際には私たちが探している Haskell の値ではないと主張するかもしれません。それらは別のものだと主張するかもしれませんが、たまたま Haskell の値と 1 対 1 で対応しているだけです。

これは、心に留めておくべき非常に重要な考え方だと思います。これら 2 つのことは異なります。動作が似ているだけです。


非常によく似ています。任意のカテゴリで矢印を作成できます。したがって、Hask( , ) の矢印と Hask( a, )の矢印を選択すると仮定しましょう。これらの矢印をカテゴリ構成と組み合わせると、Hask( , ) で矢印が得られます。これを少しひっくり返すと、私が今行ったことは、 type の値、 type の値を見つけ、それらを組み合わせて type の値を生成したと言えます。b()a()ba -> bab

言い換えれば、物事を横から見ると、カテゴリー矢印合成は関数適用の一般化された形として見ることができます。

これが、Hask のようなカテゴリを非常に興味深いものにしている理由です。一般に、これらの種類のカテゴリはデカルト クローズド カテゴリまたは CCC と呼ばれます。初期オブジェクトと指数 (積も必要) の両方があるため、型付きラムダ計算を完全にモデル化する構造があります。

しかし、それらにはまだ値がありません。

[1] 私の回答の残りの部分を読む前にこれを読んでいる場合は、読み続けてください。それが起こると期待するのはばかげていますが、実際にはそうです。私の回答全体を読んだ後にこれを読んでいる場合は、CCC がいかにクールであるかを考えてみましょう。

于 2014-01-30T16:29:54.077 に答える
6

物事をカテゴリーに分類する方法はいくつかあります。特にプログラミング言語は、非常にリッチな構造になっています。

Hask カテゴリを選択すると、抽象化のレベルを設定するだけです。価値観を語るのが苦手なレベル。

ただし、Hask では、定数は終端オブジェクト () から対応する型への矢印としてモデル化できます。たとえば、次のようになります。

  • True : () -> ブール
  • 'a' : () -> 文字

確認できます: Barr, Wells - コンピューティングの圏論、セクション 2.2。

于 2013-11-16T06:27:24.283 に答える