Answers:
爱德华·克梅特的答案显然是非常好的。但是,这有点技术性。这是一个更容易理解的解释。
免费的monad只是将函子变成monad的一种通用方法。也就是说,给定任何函子f
Free f
都是单子。这不是很有用,除非您有一对功能
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而不获得任何额外收益的一种方式。
例子:一个monoid(X)是一个具有额外结构(P)的集合(Y),基本上说它具有一个运算(可以想到加法)和某些标识(例如零)。
所以
class Monoid m where
mempty :: m
mappend :: m -> m -> m
现在,我们都知道列表
data [a] = [] | a : [a]
好吧,给定任何类型,t
我们都知道这[t]
是一个半定态
instance Monoid [t] where
mempty = []
mappend = (++)
因此列表是集合(或Haskell类型)中的“自由monoid”。
好的,所以免费的monad是相同的想法。我们拿一个函子,还给一个单子。实际上,由于monad可以看作endofunctors类别中的monoid,因此列表的定义
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)
Free f a = Pure a | Roll (f (Free f a))
as视为有趣Free f a = a + fa + ffa + ...
,即“ f适用于任何次数”。然后concatFree
(即join
)进行“将f应用于任何次数(f对a进行任意次数)”,并将两个嵌套的应用折叠为一个。并>>=
采用“ f对a进行任意次数的应用”和“如何从a到(f对b进行任意次数的应用)得出”,并且基本上将后者应用于前者内部的a并折叠嵌套。现在我自己明白了!
concatFree
基本join
?
这里有一个甚至更简单的答案:Monad是通过折叠Monadic上下文join :: m (m a) -> m a
(>>=
可以将其定义为x >>= y = join (fmap y x)
)来“计算”的东西。这就是Monad通过顺序计算链传递上下文的方式:因为在系列的每个点上,前一个调用的上下文都会与下一个调用折叠。
一个免费的monad满足所有Monad法则,但不做任何折叠(即计算)。它只是建立了一系列嵌套的上下文。创建这样的自由monadic值的用户负责对这些嵌套上下文进行操作,以便可以将这种组合的含义推迟到monadic值创建之后。
一个免费的foo恰好是满足所有“ foo”法则的最简单的东西。也就是说,它完全满足成为foo所必需的法律,而没有其他要求。
一个健忘的仿函数是一个将结构的一部分从一个类别转移到另一个类别时“忘记”它的部分。
鉴于函子F : D -> C
,和G : C -> D
我们说F -| G
,F
留给伴随到G
,或者G
是对伴随以F
每当FORALL A,B:F a -> b
是同构的a -> G b
,其中的箭头来自适当的类别。
正式地,一个自由函子与健忘函子相连。
免费的半身像
让我们从一个简单的示例开始,即自由monoid。
以一个monoid(由某个载波集定义)T
,一个将一对元素混在一起的二进制函数f :: T → T → T
和一个unit :: T
(使您具有关联律和一个身分律)来定义f(unit,x) = x = f(x,unit)
。
您可以U
从monoid类别(其中箭头是monoid同态,即确保它们映射unit
到unit
另一个monoid上,并且您可以在映射到另一个monoid之前或之后撰写而不更改含义)中构成一个函子。的集合(其中箭头只是功能箭头)“忘记”了操作和unit
,只为您提供了载体集。
然后,您可以F
从集合的类别定义到仿函数的类别,该类别与该仿函数保持相邻关系,从而定义一个仿函数。该函子是将集合映射a
到monoid 的函子[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
是在Mon
monoid 类别中,其中箭头是monoid同态,因此我们需要a来证明from的monoid同态[a] → b
可以用from的函数精确描述a → b
。
在Haskell中,我们称其存在的那一侧Set
(Hask
即,我们假装的Haskell类型的类别为Set),just foldMap
,当从from Data.Foldable
到Lists 专门化时,其类型为type Monoid m => (a → m) → [a] → m
。
这是附属的后果。值得注意的是,如果您忘记了然后免费建立起来,然后又忘记了,就像您忘记一次一样,我们可以使用它来建立单子连接。由于UFUF
〜U(FUF)
〜UF
,我们可以在身份幺同态,从传递[a]
到[a]
通过定义我们的红利同构,获取从列表同构[a] → [a]
是类型的函数a -> [a]
,而这仅仅是返回列表。
您可以通过使用以下术语描述列表来更直接地构成所有这些内容:
newtype List a = List (forall b. Monoid b => (a -> b) -> b)
免费的单子
那么什么是免费的Monad?
好吧,我们做着与以前相同的事情,我们从健忘的函子U开始,从箭头是单子同态的monads类别到箭头是自然变换的endofunctors类别,然后寻找与之相邻的函子对此。
那么,这与通常使用的免费monad的概念有何关系?
知道某物是自由的monad,就Free f
告诉您,从给出一个monad同Free f -> m
构与从给出一个自然变换(函子同构)是同一件事(同构)f -> m
。请记住,F a -> b
必须a -> U b
使F与U 保持同构。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函子与健忘的functor只是简单地/正确地伴随/对称,因此,知道某物是cofree共模子,就等于知道从中给出comonad同构性与从中w -> Cofree f
给出自然变换一样w -> f
。
Free Monad(数据结构)对Monad(类)的作用类似于List(数据结构)对Monoid(类)的作用:这是一个微不足道的实现,您可以在其中决定以后如何组合内容。
您可能知道什么是Monad,并且每个Monad需要fmap
+ join
+ return
或bind
+ 的特定(遵守Monad-law的)实现return
。
让我们假设您有一个Functor(的实现fmap
),但其余部分取决于运行时的值和选择,这意味着您希望能够使用Monad属性,但之后要选择Monad函数。
这可以使用Free Monad(数据结构)来完成,该结构以某种方式包装Functor(类型),从而使join
a是那些函子的叠加而不是归约。
现在可以将实数return
和join
要使用的实数作为归约函数的参数foldFree
:
foldFree :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
foldFree return join :: Monad m => Free m a -> m a
为了解释类型,我们可以Functor f
用Monad m
和替换b
为(m a)
:
foldFree :: Monad m => (a -> (m a)) -> (m (m a) -> (m a)) -> Free m a -> (m a)
Haskell免费monad是函子的列表。相比:
data List a = Nil | Cons a (List a )
data Free f r = Pure r | Free (f (Free f r))
Pure
类似于Nil
和Free
类似Cons
。一个免费的monad存储一个函子列表而不是一个值列表。从技术上讲,您可以使用其他数据类型实现免费的monad,但是任何实现都应与上述方法同构。
每当需要抽象语法树时,就可以使用免费的monad。自由monad的基本函子是语法树的每个步骤的形状。
我的帖子(已有人链接)提供了一些示例,说明如何使用免费的monad构建抽象语法树
我认为一个简单的具体例子会有所帮助。假设我们有一个函子
data F a = One a | Two a a | Two' a a | Three Int a a a
与显而易见的fmap
。然后Free F a
是树木,叶子有型的类型a
,其节点标记One
,Two
,Two'
和Three
。 One
-节点有一个子节点,Two
-和- Two'
节点有两个子Three
节点,- 节点有三个子节点,并带有标记Int
。
Free F
是单子。 return
映射x
到只有值的叶子的树x
。 t >>= f
看着每片叶子,然后用树代替它们。当叶子具有价值时,y
它将用树替换该叶子f y
。
图表使这一点更加清楚,但是我没有轻松绘制一个的功能!