5

Writer mEither eモナドの間には二重の関係があることに気づきました。mがモノイドの場合、

unit :: () -> m
join :: (m,m) -> m

モナドを形成するために使用できます:

return is composition: a -> ((),a) -> (m,a)
join is composition: (m,(m,a)) -> ((m,m),a) -> (m,a)

()のデュアルはボイド(空のタイプ)であり、製品のデュアルは余積です。すべてのタイプeに「コモノイド」構造を与えることができます。

unit :: Void -> e
join :: Either e e -> e

明白な方法で。今、

return is composition: a -> Either Void a -> Either e a
join is composition: Either e (Either e a) -> Either (Either e e) a -> Either e a

これがEither eモナドです。矢印はまったく同じパターンに従います。

質問:与えられたモノイドに応じて、Either eおよびの両方を実行できる単一のジェネリックコードを作成することは可能ですか?Writer m

4

3 に答える 3

5

私はこれらのモナドがカテゴリー的に二重であるとは言いませんが、むしろそれらは両方とも次の構造によって生成されます:モノイド圏を与えられた(C、⊗、1)とCの代数Aは、モナドがXをA⊗Xに送信すると考えます。最初のケースでは、CはHask、⊗は×、代数はモノイド、2番目のケースではCです。はHask、⊗は∐(いずれか)、代数は単なるタイプです(すべてのタイプは独自の方法で∐の代数です。これは「モノイド」と呼ばれるものですが、通常は別の意味があります。を参照してください。下)。いつものように、私は⊥が存在しない架空の世界で働いているので、×は実際には製品などです。モノイド圏に適した型クラスを使用してこの一般化をキャプチャし(現時点でcategory-extrasがこの点で何をしようとしているのか理解するのに疲れすぎています)、それによってWriterとEitherを同時にモナドとして定義することはおそらく可能です(モジュロニュータイプ、おそらく)。

Writer mのカテゴリ双対については、固定と見なすものによって異なりますが、最も可能性の高い候補は、mに条件がない(、)mのcomonad構造であるようです。

instance Comonad ((,) m) where
    coreturn (m, a) = a
    cojoin (m, a) = (m, (m, a))

ここでは、mがコモノイドであることに注意してください。つまり、マップm→()、m→m×mがあります)。

于 2010-04-23T07:17:57.157 に答える
3

コードは次のとおりです。

{-# LANGUAGE FlexibleInstances, EmptyDataDecls, MultiParamTypeClasses,
FunctionalDependencies, GeneralizedNewtypeDeriving, UndecidableInstances #-}

import Control.Arrow (first, second, left, right)
import Data.Monoid

data Void
data Iso a b = Iso { from :: a -> b, to :: b -> a}

-- monoidal category (Hask, m, unit)
class MonoidalCategory m unit | m -> unit where
  iso1 :: Iso (m (m x y) z) (m x (m y z))
  iso2 :: Iso x (m x unit)
  iso3 :: Iso x (m unit x)

  map1 :: (a -> b) -> (m a c -> m b c)
  map2 :: (a -> b) -> (m c a -> m c b)

instance MonoidalCategory (,) () where
  iso1 = Iso (\((x,y),z) -> (x,(y,z))) (\(x,(y,z)) -> ((x,y),z))
  iso2 = Iso (\x -> (x,())) (\(x,()) -> x)
  iso3 = Iso (\x -> ((),x)) (\((),x) -> x)
  map1 = first
  map2 = second

instance MonoidalCategory Either Void where
  iso1 = Iso f g
         where f (Left (Left x)) = Left x
               f (Left (Right x)) = Right (Left x)
               f (Right x) = Right (Right x)

               g (Left x) = Left (Left x)
               g (Right (Left x)) = Left (Right x)
               g (Right (Right x)) = Right x
  iso2 = Iso Left (\(Left x) -> x)
  iso3 = Iso Right (\(Right x) -> x)
  map1 = left
  map2 = right

-- monoid in monoidal category (Hask, c, u)
class MonoidM m c u | m -> c u where
  mult :: c m m -> m
  unit :: u -> m

-- object of monoidal category (Hask, Either, Void)
newtype Eith a = Eith { getEith :: a } deriving (Show)

-- object of monoidal category (Hask, (,), ())
newtype Monoid m => Mult m = Mult { getMult :: m } deriving (Monoid, Show)

instance MonoidM (Eith a) Either Void where
  mult (Left x) = x
  mult (Right x) = x
  unit _ = undefined

instance Monoid m => MonoidM (Mult m) (,) () where
  mult = uncurry mappend
  unit = const mempty

instance (MonoidalCategory c u, MonoidM m c u) => Monad (c m) where
  return = map1 unit . from iso3
  x >>= f = (map1 mult . to iso1) (map2 f x)

使用法:

a = (Mult "hello", 5) >>= (\x -> (Mult " world", x+1))
                                 -- (Mult {getMult = "hello world"}, 6)
inv 0 = Left (Eith "error")
inv x = Right (1/x)
b = Right 5 >>= inv              -- Right 0.2
c = Right 0 >>= inv              -- Left (Eith {getEith="error"})
d = Left (Eith "a") >>= inv      -- Left (Eith {getEith="a"})
于 2010-04-27T20:47:34.287 に答える
1

厳密に言えば、二重では()ありVoidません。⊥の存在は、すべてのタイプが居住していることを意味します。したがって、⊥はの唯一の居住者でありVoid、期待どおりに最終オブジェクトになります。()には2つの値が存在するため、関係ありません。⊥離れて手を振ると、それ()はターミナルでありVoid、期待どおりに初期です。

あなたの例もコモノイド構造ではないと思います。コモノイドの署名は次のようになります。

class Comonoid a
    coempty :: a -> ()
    coappend :: a -> (a, a)

これは、同等のコモノイド法則がどうあるべきかを考えると、かなり役に立たないと思います。

代わりに、代数的データ型に適用されるように、あなたが得ているものは、自然界よりも標準的な合計/積モノイドとより密接に関連しているのだろうか?VoidEitherは0/+ですが、()(,)は1/*です。しかし、残りの部分を正当化する方法がわかりません。

于 2010-04-22T12:56:00.473 に答える