Haskell 单子可以是单子吗?

Haskell 单子可以是单子吗?,haskell,category-theory,Haskell,Category Theory,我知道什么是单子。我想我已经正确地理解了什么是科摩纳德。(或者更确切地说,一个人所做的似乎足够简单;棘手的部分是理解这件事的有用之处……) 我的问题是:有些东西可以是单子和单子吗 我预见到两个可能的答案: 是的,这是常见的,并且广泛有用 不,他们的工作如此不同,没有理由希望两者兼而有之 那么,是哪一个呢?是的。将一些评论转化为答案: newtype Identity a = Identity {runIdenity :: a} deriving Functor instance Monad

我知道什么是单子。我想我已经正确地理解了什么是科摩纳德。(或者更确切地说,一个人所做的似乎足够简单;棘手的部分是理解这件事的有用之处……)

我的问题是:有些东西可以是单子和单子吗

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

  • 是的,这是常见的,并且广泛有用
  • 不,他们的工作如此不同,没有理由希望两者兼而有之

那么,是哪一个呢?

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

newtype Identity a = Identity {runIdenity :: a} deriving Functor
instance Monad Identity where
  return = Identity
  join = runIdentity
instance CoMonad Identity where
  coreturn = runIdentity
  cojoin = Identity
读者和作者是完全对立的,如

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)
类CoMonoid m,其中
comempty::(m,a)->a
comappend::m->(m,m)
--每种haskell类型都是一种科摩罗类
--那是因为CCC很无聊!
实例幺半群a=>Monad((,)a)其中
返回x=(mempty,x)
连接(a,(b,x))=(ab,x)
实例CoMonoid a=>CoMonad((,)a)其中
coreturn=comempty
cojoin=associate。第一个comappend
实例CoMonoid a=>Monad((->)a),其中
return=flip(咖喱馅饼)
加入f=未经修正的f。comappend
实例幺半群a=>CoMonad((->)a),其中
coreturn f=f mempty
cof a b=f(a b)

这取决于你认为的“单子”是什么。如果你问“一个类型是否可能同时是
Monad
Comonad
的实例?”那么,是的。这里有一个简单的例子

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)

如果从数学上讲,那么单子是一个三元组
(X,return,bind)
,其中
X
是一个类型,
return
bind
遵循您期望的类型和规律。类似地,comonad是
(X,extend,extract)
。我刚刚演示了
X
s可能是相同的,但是由于
extend
return
extract
bind
的类型不同,它们不可能是相同的函数。因此,数学单子永远不可能是共元。

共自由共元产生了一些数据结构,这些数据结构对于单子和共元都很有用:

data Cofree f a = a :< f (Cofree f a)
(请注意,上面的函数不在列表中,而是在
streams
包中定义的)

类似地,reader arrow不是一个替代品,但是
Cofree((->)r)
产生一个摩尔机器,摩尔机器也是单子和单子:

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

那么这些例子背后的直觉是什么呢?我们可以免费获得科摩纳迪克手术。我们得到的一元运算都是对角化的形式。使用alternative,我们可以将
事物组合在一起以“弄脏”结构,并在结构用完时将“空”事物变为“弄脏”。这让我们可以处理有限的情况。由于缺乏替代方案,我们需要有一个无限量的结构,这样无论我们进行多少次“连接”操作(我们可以认为是拼接或替换),总有更多的空间放置拼接元素(如希尔伯特酒店:)

P.>相对地,每个科摩纳德都产生了一个相关的Monad(虽然我认为这更令人好奇):


扩展Hammer的建议,编写函数
[x]->[[x]]]
似乎足够简单。比如说,

map (\ x -> [x])
这样就行了。所以看起来列表可以组成一个comonad。啊,等等。它处理
cojoin
,但是
coreturn::[x]>x
呢?这大概就是为什么只有非空列表才会形成一个comonad

这为我们提供了一个类型为
([x]->x)->[x]->[x]
的cobind函数。有趣的是,胡格尔不知道这样的功能。然而我们已经有了
concatMap::(x->[x])->[x]->[x]
。我没有看到cobind函数的直接用途,但我可以想象一个存在


我仍在努力思考科摩纳德以及它可能有什么用处。到目前为止的答案给了我一些思考的东西…

有许多有趣的结构,它们既是一个
单子,又是一个
单子

instance Monoid e => Monad ((,) e)
instance Comonad ((,) e)
instance Monad ((->) e)
instance Monoid e => Comonad ((->)e)
恒等式
函子在这里已经被其他一些人指出,但也有一些不平凡的例子

Writer
Monad
作为
Comonad
扮演着
Reader
的角色

instance Monoid e => Monad ((,) e)
instance Comonad ((,) e)
instance Monad ((->) e)
instance Monoid e => Comonad ((->)e)
读卡器
单子
扮演着
写卡器
-like角色,充当
Comonad

instance Monoid e => Monad ((,) e)
instance Comonad ((,) e)
instance Monad ((->) e)
instance Monoid e => Comonad ((->)e)
非空列表也同时形成单子和余子,事实上是涉及余子余子的更大结构的特例。
Identity
案例也可以看作是这种情况的特例

还有各种基于Kan扩展的
Yoneda
Codensity
类构造,它们用于转换monad和comonad,尽管它们在操作效率方面支持其中一种

我还有一个适配器,可以将任意的comonad转换成monad转换器。不幸的是,在哈斯凯尔不可能实现相反的转换


在线性代数中,有一个概念是a。理想情况下,如果我们有同时形成
Monad
Comonad
的东西,并且我们希望在不逐案推理的情况下将这些操作一起使用,我们希望
return
join
是Comonad余代数,
extract
duplicate
Monad
代数。如果这些条件成立,那么您实际上可以对同时具有
单子f
共元f
约束的代码进行推理,并在不进行逐案推理的情况下混合每个约束的组合子。

好吧,
标识
是一个很小的例子。
读卡器
写卡器
也可以是共元,虽然它们所做的是不同的,并且需要交换
幺半群
约束。非空列表是另一个例子。哎,哈斯凯尔伟大的人们,这是你们大放异彩的时候了!从评论中走出来,开始解释吧!我想要一个书面的或三个答案。科摩纳德