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)
Run Code Online (Sandbox Code Playgroud)
我在理解第二go
行时遇到了很多麻烦,特别是在描述自由monad是什么的情况下.有些人可以描述一下这是如何工作的以及为什么它会成为Free f a
一个免费的monad?
Tik*_*vis 14
在这一点上,你只是制作Free
一个仿函数而不是一个monad.当然,要成为一个monad,它也必须是一个仿函数!
我认为如果我们重命名Free
构造函数以避免混淆会更容易思考:
data Free f a = Pure a | Wrap (f (Free f a))
Run Code Online (Sandbox Code Playgroud)
现在让我们来看看我们正在构建的结构.对于这种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
在go fmap
本身时 - 重要的区别在于第一层fmap
适用于单层f
并且go
递归地适用于整个 Free f a
结构.
我希望这可以解决一些问题.
要告诉你实话,我通常只是觉得它更容易不阅读代码在这些简单的功能,而是阅读类型,然后写函数自己.把它想象成一个谜题.你正试图构建这个:
mapFree :: Functor f => (a -> b) -> Free f a -> Free f b
Run Code Online (Sandbox Code Playgroud)
那我们该怎么做呢?好吧,我们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
Run Code Online (Sandbox Code Playgroud)
有了这两个类型的注释,并且知道了类型Pure
,你应该立即看到解决方案:
mapFree f (Pure a) = Pure (f a)
Run Code Online (Sandbox Code Playgroud)
现在第二种情况:
mapFree f (Free fa) = ...
-- f :: a -> b
-- fa :: Functor f => f (Free f a)
Run Code Online (Sandbox Code Playgroud)
好吧,因为f
是Functor
,我们实际上可以mapFree
用来应用于mapFree f
内部组件f (Free f a)
.所以我们得到:
mapFree f (Free fa) = Free (fmap (mapFree f) fa)
Run Code Online (Sandbox Code Playgroud)
现在,使用此定义作为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)
Run Code Online (Sandbox Code Playgroud)
通过一些工作,您可以验证我们刚刚到达的定义与您困惑的定义相同.(正如其他人所提到的,(<$>)
(定义于Control.Applicative
)只是它的同义词fmap
.)你可能仍然无法理解它,但是你设法写了它,对于类型这样抽象,因为这些通常是足够好的.
至于理解它,有助于我的事情如下:把Free
monad 想象成一种类似列表的结构,Pure
as as []
和Free
as (:)
.从类型的定义中你应该看到:Pure
是基本情况,并且Free
是递归的情况.什么fmap
情况下做的是"推"的映射功能,这种结构的底部,到了那里的Pure
生活.