从各个角度看Monad - 数学,图解和编程

时间:2013-09-21 02:04:15

标签: haskell functional-programming monads category-theory

我正在尝试将Monad的分类定义与我在其他一些教程/书籍中看到的其他一般表示/定义进行协调。

下面,我(或许有力地)试图将两个定义关闭,请指出错误并提供更正,如果需要的话

所以从Monads的定义开始

  

Monads只是endofunctors类别中的幺半群。

并且对endofunctors有一点了解,我假设Monad可以写为

((a->b)->Ma->Mb)->((b->c)->Mb->Mc) 

LHS的“Type”(左侧)是Mb,RHS的类型是Mc,所以我想我可以按如下方式编写

Mb-> (b->c)-> Mc, **which is how we define bind**

以下是我看到Monads在endofuctors类别中的看法(它们本身在Category C中,types'为objects

Monad Visually

这有什么意义吗?

3 个答案:

答案 0 :(得分:9)

嗯,嗯,我觉得你有点不对劲。 monad是一个endofunctor,它在Hask(Haskell类型的类别)中具有类F :: * -> *的东西,其中有一些函数知道如何将态射(函数)注入到Hask的子类别中,函数放在{{1}上s为态射,F为对象,F。你所拥有的东西似乎是哈斯克的自然变革。

示例:fmapMaybeEither a等。

现在monad也必须有2个自然变换(hask中为(,) a)。

Functor F, Functor g => F a -> G a

或者在haskell代码中

n : Identity -> M
u : M^2 -> M

分别对应n :: Identity a -> M a -- Identity a == a u :: M (M a) -> M a return

现在我们必须到达join。你所拥有的绑定实际上只是>>=,我们真正想要的是fmap。这很容易定义为

m a -> (a -> m b) -> m b

多田!我们有单子。现在为这个幺半群的endofunctors。

在endofunctors上的幺半群将有一个仿函数作为对象和自然变换作为态射。有趣的是,两个endofunctor的产品是它们的组成。这是我们新的幺半群的Haskell代码

 m >>= f = join $ f `fmap` m

这个去了

type (f <+> g) a = f (g a)
class Functor m => Monoid' m where
    midentity' :: Identity a -> m a
    mappend' :: (m <+> m) a -> m a

看起来很熟悉?

答案 1 :(得分:5)

定义“Monads只是endofunctors类别中的幺半群。”,虽然这是一个糟糕的起点。它来自blog post,主要是为了开个玩笑。但如果你对这种对应感兴趣,可以在Haskell中证明:

类别的外行描述是对象之间的对象和态射的抽象集合。类别之间的映射称为仿函数,并将对象映射到对象,并将态射映射到态射,并保留身份。 endofunctor 是从类别到其自身的仿函数。

{-# LANGUAGE MultiParamTypeClasses, 
             ConstraintKinds,
             FlexibleInstances,
             FlexibleContexts #-}

class Category c where
  id  :: c x x
  (.) :: c y z -> c x y -> c x z

class (Category c, Category d) => Functor c d t where
  fmap :: c a b -> d (t a) (t b)

type Endofunctor c f = Functor c c f

满足所谓naturality conditions的仿函数之间的映射称为自然变换。在Haskell中,这些是类型为(Functor f, Functor g) => forall a. f a -> g a的多态函数。

类别C上的 monad 有三件事(T,η,μ)T是endofunctor,1是{{1}上的身份仿函数}}。 Mu和eta是两个满足triangle identity和相关性标识的自然变换,定义为:

  • C
  • η : 1 → T

在Haskell中μ : T^2 → Tμjoinη

  • return
  • return :: Monad m => a -> m a

可以写出Haskell中Monad的分类定义:

join :: Monad m => m (m a) -> m a

可以从这些派生运算符派生出来。

class (Endofunctor c t) => Monad c t where
  eta :: c a (t a)
  mu :: c (t (t a)) (t a)

这是一个完整的定义,但同样您也可以证明Monad定律可以用仿函数类别表示为Monoid定律。我们可以构造这个仿函数类别,它是一个带有对象作为仿函数(即类别之间的映射)和自然变换(即仿函数之间的映射)作为态射的类别。在endofunctors类别中,所有仿函数都是同一类别之间的仿函数。

(>>=) :: (Monad c t) => c a (t b) -> c (t a) (t b)
(>>=) f = mu . fmap f

我们可以证明这会产生一个带有函子组成的类别作为态射组成:

newtype CatFunctor c t a b = CatFunctor (c (t a) (t b))

幺半群有通常的定义:

-- Note needs UndecidableInstances to typecheck
instance (Endofunctor c t) => Category (CatFunctor c t) where
  id = CatFunctor id
  (CatFunctor g) . (CatFunctor f) = CatFunctor (g . f)

一类仿函数的幺半群具有自然变换作为身份a和乘法运算,它结合了自然变换。可以定义Kleisli成分以满足乘法定律。

class Monoid m where
  unit :: m
  mult :: m -> m -> m

所以你有它“Monads只是endofunctors类别中的幺半群”,这只是来自endofunctors和(mu,eta)的monad的正常定义的“无点”版本。

(<=<) :: (Monad c t) => c y (t z) -> c x (t y) -> c x (t z)
f <=< g = mu . fmap f . g 

通过一些替换,我们可以证明instance (Monad c t) => Monoid (c a (t a)) where unit = eta mult = (<=<) 的幺半群属性是monad自然变换的三角形和相关性图的等价陈述。

(<=<)

如果您对diagrammatic representations感兴趣,我已经写了一些关于用字符串图表示它们的内容。

答案 2 :(得分:2)

在我看来,你忽略了重要的事情:

  • 定义中有“monoid”一词。您没有在帖子中考虑它。
  • 最好用“monoid object”替换它以便精确。 Monoids生活在抽象代数中,monoid对象生活在范畴理论中。不同的物种。
    • Monoids是某些类别中的幺半群对象,但这与此无关。
  • monoid对象可以仅定义为monoid类别。

因此,理解定义的途径如下:

  • 您在Haskell类型和函数 Hask 的类别中考虑了一类endofunctors(我们称之为 E )。它的对象是 Hask 上的仿函数,其中从F到G的态射是类型F a的多态函数 - > G a with some property。
  • 您认为 E 上的结构会将其转换为幺半群类别,即仿函数和身份仿函数的组合。
  • 您考虑了幺半群对象的定义,例如:来自维基百科。
  • 您可以考虑在特定类别 E 中的含义。这意味着M是一个endofunctor,μ与“join”的类型相同,η与“return”的类型相同。
  • “(&gt;&gt; =)”是通过“加入”定义的。

建议。不要试图在Haskell中表达一切。它专为编写程序而非数学而设计。数学符号在这里更方便,因为你可以把编写器的组合写成“∘”而不会遇到类型检查器的麻烦。