Monads作为附属物

时间:2011-01-15 00:31:01

标签: haskell monads functor category-theory

我一直在读类别理论中的单子。 monads的一个定义使用一对伴随函子。 monad是使用这些仿函数的往返定义的。显然,在类别理论中,附加是非常重要的,但我没有看到任何关于伴随函子的Haskell monad的解释。有没有人给它一个想法?

5 个答案:

答案 0 :(得分:38)

编辑:为了好玩,我会做对的。保留下面的原始答案

类别附加的当前附加代码现在位于附件包中:http://hackage.haskell.org/package/adjunctions

我只是明确而简单地通过州monad工作。此代码使用变换器包中的Data.Functor.Compose,但其他方式是自包含的。

f(D - > C)和g(C - > D)之间的附加,写入f - | g,可以以多种方式表征。我们将使用counit / unit(epsilon / eta)描述,它给出了两个自然变换(仿函数之间的态射)。

class (Functor f, Functor g) => Adjoint f g where
     counit :: f (g a) -> a
     unit   :: a -> g (f a)

请注意,对象中的“a”实际上是C中的身份函子,而单元中的“a”实际上是D中的身份函子。

我们还可以从counit / unit定义中恢复hom-set adjunction定义。

phiLeft :: Adjoint f g => (f a -> b) -> (a -> g b)
phiLeft f = fmap f . unit

phiRight :: Adjoint f g => (a -> g b) -> (f a -> b)
phiRight f = counit . fmap f

在任何情况下,我们现在都可以从我们的单位/国家协议中定义一个Monad:

instance Adjoint f g => Monad (Compose g f) where
    return x = Compose $ unit x
    x >>= f  = Compose . fmap counit . getCompose $ fmap (getCompose . f) x

现在我们可以实现(a,)和(a - >)之间的经典附加:

instance Adjoint ((,) a) ((->) a) where
    -- counit :: (a,a -> b) -> b
    counit (x, f) = f x
    -- unit :: b -> (a -> (a,b))
    unit x = \y -> (y, x)

现在是一个类型同义词

type State s = Compose ((->) s) ((,) s)

如果我们在ghci中加载它,我们可以确认State正是我们的经典状态monad。请注意,我们可以使用相反的组合并获取Costate Comonad(也就是商店comonad)。

我们可以用这种方式制作monad的其他附件(例如(Bool,)Pair),但它们有点奇怪的monad。不幸的是,我们不能以令人愉快的方式直接在Haskell中执行引导Reader和Writer的附加功能。我们可以做Cont,但正如copumpkin所描述的那样,需要来自相反类别的附加,所以它实际上使用了“Adjoint”类型类的不同“形式”来反转某些箭头。该表单也在附件包中的不同模块中实现。

德里克·埃尔金斯在“莫纳德读者13”中的文章以不同的方式介绍了这一材料 - 用类别理论计算单子:http://www.haskell.org/wikiupload/8/85/TMR-Issue13.pdf

此外,Hinze最近的Kan Extensions for Program Optimization论文从MonSethttp://www.cs.ox.ac.uk/ralf.hinze/Kan.pdf

之间的附件开始构建列表monad。

旧答案:

两个参考文献。

1)类别附加物一如既往地传递附加物的表示以及单子从它们产生的方式。像往常一样,考虑很好,但文档很简单:http://hackage.haskell.org/packages/archive/category-extras/0.53.5/doc/html/Control-Functor-Adjunction.html

2)-Cafe还提供了关于附加作用的有希望但简短的讨论。其中一些可能有助于解释类别附加:http://www.haskell.org/pipermail/haskell-cafe/2007-December/036328.html

答案 1 :(得分:10)

Derek Elkins最近在晚餐时向我展示了Cont Monad是如何通过将(_ -> k)逆变仿函数与其自身组合而产生的,因为它恰好是自我伴随的。这就是你如何得到(a -> k) -> k。然而,它的反对导致双重否定消除,这不能用Haskell写出来。

对于一些说明并证明这一点的Agda代码,请参阅http://hpaste.org/68257

答案 2 :(得分:8)

这是一个老线程,但我发现这个问题很有意思, 所以我自己做了一些计算。希望Bartosz还在那里 并且可能会读到这个..

事实上,在这种情况下,Eilenberg-Moore的结构确实给出了非常清晰的画面。 (我将使用CWM表示法与Haskell一样的语法)

T成为列表monad < T,eta,mu >eta = returnmu = concat) 并考虑T代数h:T a -> a

(请注意,T a = [a]是免费的幺半群<[a],[],(++)>,即身份[]和乘法(++)。)

根据定义,h必须满足h.T h == h.mu ah.eta a== id

现在,一些简单的图表追逐证明h实际上诱导了一个幺半群结构(由x*y = h[x,y]定义), 并且h成为这种结构的单一同态。

相反,Haskell中定义的任何幺半群结构< a,a0,* >自然被定义为T-代数。

以这种方式(h = foldr ( * ) a0,一个'(:)替换(*)[]映射到a0的功能。< / p>

因此,在这种情况下,T-代数的类别只是Haskell,HaskMon中可定义的幺半群结构的类别。

(请检查T-algebras中的态射实际上是单态同态。)

它还将列表描述为HaskMon中的通用对象,就像Grp中的免费产品,CRng中的多项式环等一样。

对应于上述结构的调整是< F,G,eta,epsilon >

,其中

  • F:Hask -> HaskMon,其类型a为a'生成的“免费幺半群”,即[a]
  • G:HaskMon -> Hask,健忘的仿函数(忘记乘法),
  • eta:1 -> GF,由\x::a -> [x]
  • 定义的自然转换
  • epsilon: FG -> 1,由上面的折叠函数定义的自然变换                 (从“自由幺半群”到“商幺”的“规范投射”)

接下来,还有另一个'Kleisli类别'和相应的附加功能。 你可以检查它只是具有态射a -> T b的Haskell类型的类别, 其成分由所谓的“Kleisli成分”(>=>)给出。 典型的Haskell程序员会发现这个类别更为熟悉。

最后,如CWM中所示,T-代数的类别 (相应的Kleisli类别)成为类别中的终端(resp.initial)对象 在合适的意义上定义列表monad T的辅助。

我建议对二叉树仿函数T a = L a | B (T a) (T a)进行类似的计算以检查您的理解。

答案 3 :(得分:2)

我已经为Eilenberg-Moore找到了任何monad的附加仿函数的标准结构,但是我不确定它是否会增加任何洞察力。构造中的第二类是T-代数。 T代数在初始类别中添加“产品”。

那么如何为列表monad工作呢?列表monad中的仿函数包括类型构造函数,例如Int->[Int]和函数映射(例如,映射到列表的标准应用)。代数添加从列表到元素的映射。一个例子是添加(或乘以)整数列表的所有元素。仿函数F采用任何类型,例如Int,并将其映射到Int列表中定义的代数,其中产品由monadic join定义(反之亦然,join定义为产品)。健忘的仿函数G采用代数并忘记了产品。然后使用伴随函子的FG对以通常的方式构造monad。

我必须说我不是更聪明的人。

答案 4 :(得分:1)

如果您有兴趣,这里有一些非专家的想法 关于monad和adjunction在编程语言中的作用:

首先,给定的monad T存在对T的Kleisli类别的唯一附加。 在Haskell中,monad的使用主要局限于此类别的操作  (基本上是一类免费代数,没有商)。 事实上,所有人都可以用Haskell Monad来组成一些Kleisli态射 通过使用do表达式a->T b等来键入(>>=)来创建新的 射。在这种情况下,monad的作用仅限于经济 of notation.One利用态射的相关性来写(例如)[0,1,2] 而不是(Cons 0 (Cons 1 (Cons 2 Nil))),也就是说,您可以将序列写为序列, 不是一棵树。

即使使用IO monads也不是必需的,因为当前的Haskell类型系统功能强大 足以实现数据封装(存在类型)。

这是我对原始问题的回答, 但我很好奇Haskell专家对此有何看法。

另一方面,正如我们已经注意到的那样,monad和。之间也存在1-1的对应关系 (T-)代数的附加。按照麦克莱恩的说法,选择是一种方式 表达类别的等价性。 在<F,G>:X->A的附加F的典型设置中,A是某种类型 '自由代数发生器'和G''健忘的仿函数',相应的monad 将(通过使用T代数)描述X的对象构造T的代数结构的方式(以及何时)。

在Hask和列表monad T的情况下,foldr (*) e::[a]->a引入的结构就是这样 of monoid,这可以帮助我们通过代数建立代码的属性(包括正确性) 幺半群理论提供的方法。例如,函数<a,(*),e>可以 只要<F,G>:Hask->A是一个幺半群,就可以很容易地看作是一个关联操作, 可以由编译器利用的事实来优化计算(例如,通过并行)。 另一个应用是使用分类来识别和分类函数式编程中的“递归模式” 希望(部分)处理'函数式编程的转换',Y(任意递归组合器)的方法。

显然,这种应用是类别理论创造者(MacLane,Eilenberg等)的主要动机之一, 即,建立类别的自然等价,并将一种众所周知的方法转移到一个类别中 到另一个(例如拓扑空间的同源方法,编程的代数方法等)。 在这里,adjoints和monad是利用这种类别连接不可或缺的工具。 (顺便提一下,monad(及其双重,comonads)的概念是如此普遍,以至于人们甚至可以定义'cohomologies' Haskell类型。但我还没有想过。)

至于你提到的非决定性功能,我更不用说了...... 但请注意;如果某个类别A的附件T定义了列表monad K:A->MonHask, 必须有一个独特的“比较函子”T a1 .. an = T1 T11 .. T1m | ...(在Haskell中可定义的幺半群类别),请参阅CWM。 实际上,这意味着您的兴趣类别必须是某种限制形式的幺半群(例如,它可能缺少一些商而不是免费代数)才能定义列表monad。

最后,一些评论:

我在上一篇文章中提到的二叉树仿函数很容易推广到任意数据类型 {{1}}。 也就是说,Haskell中的任何数据类型都自然地定义了monad(连同相应的代数类别和Kleisli类别), 这只是Haskell中任何数据构造函数的结果。 这是我认为Haskell的Monad类不仅仅是语法糖的另一个原因 (当然,这在实践中非常重要)。

相关问题