什么是免费单子?
我已经看到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 -> C
, G : C -> D
,我们说F -| G
F -| G
, F
与G
,或者G
与F
于a,b中: F a -> b
与a -> 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
。
由此产生的后果是作为一种附带。 值得注意的是,如果你忘记了然后建立免费,那么再次忘记,就像你忘了一次,我们可以用它来建立一元连接。 因为UFUF
〜 U(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
。
下一篇: Is jQuery a monad