自由Monad的推导

时间:2013-04-13 14:53:53

标签: haskell monads

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?

3 个答案:

答案 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的第二种情况。对于gofmap本身只是Free f a。对于<$>fmapf。我们所做的是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)

好吧,由于fFunctor,我们实际上可以使用mapFreemapFree 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.