10

以前の回答で、Petr Pudlak は HaskからHaskCFunctorのファンクター以外のファンクターのクラスを定義しました。型ファミリーを使用して少し書き直すと、次のようになります

class CFunctor f where
  type Dom f :: * -> * -> *               -- domain category
  type Cod f :: * -> * -> *               -- codomain category
  cmap :: Dom f a b -> Cod f (f a) (f b)  -- map morphisms across categories

たとえば、次のようなインスタンスで

instance CFunctor Maybe where
  type Dom Maybe = (->)                   -- domain is Hask
  type Cod Maybe = (->)                   -- codomain is also Hask 
  cmap f = \m -> case m of
                   Nothing -> Nothing
                   Just x  -> Just (f x)

圏論では、F : C --> Dがファンクターであり、G : D --> Eがファンクターであるときはいつでも、合成GF : C --> Eもファンクターです。

これを Haskell で表現したいと思います。書くことができないのでinstance CFunctor (f . g)、ラッパー クラスを紹介します。

newtype Wrap g f a = Wrap { unWrap :: g (f a) }

インスタンスを書くことで、CFunctor私は限ります

instance (CFunctor f, CFunctor g, Cod f ~ Dom g) => CFunctor (Wrap g f) where
  type Dom (Wrap g f) = Dom f
  type Cod (Wrap g f) = Cod g
  cmap = undefined

しかし、の実装がcmapどうあるべきかわかりません。何かアドバイス?

PS、これらすべての最終的な理由はAdjunction、メソッドunitおよびを含むクラスを導入し、counit付加物からモナド インスタンスを自動的に派生させることです。しかし最初に、2 つのファンクターの合成もファンクターであることをコンパイラーに示す必要があります。

cmap.cmapタイプのオブジェクトで使用でき、g (f a)それが機能することはわかっていますが、それは少し不正行為のように思えます-確かにファンクターは単なるファンクターであり、コンパイラーはそれが実際に2つの構成であることを知る必要はありません他のファンクター?

4

1 に答える 1

6

F : C → Dファンクターとが与えられた場合G : D → E、ファンクター合成は、カテゴリとG ∘ F : C → Eの間のオブジェクトのマッピング (そのようなもの)と射間のマッピング (そのようなもの)です。CE(G ∘ F)(X) = G(F(X))(G ∘ F)(f) = G(F(f))

CFunctorこれは、インスタンスを次のように定義する必要があることを示唆しています。

instance (CFunctor f, CFunctor g, Cod f ~ Dom g) => CFunctor (Wrap g f) where
  type Dom (Wrap g f) = Dom f
  type Cod (Wrap g f) = Cod g
  cmap f = cmap (cmap f)

ただし、cmap2 回構成すると、 Dom f a b -> Cod g (g (f a)) (g (f b))and cmapin this instance has a type が得られますDom f a b -> Cod g (Wrap g f a) (Wrap g f b)

からg (f a)Wrap g f、またその逆も可能ですが、インスタンス宣言は の構造について何の仮定もしていないためCod g、うまくいきません。

functor はカテゴリ間のマッピングであることがわかっているのでCod gCategory(Haskell 側では、これにはCategory (Cod g)制約が必要です) であるという事実を使用できます。これにより、操作する操作がほとんどなくなります。

cmap f = lift? unWrap >>> cmap (cmap f) >>> lift? Wrap

lift?ただし、これには、関数をHaskカテゴリからカテゴリに持ち上げる便利な持ち上げ演算子が必要Cod gです。のように書くCod g(~>)、 のタイプは次のようにlift?なります。

lift? :: (a -> b) -> (a ~> b)

lift? unWrap  :: Wrap g f a ~> g (f a)
cmap (cmap f) :: g (f a)    ~> g (f b)
lift? Wrap    :: g (f b)    ~> Wrap g f b

lift? unWrap >>> cmap (cmap f) >>> lift? Wrap :: Wrap g f a ~> Wrap g f b

このリフティング演算子には、少なくとも 2 つの選択肢があります。

  • 制約を から に拡張できますCategory (Cod g)Arrow (Cod g)この場合、リフティング演算子はarrになります。
  • または、Sjoerd Visscher がコメントで言及しているように、意味的に実行時に と でWrapあるという事実を使用できます。その場合、 の使用は正当化されます。unWrapidunsafeCoerce
于 2012-12-24T00:10:25.493 に答える