什么是免费单子?

我已经看到Free Monad这个术语偶尔会弹出一段时间,但似乎每个人似乎都会使用/讨论它们,而没有给出它们的解释。 那么,什么是免费单子? (我会说我熟悉单子和Haskell的基础知识,但对类别理论只有非常粗略的理解。)


爱德华Kmett的答案显然很棒。 但是,这有点技术性。 这可能是一个更容易理解的解释。

自由monads只是将函数转化为monad的一般方法。 也就是说,给定任意函数f Free f是一个monad。 这不会很有用,除非你有一对功能

liftFree :: Functor f => f a -> Free f a
foldFree :: Functor f => (f r -> r) -> Free f r -> r

其中第一个让你“进入”你的monad,第二个让你有一种“摆脱”它的方法。

更一般地说,如果X是一个带有一些额外东西P的Y,那么“自由X”是一种从Y到X获得额外东西的方式。

例子:幺半群(X)是一个具有额外结构(P)的集合(Y),基本上说它有一个操作(你可以想到加法)和一些身份(如零)。

所以

class Monoid m where
   mempty  :: m
   mappend :: m -> m -> m

现在,我们都知道名单

data [a] = [] | a : [a]

好吧,给定任何类型t我们知道[t]是一个monoid

instance Monoid [t] where
  mempty   = []
  mappend = (++)

所以列表是在集合(或Haskell类型)上的“自由幺半群”。

好吧,免费的monads是一样的想法。 我们拿一个函数,并且给一个monad。 事实上,因为monad可以被看作是endo函子类的monoids,所以定义一个列表

data [a] = [] | a : [a]

看起来很像自由单体的定义

data Free f a = Pure a | Roll (f (Free f a))

并且Monad实例与列表的Monoid实例有相似之处

--it needs to be a functor
instance Functor f => Functor (Free f) where
  fmap f (Pure a) = Pure (f a)
  fmap f (Roll x) = Roll (fmap (fmap f) x)

--this is the same thing as (++) basically
concatFree :: Functor f => Free f (Free f a) -> Free f a
concatFree (Pure x) = x
concatFree (Roll y) = Roll (fmap concatFree y)

instance Functor f => Monad (Free f) where
  return = Pure -- just like []
  x >>= f = concatFree (fmap f x)  --this is the standard concatMap definition of bind

现在,我们得到了两项业务

-- this is essentially the same as x -> [x]
liftFree :: Functor f => f a -> Free f a
liftFree x = Roll (fmap Pure x)

-- this is essentially the same as folding a list
foldFree :: Functor f => (f r -> r) -> Free f r -> r
foldFree _ (Pure a) = a
foldFree f (Roll x) = f (fmap (foldFree f) x)

这里有一个更简单的答案:Monad是当monadic上下文被join :: m (ma) -> ma (回忆>>=可以被定义为(join .) . flip fmap )时“计算”的东西。 这就是Monads如何通过顺序计算链来实现上下文:因为在该系列的每个点上,来自前一个调用的上下文与下一个调用崩溃。

一个免费的monad满足所有的Monad定律,但不会做任何崩溃(即计算)。 它只是建立一个嵌套的上下文系列。 创建这种免费单点值的用户负责对这些嵌套上下文进行操作,以便这种构图的含义可以推迟到创建单值之后。


免费的foo碰巧是满足所有'foo'法则的最简单的事情。 也就是说,它完全符合必要的法律,而不需要额外的东西。

一个健忘的函子是一个“忘记”结构的一部分,因为它从一个类到另一个类。

给定函数F : D -> CG : C -> D ,我们说F -| G F -| GFG ,或者GF于a,b中: F a -> ba -> G b同构,其中箭头来自适当的类别。

形式上,一个免费的函数是伴随着一个健忘的仿函数。

自由Monoid

让我们从一个更简单的例子开始,这个免费的monoid。

以一个由一些载体集合T定义的monoid,一个二元函数将一对元素混合在一起f :: T → T → T和一个unit :: T ,使得你有一个关联律和一个同一性定律: f(unit,x) = x = f(x,unit)

你可以做一个仿函数U从类群的分类(其中箭头是半群同态,也就是说,它们确保它们映射unit ,以unit的另一半群,并且您可以映射之前或之后撰写的另一半群不改变的意思)到“忘记”操作和unit的集合类别(箭头只是功能箭头),并且只给你载体集合。

然后,你可以定义一个仿函数F从类别集合返回到这个仿函数的左边。 也就是说算符是一套映射函子a到半群[a]其中unit = []mappend = (++)

所以要回顾一下我们的例子,在伪Haskell中:

U : Mon → Set -- is our forgetful functor
U (a,mappend,mempty) = a

F : Set → Mon -- is our free functor
F a = ([a],(++),[])

然后为了显示F是免费的,需要证明它是留给U一个健忘函数,也就是说,正如我们上面提到的那样,我们需要证明

F a → b同构于a → U b

现在,记住F的目标是monique类别Mon ,其中箭头是幺半同态,所以我们需要a来证明从[a] → b的幺半群同态可以用a → b的函数精确描述。

在Haskell中,我们称这个生存在Set (er, Hask ,我们假装为Haskell类型的类为Set)的foldMap ,只是foldMap ,当它从Data.Foldable到Lists时,其类型为Monoid m => (a → m) → [a] → m

由此产生的后果是作为一种附带。 值得注意的是,如果你忘记了然后建立免费,那么再次忘记,就像你忘了一次,我们可以用它来建立一元连接。 因为UFUFU(FUF) UF ,我们可以通过从身份半群同态[a][a]通过定义我们的红利同构,获取从列表同构[a] → [a]是类型a -> [a]函数,这只是返回列表。

您可以更直接地通过用以下术语描述一个列表来组合所有这些:

newtype List a = List (forall b. Monoid b => (a -> b) -> b)

Free Monad

那么什么是Free Monad?

那么,我们做同样的事情,我们以前做过,我们从单子类别中的一个健忘的仿函数U开始,其中箭头是单子同态到箭头是自然变换的内生函数的类别,并且我们寻找一个函数,它是伴随的对此。

那么,这与通常使用的免费monad的概念有何关系?

知道了什么是一个免费的单子, Free f ,告诉你,从给定的单子同态Free f -> m ,是同样的事情(同构)为给从自然转化(仿函数同态) f -> m 。 记住F a -> b必须与a -> U b同构,因为F与U相邻。这里将映射的单子映射到函子。

F至少与我在free软件包中使用的Free类型同构。

通过定义,我们也可以将它与上面的免费列表的代码更紧密地类比

class Algebra f x where
  phi :: f x -> x

newtype Free f a = Free (forall x. Algebra f x => (a -> x) -> x)

Cofree Comonads

我们可以通过看看存在的健忘函子的正确伴随来构建类似的东西。 一个cofree函子简单地/正确地伴随着一个健忘的函数,并且通过对称性,知道某事是一个cofree comonad就像知道给出一个来自w -> Cofree f的共同同态w -> Cofree f是一样的, w -> f

链接地址: http://www.djcxy.com/p/8365.html

上一篇: What are free monads?

下一篇: Is jQuery a monad