5

Writer m我注意到和Either 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)

() 的对偶是 Void(空类型),积的对偶是联积。每个类型 e 都可以被赋予“comonoid”结构:

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单子。箭头遵循完全相同的模式。

问题:是否可以编写一个通用代码来执行 asEither e和 asWriter m取决于给定的幺半群?

4

3 回答 3

5

我不会说这些单子是绝对对偶的,而是它们都是由以下构造产生的:给定一个单类(C, ⊗, 1) 和 C 中的代数 A,考虑将 X 发送到 A ⊗ X 的 monad。在第一种情况下,C 是 Hask,⊗ 是 ×,代数是幺半群,而在第二种情况下 C是 Hask,⊗ 是 ∐(两者之一),代数只是一种类型(每种类型都是以独特方式写出 ∐ 的代数——这就是你所说的“comonoid”,尽管这通常意味着别的东西,请参阅以下)。按照惯例,我在一个虚构的世界中工作,⊥ 不存在,所以 × 实际上是一个产品等等。可能有可能使用合适的类型类来捕获这种常见的泛化,用于 monoidal 类别(我太累了,无法理解 category-extras 目前在这方面试图做什么),从而同时将 Writer 和 Either 定义为单子(模新类型,可能)。

至于 Writer m 的分类对偶——嗯,这取决于你想认为什么是固定的,但最有可能的候选者似乎是 (,) m 上的共单子结构,而 m 上没有任何条件:

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,使其成为您所期望的终端对象。()包含两个值,因此不相关。如果你挥手 ⊥ 离开,那么()就是终端并且Void是初始的。

我不认为你的例子是一个comonoid结构,或者 - 一个comonoid的签名应该是这样的,我认为:

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

我认为,如果你考虑等效的共形定律必须是什么,最终会变得相当无用。

相反,我想知道您所得到的是否与应用于代数数据类型的标准 sum/product monoids 比自然更密切相关?VoidEither是 0/+,而()(,)是 1/*。但我不确定如何证明其余部分的合理性。

于 2010-04-22T12:56:00.473 回答