我偶尔会看到Free Monad这个词出现,但每个人似乎都在使用/讨论它们,却没有解释它们是什么。那么:什么是免费的单子?(我想说我熟悉单子和Haskell的基础知识,但只有一个非常粗略的范畴理论知识。)
当前回答
爱德华·克米特的回答显然很棒。但是,这有点技术性。这里有一个可能更容易理解的解释。
自由单子只是将函子转换为单子的一种通用方法。也就是说,给定任意函子,自由f是一个单子。这不是很有用,除非你得到一对函数
liftFree :: Functor f => f a -> Free f a
foldFree :: Functor f => (f r -> r) -> Free f r -> r
第一个选项让你“进入”你的单子,第二个选项让你“退出”单子。
更一般地说,如果X是一个Y,加上一些额外的P,那么“自由X”是一种从Y到X而不获得任何额外东西的方法。
例如:一个单类(X)是一个带有额外结构(P)的集合(Y),它基本上说它有一个操作(你可以想到加法)和一些恒等式(比如零)。
So
class Monoid m where
mempty :: m
mappend :: m -> m -> m
现在,我们都知道列表
data [a] = [] | a : [a]
对于任意类型的t,我们知道[t]是一个一元曲面
instance Monoid [t] where
mempty = []
mappend = (++)
因此列表是集合(或Haskell类型)之上的“自由单类”。
免费单子也是一样的。我们取一个函子,然后返回一个单子。事实上,由于单子可以被视为内函子范畴内的单类,一个列表的定义
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)
其他回答
我想举个简单具体的例子会有帮助。假设我们有一个函子
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 -nodes有一个子节点,Two-和Two'-nodes有两个子节点,three -nodes有三个子节点,也被标记为Int。
Free F是一个单子。Return将x映射到树,它是一个值为x的叶子。t >>= f查看每个叶子并将它们替换为树。当叶结点的值为y时,它就会用树f y替换叶结点。
一个图表使这更清楚,但我没有工具来轻松地画一个!
这里有一个更简单的答案:一个Monad是一些“计算”时,Monad上下文被join:: m (m A) -> m A(回忆>>=可以定义为x >>= y = join (fmap y x))。这就是Monads如何通过一个连续的计算链携带上下文:因为在系列中的每个点上,来自前一个调用的上下文与下一个调用一起折叠。
一个自由的单子满足所有的单子定律,但不做任何折叠(即计算)。它只是建立了一系列嵌套的上下文。创建这样一个自由单体值的用户负责对这些嵌套上下文做一些事情,这样组合的含义可以延迟到单体值创建之后。
Free Monad(数据结构)之于Monad(类),就像List(数据结构)之于Monoid(类):这是一个简单的实现,在那里你可以决定如何组合内容。
你可能知道什么是Monad,每个Monad都需要fmap + join + return或bind + return的特定实现(遵循Monad法)。
让我们假设你有一个Functor (fmap的实现),但其余的取决于值和在运行时所做的选择,这意味着你想要能够使用Monad属性,但想要选择Monad功能之后。
这可以使用Free Monad(数据结构)来完成,它以这样一种方式包装Functor(类型),以便连接是这些Functor的堆叠而不是约简。
你想要使用的实际返回和连接,现在可以作为约约函数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 free monad是一个函子列表。比较:
data List a = Nil | Cons a (List a )
data Free f r = Pure r | Free (f (Free f r))
Pure类似于Nil, Free类似于Cons。Free单子存储的是一个函子列表,而不是一个值列表。从技术上讲,你可以使用不同的数据类型来实现免费的单子,但是任何实现都应该与上面的同构。
只要需要抽象语法树,就可以使用免费的单子。自由单子的基函子是语法树中每一步的形状。
我的文章(已经有人链接了)给出了几个如何用免费单子构建抽象语法树的例子
爱德华·克米特的回答显然很棒。但是,这有点技术性。这里有一个可能更容易理解的解释。
自由单子只是将函子转换为单子的一种通用方法。也就是说,给定任意函子,自由f是一个单子。这不是很有用,除非你得到一对函数
liftFree :: Functor f => f a -> Free f a
foldFree :: Functor f => (f r -> r) -> Free f r -> r
第一个选项让你“进入”你的单子,第二个选项让你“退出”单子。
更一般地说,如果X是一个Y,加上一些额外的P,那么“自由X”是一种从Y到X而不获得任何额外东西的方法。
例如:一个单类(X)是一个带有额外结构(P)的集合(Y),它基本上说它有一个操作(你可以想到加法)和一些恒等式(比如零)。
So
class Monoid m where
mempty :: m
mappend :: m -> m -> m
现在,我们都知道列表
data [a] = [] | a : [a]
对于任意类型的t,我们知道[t]是一个一元曲面
instance Monoid [t] where
mempty = []
mappend = (++)
因此列表是集合(或Haskell类型)之上的“自由单类”。
免费单子也是一样的。我们取一个函子,然后返回一个单子。事实上,由于单子可以被视为内函子范畴内的单类,一个列表的定义
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)
推荐文章
- 阅读GHC核心
- 在c#中,什么是单子?
- 常见的Haskell操作符是否有可发音的名称?
- 什么时候- xallowambiguousttypes是合适的?
- 在Android操作系统上运行Haskell程序
- Haskell中“数据”和“新类型”之间的差异
- Haskell对Node.js的响应是什么?
- Not a Functor/Functor/Applicative/Monad?
- 比较Haskell的Snap和Yesod的web框架
- Haskell:将Int转换为字符串
- 获取与模板Haskell关联的类型同义词
- Haskell:列表,数组,向量,序列
- 推挽型FRP和箭头型FRP有什么本质区别?
- Template Haskell有什么不好的?
- 感叹号在Haskell声明中是什么意思?