Control.Monad.Free
实现了一个免费的monad:
data Free f a = Pure a | Free (f (Free f a))
instance Functor f => Functor (Free f) where
fmap f = go where
go (Pure a) = Pure (f a)
go (Free fa) = Free (go <$> fa)
我在理解第二条go
行时遇到了很多麻烦,特别是在what a free monad is的描述中。有人可以描述一下这是如何工作的以及它为什么让Free f a
成为一个免费的monad?
答案 0 :(得分:14)
此时,你只是让Free
成为一个仿函数而不是一个monad。当然,要成为一个monad,它也必须是一个仿函数!
我认为如果我们重命名Free
构造函数以避免混淆,会更容易思考:
data Free f a = Pure a | Wrap (f (Free f a))
现在让我们来看看我们正在构建的结构。对于Pure
案例,我们只有a
类型的值。对于Wrap
案例,我们在Free f a
仿函数中包含另一个 f
值。
让我们忽略构造函数一秒钟。也就是说,如果我们Wrap (f (Pure a))
让我们将其视为f a
。这意味着我们构建的结构只是f
- 一个仿函数 - 反复应用了很多次。此类型的值类似于:f (f (f (f (f a))))
。为了使其更具体,请f
为[]
获取:[[[[[a]]]]]
。我们可以通过重复使用Wrap
构造函数来获得我们想要的多个级别;当我们使用Pure
时,一切都会结束。
重新构建构造函数,[[a]]
看起来像:Wrap [Wrap [Pure a]]
。
所以我们所做的就是取Pure
值并重复应用仿函数。
鉴于重复应用仿函数的这种结构,我们如何在其上映射函数?对于Pure
案例 - 在我们将其包装在f
之前 - 这非常简单:我们只应用该函数。但是如果我们已经将f
中的值至少包裹了一次,我们必须映射外层,然后递归映射所有内层。换句话说,我们必须将Free
monad上的映射映射到仿函数f
上。
这正是go
的第二种情况。对于go
,fmap
本身只是Free f a
。对于<$>
,fmap
为f
。我们所做的是fmap
go
超过f
,这使整个事情递归。
由于此映射函数是递归的,因此它可以处理任意数量的级别。因此,我们可以通过[[a]]
或[[[[a]]]]
或其他方式映射函数。这就是为什么我们需要fmap go
当{是fmap
本身时 - 重要的区别是第一个fmap
适用于{{1> 层{{1} }和f
递归地用于整个 go
构造。
我希望这有点清楚。
答案 1 :(得分:7)
说实话,我通常只是发现不更容易阅读这些更简单的函数中的代码,而是阅读 types 然后编写函数我。把它想象成一个谜题。你正试图构建这个:
mapFree :: Functor f => (a -> b) -> Free f a -> Free f b
那我们该怎么做呢?好吧,我们先来看Pure
构造函数:
mapFree f (Pure a) = ...
-- I like to write comments like these while using Haskell, then usually delete
-- them by the end:
--
-- f :: a -> b
-- a :: a
在那里有两个类型注释,并且知道Pure
的类型,您应该立即看到解决方案:
mapFree f (Pure a) = Pure (f a)
现在是第二种情况:
mapFree f (Free fa) = ...
-- f :: a -> b
-- fa :: Functor f => f (Free f a)
好吧,由于f
是Functor
,我们实际上可以使用mapFree
将mapFree f
应用于f (Free f a)
的内部组件。所以我们得到:
mapFree f (Free fa) = Free (fmap (mapFree f) fa)
现在,使用此定义作为Functor f => Functor (Free f)
实例,我们得到:
instance Functor f => Functor (Free f) where
fmap f (Pure a) = Pure (f a)
fmap f (Free fa) = Free (fmap (fmap f) fa)
通过一些工作,您可以验证我们刚刚到达的定义与您所困惑的定义相同。 (正如其他人所提到的,(<$>)
(在Control.Applicative
中定义)只是fmap
的同义词。)您可能仍然无法理解它,但您设法编写它,对于类型为抽象,因为这些往往足够好。
至于理解它,帮助我的事情如下:将Free
monad视为一种类似列表的结构,Pure
为[]
并且Free
为(:)
。从类型的定义中你应该看到:Pure
是基本情况,Free
是递归情况。 fmap
实例正在做的是将映射函数“推”到此结构的底部,即Pure
所在的位置。
答案 2 :(得分:1)
由于我自己很困惑,我回答一个问题......这可能是一个正确的替代(依靠Tikhon的Wrap澄清)吗?
...
fmap g = go where
go (Pure a) = Pure (g a)
go (Wrap fa) = Wrap (go <$> fa)
Substituting "fmap g" for "go", and "fmap" for "<$>" (since "<$>" is infix,
we flip "go" and "<$>"):
fmap g (Pure a) = Pure (g a)
fmap g (Wrap fa) = Wrap (fmap (fmap g) fa)
Substituting "f (Free f a)" for "fa" in the last line (from the first data
declaration):
fmap g (Wrap fa) = Wrap ( fmap (fmap g) (f (Free f a)) )
= Wrap ( f ( fmap g (Free f a) ) )
= wrap ( f (Pure (g a) ) ) --if Free f a is Pure
or
= Wrap ( f ( fmap g (Wrap fa') ) ) --if Free f a is Wrap
The last line includes the recursion "fmap g (Wrap fa')", which would continue
unless Pure is encountered.