什么是免费单子?
我已经看到“ Free Monad”这个术语偶尔会出现一段时间,但是似乎每个人似乎都会使用/讨论它们,而没有给出它们的解释。 那么,什么是免费的单子呢? (我会说我熟悉单子和Haskell的基础知识,但对类别理论只有非常粗略的了解。)
爱德华·科梅特的答案显然很棒。 但是,这有点技术性。 这是一个可能更容易理解的解释。
自由单体只是将函数转化为单子的一般方法。 也就是说,给定任何函数f
Free f
是一个monad。 除非你有一对function,否则这不会很有用
liftFree :: Functor f => fa -> Free fa foldFree :: Functor f => (fr -> r) -> Free fr -> 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]
好吧,给定任何types我们知道[t]
是一个monoid
instance Monoid [t] where mempty = [] mappend = (++)
所以列表是“自由幺半群”的集合(或Haskelltypes)。
好吧,免费的monads是一样的想法。 我们采取一个函数,并给一个monad。 事实上,monad可以被看作是单一类的内在函子,就是列表的定义
data [a] = [] | a : [a]
看起来很像自由monads的定义
data Free fa = Pure a | Roll (f (Free fa))
而Monad实例与列表的Monoid实例有相似之处
--it needs to be a functor instance Functor f => Functor (Free f) where fmap f (Pure a) = Pure (fa) fmap f (Roll x) = Roll (fmap (fmap f) x) --this is the same thing as (++) basically concatFree :: Functor f => Free f (Free fa) -> Free fa 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 fx) --this is the standard concatMap definition of bind
现在,我们得到了我们的两个操作
-- this is essentially the same as \x -> [x] liftFree :: Functor f => fa -> Free fa liftFree x = Roll (fmap Pure x) -- this is essentially the same as folding a list foldFree :: Functor f => (fr -> r) -> Free fr -> r foldFree _ (Pure a) = a foldFree f (Roll x) = f (fmap (foldFree f) x)
这里有一个更简单的答案:Monad是当单元上下文被join :: m (ma) -> ma
(回忆>>=
可以被定义为(join .) . flip fmap
)时“计算”的东西。 这就是Monad如何通过顺序的计算链来进行上下文的操作:因为在系列中的每一个点上,来自前一个调用的上下文都与下一个调用崩溃了。
一个免费的monad满足所有的Monad定律,但是不会做任何崩溃(即计算)。 它只是build立一个嵌套的上下文系列。 创build这种自由一元值的用户负责对这些嵌套的上下文进行操作,以便这种构成的含义可以推迟到一元值创build之后。
免费的foo恰好是满足所有'foo'定律的最简单的东西。 也就是说,它完全符合必要的法律,而不是任何额外的东西。
一个健忘的函数是一个“忘记”结构的一部分,因为它从一个类别到另一个。
给定函子F : D -> C
, G : C -> D
,我们说F -| G
F -| G
, F
是与G
相邻的,或者G
是与F
只要a,b: F a -> b
同构于a -> G b
,其中箭头来自适当的类别。
forms上,一个免费的函数是一个健忘的函数。
自由Monoid
让我们从一个更简单的例子开始,这个自由的monoid。
以一个载体组T
定义的monoid为一个二元函数,将一对元素混合在一起f :: T → T → T
和一个unit :: T
,这样就可以得到一个关联律和一个同一性定律: f(unit,x) = x = f(x,unit)
。
你可以从monoids类别(其中箭头是幺半群同态,也就是说,他们确保他们在另一个monoid上映射unit
到unit
,并且可以在映射到另一个monoid之前或之后构成一个函子U
而不改变意义)到“忘记”操作和unit
的集合(箭头只是function箭头)的类别,并且只给你载体集合。
然后,你可以定义一个仿函数F
从这个类的集合返回到这个仿函数的左边。 该函子是将一个集映射到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
是自由的,需要certificate它是留给U
一个健忘的函子,就像我们上面提到的那样,我们需要certificate
F a → b
同构于a → U b
现在,记住F
的目标是monique类Mon
,其中箭头是幺半群同态,所以我们需要一个certificate从[a] → b
的幺半群同态可以由a → b
的函数精确描述。
在Haskell中,我们称这个生存在Set
(er, Hask
,我们假设的Haskelltypes为Set)的foldMap
,只是当从Data.Foldable
到Lists专门化的types为Monoid m => (a → m) → [a] → m
。
由此产生的后果是后果。 值得注意的是,如果你忘记了那么免费build立,那么再次忘记它,就像你忘记了一次,我们可以用它来build立一元连接。 由于UFUF
U(FUF)
,我们可以通过定义我们的连接的同构从[a]
到[a]
传递同态幺半群同态,从[a] → [a]
得到列表同构是函数typesa -> [a]
,这只是返回列表。
您可以通过以下这些术语描述一个列表来更直接地构成所有这些:
newtype List a = List (forall b. Monoid b => (a -> b) -> b)
免费的Monad
那么什么是免费单子 ?
那么,我们做同样的事情,我们做了以前,我们从一个健忘的仿函数U开始,箭头是monad同态到箭头是自然变换的endofunctors的类别,我们寻找一个函数是左伴随的到那个。
那么,这与通常使用的免费monad的概念有什么关系呢?
知道某物是一个自由的单子, Free f
告诉你,给Free f -> m
的单子同态是同样的事情(同构),从f -> m
给自然变换(仿函数同态)。 记住F a -> b
必须同构于a -> U b
因为F要留在U. U这里映射到函子的单子。
F至less与我在free
软件包中使用的Free
types同构。
通过定义,我们也可以将它与上面的自由列表的代码更紧密地类比
class Algebra fx where phi :: fx -> x newtype Free fa = Free (forall x. Algebra fx => (a -> x) -> x)
Cofree Comonads
我们可以构造一个类似的东西,只要看看存在的健忘函数的正确伴随。 一个cofree函子简单地/正确地伴随着一个健忘的函数,而且通过对称性,知道某个东西是一个cofree的comonad就像知道给出一个从w -> Cofree f
cofree的共同同态w -> Cofree f
是一样的, w -> f
。
Free Monad(数据结构)是Monad(类),就像List(数据结构)到Monoid(类):这是一个简单的实现,在这里你可以决定如何组合内容。
你可能知道Monad是什么,每个Monad需要一个特定的(Monad-law持久)实现fmap
+ join
+ return
或者bind
+ return
。
让我们假设你有一个Functor(一个fmap
的实现),但其余的依赖于在运行时所做的值和select,这意味着你希望能够使用Monad属性,但希望之后selectMonad函数。
这可以通过使用Free Monad(数据结构)来完成,它以这种方式包装了Functor(type),这样join
就相当于这些函子的堆叠,而不是减less。
真正的return
和join
要使用,现在可以作为参数给予foldFree
函数foldFree
:
foldFree :: Functor f => (a -> b) -> (fb -> b) -> Free fa -> b foldFree return join :: Monad m => Free ma -> ma
为了解释types,我们可以用Monad m
和b
replaceFunctor f
(ma)
:
foldFree :: Monad m => (a -> (ma)) -> (m (ma) -> (ma)) -> Free ma -> (ma)
Haskell免费monad是一个函数列表。 比较:
data List a = Nil | Cons a (List a ) data Free fr = Pure r | Free (f (Free fr))
Pure
类似于Nil
, Free
类似于Cons
。 一个免费的monad存储一个函数列表,而不是一个值列表。 从技术上讲,你可以使用不同的数据types来实现自由monad,但是任何实现都应该与上面的同构。
每当你需要一个抽象的语法树时,你可以使用免费的monads。 自由单体的基函数是语法树每一步的形状。
我的post ,有人已经链接,给出了几个例子,如何build立与自由monads抽象语法树
我认为一个简单的具体例子会有所帮助。 假设我们有一个函子
data F a = One a | Two aa | Two' aa | Three Int aaa
与明显的fmap
。 那么Free F a
是叶子types为a
,节点标记为One
, Two
, Two'
和Three
的树种。 One
节点有一个孩子, Two
和Two'
节点有两个孩子, Three
节点有三个孩子,也有一个Int
标记。
Free F
是一个monad。 return
地图x
return
到只有值为x
的叶子的树。 t >>= f
看着每一片树叶,并用树代替它们。 当叶子有价值时,它用叶子代替叶子。
一个图更清晰,但我没有容易绘制的设施!