monad可以成为comonad吗?

时间:2013-05-14 19:52:56

标签: haskell category-theory

我知道monad是什么。我我已经正确地把我的想法包围在一个comonad是什么。 (或者说,

我的问题是:有些东西可以是monad 是comonad吗?

我预见到两个可能的答案:

  • 是的,这很常见且非常有用。
  • 不,他们做了不同的工作,没有理由想要两者兼而有之。

那么,这是什么?

5 个答案:

答案 0 :(得分:25)

Cofree Comonad产生的一些数据结构可用作Monads和Comonads:

data Cofree f a = a :< f (Cofree f a)

每个Cofree Comonad对一个替代仿函数产生一个Monad - 请参见此处的实例:

http://hackage.haskell.org/packages/archive/free/3.4.1/doc/html/Control-Comonad-Cofree.html

instance Alternative f => Monad (Cofree f) where
  return x = x :< empty
  (a :< m) >>= k = case k a of
                     b :< n -> b :< (n <|> fmap (>>= k) m)

这给了我们,例如非空列出Monad和Comonads(以及非空的f分支树等)。

Identity不是替代方案,但Cofree Identity会产生无限流,我们实际上可以为该流提供不同的 monad实例:

http://hackage.haskell.org/packages/archive/streams/3.1/doc/html/Data-Stream-Infinite.html

data Stream a = a :> Stream a
instance Comonad Stream where
  duplicate = tails
  extend f w = f w :> extend f (tail w)
  extract = head

instance Monad Stream where
  return = repeat
  m >>= f = unfold (\(bs :> bss) -> (head bs, tail <$> bss)) (fmap f m)

(注意上面的函数不在列表中,而是在streams包中定义)。

类似地,读者箭头不是替代品,但Cofree ((->) r)产生了摩尔机器,而摩尔机器也是monad和comonads:

http://hackage.haskell.org/packages/archive/machines/0.2.3.1/doc/html/Data-Machine-Moore.html

data Moore a b = Moore b (a -> Moore a b)
instance Monad (Moore a) where
  return a = r where r = Moore a (const r)
  Moore a k >>= f = case f a of
    Moore b _ -> Moore b (k >=> f)
  _ >> m = m
instance Comonad (Moore a) where
  extract (Moore b _) = b
  extend f w@(Moore _ g) = Moore (f w) (extend f . g)

那么所有这些例子背后的直觉是什么?好吧,我们免费获得comonadic操作。我们得到的monadic操作都是对角化的形式。有了替代方案,我们可以<|>一起“扼杀”结构,并在我们用完结构来消除时,将“空”的东西魔术化。这让我们可以处理有限的案例。缺乏替代方案我们需要有一个无限量的结构,这样无论我们制造了多少“连接”操作(我们可以认为是拼接或替换),拼接元素总是有更多的空间(比如希尔伯特酒店:http://www.encyclopediaofmath.org/index.php/Hilbert_infinite_hotel)。

相关地,每个 Comonad都会产生一个相关的Monad(虽然我认为这更像是一个好奇心):

http://hackage.haskell.org/packages/archive/kan-extensions/3.1.1/doc/html/Control-Monad-Co.html

http://comonad.com/reader/2011/monads-from-comonads/

答案 1 :(得分:22)

是。将一些评论转化为答案:

newtype Identity a = Identity {runIdenity :: a} deriving Functor
instance Monad Identity where
  return = Identity
  join = runIdentity
instance CoMonad Identity where
  coreturn = runIdentity
  cojoin = Identity

Reader和Writer是精确的对偶,如

所示
class CoMonoid m where
  comempty :: (m,a) -> a
  comappend :: m -> (m,m)
--every haskell type is a CoMonoid
--that is because CCCs are boring!

instance Monoid a => Monad ((,) a) where
  return x = (mempty,x)
  join (a,(b,x)) = (a <> b, x)
instance CoMonoid a => CoMonad ((,) a) where
  coreturn = comempty
  cojoin = associate . first comappend

instance CoMonoid a => Monad ((->) a) where
  return = flip (curry comempty)
  join f = uncurry f . comappend
instance Monoid a => CoMonad ((->) a)  where
  coreturn f = f mempty
  cojoin f a b = f (a <> b)

答案 2 :(得分:14)

有许多有趣的结构都是MonadComonad

其他几个人已经在这里指出了Identity仿函数,但是有一些非常重要的例子。

Writer Monad扮演Reader类似Comonad的角色。

instance Monoid e => Monad ((,) e)
instance Comonad ((,) e)

Reader Monad扮演Writer类似Comonad的角色。

instance Monad ((->) e)
instance Monoid e => Comonad ((->)e)

非空列表也同时形成monad和comonad,实际上是涉及cofree comonad的更大构造的特例。 Identity案例也可以视为一种特殊情况。

还有各种基于Kan扩展的YonedaCodensity类似的构造,它们可以转换monad和comonads,尽管它们在运营效率方面偏向于其中一个。

我还有一个适配器,可以将任意comonad转换为monad转换器。可悲的是,在Haskell中无法进行相反的转换。

在线性代数中有一个bialgebra的概念。理想情况下,如果我们的某些内容同时形成MonadComonad,并且我们希望在不根据具体情况的基础上将这些操作结合使用,那么我们希望拥有return join }和extract是Comonad余代数,并且duplicateMonadMonad f代数。如果这些条件成立,那么您实际上可以推断出同时具有Comonad f和{{1}}约束的代码,并且在没有逐案推理的情况下混合各自的组合子。

答案 3 :(得分:5)

这取决于你认为的“monad”是什么。如果您问“某个类型是否可以同时成为MonadComonad的实例?”好的。这是一个微不足道的例子。

newtype Id a = Id a

instance Monad Identity where
  return       = Id
  (Id a) >>= f = f a

instance Comonad Identity where
  extract (Id a) = a
  extend f ida = Id (f ida)

如果您在数学上表示,那么monad就是三(X, return, bind),其中X是一种类型,returnbind遵循您期望的类型和法则。同样,comonad是(X, extend, extract)。我刚刚证明X可能是相同的,但由于extendreturnextractbind的类型不同,因此不是可能他们是相同的功能。所以数学monad永远不会是一个comonad。

答案 4 :(得分:1)

扩展Hammer的建议,编写函数[x] -> [[x]]似乎很简单。例如,

map (\ x -> [x])

会工作得很好。所以看起来列表可能形成一个comonad。啊,等等。处理cojoin,但coreturn :: [x] -> x怎么办?据推测,这个就是为什么只有非空列表形成一个comonad。

这为我们提供了类型为([x] -> x) -> [x] -> [x]的cobind函数。有趣的是,Hoogle知道没有这样的功能。但我们已经concatMap :: (x -> [x]) -> [x] -> [x]了。我没有看到cobind函数立即使用,但我可以想象一个现有的。

我仍然试图围绕comonad以及它可能有用的内容。到目前为止,答案给了我一些思考......