Monads作为附件


78

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


2
很好的问题,我自己对此一直很好奇。
汤姆·克罗基特

我试图找出一个monad的一般来说,这两个(伴随)函子实际上是什么...所以,我也很希望能回答您的问题!我目前正在阅读MacLane书,因此,如果我找到答案,我会立即发布。
Alp Mestanogullari 2011年

3
我注意到,在大多数示例中,第一个函子进入一个更丰富的类别,具有更多的结构,第二个函子是健忘的。因此,将两者结合成往返的monad具有某种更丰富的结构痕迹。我的类比是:从寒武纪开始,用Evolution函子将其映射到我们当前的生态系统中,而不是使用一些健忘的函子进行映射。您得到的是描述动物不同身体计划的“单子”(它们都是在寒武纪爆炸期间产生的)。Monads作为诸如组,代数等事物的“身体计划”
Bartosz Milewski

也许这个问题更适合Programmer.stackexchange.com ...?
stakx-不再贡献

3
我希望看到更多的单子分解为伴随函子。我对身份,常量,读者,作家,列表,树,也许,自由和概率感兴趣。State和Cont已经在答案中。
phischu 2014年

Answers:


39

编辑:只是为了好玩,我会做正确的。原始答案保留在下面

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

我将简单明了地通过状态monad进行工作。这段代码Data.Functor.Compose是在Transformers软件包中使用的,但是它是自包含的。

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中的身份函子。

我们还可以从单位/单位定义中恢复hom-set附加定义。

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正是我们的经典state monad。请注意,我们可以采用相反的组成并获得Costate Comonad(又名comonad商店)。

我们还可以通过这种方式将其他附加词做成单子(例如(Bool,)Pair),但它们却是一种奇怪的单子。不幸的是,我们无法以令人愉快的方式直接在Haskell中进行诱导Reader和Writer的附加操作。我们可以做Cont,但是正如copumpkin所描述的,这需要来自相反类别的附加语,因此它实际上使用了“ Adjoint”类型类的另一个“形式”,它使一些箭头反向。该形式还可以在附加程序包的其他模块中实现。

德里克·埃尔金斯(Derek Elkins)在《 Monad Reader 13-用范畴论计算单子》中的文章以不同的方式介绍了该材料:http : //www.haskell.org/wikiupload/8/85/TMR-Issue13.pdf

另外,Hinze最近在“ Kan Extensions for Program Optimization”一文中还通过Mon和之间的附加内容逐步构建了列表monad Sethttp : //www.cs.ox.ac.uk/ralf.hinze/Kan.pdf


旧答案:

两个参考。

1)范畴扩展一如既往地提供了附加词及其从中产生的单子的表示。和往常一样,最好考虑一下,但对文档的要求很轻:http : //hackage.haskell.org/packages/archive/category-extras/0.53.5/doc/html/Control-Functor-Adjunction.html

2)-咖啡馆还对附属的作用进行了有希望的但简短的讨论。其中一些可能有助于解释类别引伸:http : //www.haskell.org/pipermail/haskell-cafe/2007-December/036328.html


您足够友好地为Adjoint的具体实例添加类型签名,但是我相信应该如此unit :: b -> (a -> (a,b))
phischu 2014年

嗯,我认为这更有意义。
sclv 2014年

10

德里克·埃尔金斯(Derek Elkins)最近在晚餐时向我展示了Cont Monad是如何通过将(_ -> k)自反函子与自身组合而产生的,因为它恰好是自伴的。这就是您摆脱(a -> k) -> k困境的方式。但是,它的协同作用会导致双重否定消除,这无法用Haskell编写。

有关说明和证明这一点的某些Agda代码,请参见http://hpaste.org/68257


1
您能否提供更多细节?我并不一定期望函子会生活在Hask中。更像是一个函子从Hask跳到另一个类别,另一个又回来了。例如,据我所知,Moggi定义了某种元语言的函子。
Bartosz Milewski

@BartoszMilewski:只是决定重新考虑这个问题!我构建了一个Agda证明,对其进行了更好的解释:hpaste.org/68257。我还将进一步讲解其他常见的Haskell单子对的伴随函子。
copumpkin,2012年

9

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

实际上,在这种情况下,艾伦伯格-摩尔(Eilenberg-Moore)结构的确提供了非常清晰的画面。(我将在类似Haskell的语法中使用CWM表示法)

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

(请注意,这T a = [a]是一个免费的monoid <[a],[],(++)>,即身份[]和乘法(++)。)

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

现在,一些简单的图追踪证明h实际上在(定义x*y = h[x,y])上诱导了一个单面体结构,并且h对该结构变成了一个单面体同构。

相反,< a,a0,* >在Haskell中定义的任何类单元结构自然定义为T代数。

通过这种方式(h = foldr ( * ) a0,一个功能“替代”(:)(*),并映射[]a0,身份)。

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

(请检查T代数中的态射实际上是类单态同态。)

它还将列表表征为HaskMon中的通用对象,就像Grp中的免费乘积,CRng中的多项式环等。

与上述结构相对应的裁定为 < F,G,eta,epsilon >

哪里

  • F:Hask -> HaskMon,其类型为“生成的自由monoid a”,即[a]
  • G:HaskMon -> Hask,健忘的函子(忘记乘法),
  • eta:1 -> GF ,天然转化定义通过\x::a -> [x]
  • epsilon: FG -> 1 ,由上面的折叠函数定义的自然变换(从自由单半体到其商单半体的“规范超越”)

接下来,还有另一个“克莱斯里类别”和相应的附加语。您可以检查它是否只是具有态射的Haskell类型的类别a -> T b,其组成由所谓的“ Kleisli组成”给出(>=>)。典型的Haskell程序员会发现此类别更加熟悉。

最终,如CWM中所示,T代数的类别(分别为Kleisli类别)成为在适当意义上定义列表单子T的裁量类别中的最终对象(分别为初始)。

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


我还在这儿。感谢您的解释。这与我从Eilenberg-Moore构造中得出的结论相似,不同之处在于您的描述更好,更详细。问题在于,它不能使人们更好地了解Monad在Haskell中的作用。list monad应该描述非确定性函数。如果有人能够显示出一类不确定性函数的构造,并且表明它与Hask之间的附加关系产生了清单单子,我会真的印象深刻。
Bartosz Milewski

@BartoszMilewski可能是在交错的9年中的某个时候,您遇到了这种情况,但这实际上就是Kleisli附加功能的列表版本为您提供的功能。它说明了具有传统Haskell函数的类型类别与它们之间具有不确定函数的类型类别之间的附加关系。欢呼声
HaskellLearner

2

我已经为Eilenberg-Moore的任何monad找到了辅助函子的标准构造,但是我不确定它是否对问题有任何启示。构造中的第二类是T代数。AT代数将“产品”添加到初始类别。

那么,对于列表monad它将如何工作?list monad中的函子由类型构造Int->[Int]函数(例如)和功能映射(例如map到list的标准应用程序)组成。代数添加了从列表到元素的映射。一个示例是将一个整数列表中的所有元素相加(或相乘)。函子F采用任何类型,例如Int,并将其映射到Int列表中定义的代数,其中乘积由一元连接定义(反之亦然,连接定义为乘积)。健忘的函子G取一个代数就忘记了乘积。一对FG,伴随函子的随后用于构建单子以通常的方式。

我必须说我没有一个明智的选择。


1

如果您有兴趣,可以考虑以下非专家对编程语言中monad和附加词的作用的看法:

首先,对于给定的monad,存在TKleisli类别的唯一附加语T。在Haskell中,monad的使用主要限于此类别中的运算(该类别实质上是自由代数,无商数的类别)。实际上,使用Haskell Monad所能做的就是a->T b通过使用do表达式(>>=)等来组合一些类型的Kleisli态射像,以创建新的态射像。在这种情况下,单子的作用仅限于符号的经济性。一个人利用态素的关联性来写(比如说)[0,1,2] 而不是(Cons 0 (Cons 1 (Cons 2 Nil))),即可以将序列写为序列而不是树。

甚至不需要使用IO monad,因为当前的Haskell类型系统功能强大,足以实现数据封装(现有类型)。

这是我对您最初的问题的回答,但我很好奇Haskell专家对此有何评论。

另一方面,正如我们已经指出的,单子与(T-)代数的附加词之间也存在1-1对应关系。用MacLane的术语来说,伴随是“表达类别对等的一种方式”。在典型的附加条件中<F,G>:X->AF某种形式的“自由代数生成器”和G是“健忘函子”,相应的单子将(通过使用T代数)描述如何(以及何时)构造的代数结构A。的对象X

在Hask和list monad T的情况下,T引入的结构是类半体的结构,这可以帮助我们通过类半体理论提供的代数方法来建立代码的属性(包括正确性)。例如,该函数foldr (*) e::[a]->a可以很容易地被视为关联操作,只要它<a,(*),e>是一个monoid,便可以被编译器用来优化计算(例如,通过并行性)的事实。另一个应用程序是使用分类方法对函数式编程中的“递归模式”进行识别和分类,以期(部分)处置“函数式编程的起点” Y(任意递归组合器)。

显然,这种应用是类别理论的创建者(MacLane,Eilenberg等)的主要动机之一,即建立类别的自然对等关系,并将一种类别中的知名方法转移到另一类别(例如,拓扑空间的同构方法,编程的代数方法等)。在这里,伴随和单子是利用类别的这种联系的必不可少的工具。(顺便提一下,单子(及其双偶,共母)的概念是如此笼统,以至于甚至可以定义Haskell类型的“同调”,但我还没有想到。)

至于您提到的非确定性函数,我要说的要少得多。如果<F,G>:Hask->A某个类别A的附加语定义了列表monad T,则必须有一个唯一的“比较函子” K:A->MonHask(在Haskell中可定义的类单元体的类别),请参阅CWM。实际上,这意味着您要关注的类别必须是某种受限形式的Monoid类别(例如,它可能缺少一些商但不是自由代数)才能定义列表单子。

最后,一些评论:

我在上一篇文章中提到的二叉树函子很容易推广到任意数据类型 T a1 .. an = T1 T11 .. T1m | ...。也就是说,Haskell中的任何数据类型自然会定义一个monad(以及相应的代数类别和Kleisli类别),这仅仅是Haskell中任何数据构造函数的总和。这就是为什么我认为Haskell的Monad类不只是语法糖(当然,这在实践中非常重要)的另一个原因。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.