我正在阅读http://www.haskellforall.com/2013/06/from-zero-to-cooperative-threads-in-33.html,其中抽象语法树派生为表示一组指令的仿函数的自由monad。我注意到免费monad Free与仿函数Fix上的fixpoint运算符没有太大区别。
本文使用monad操作和do
语法以简洁的方式构建这些AST(fixpoints)。我想知道这是免费monad实例的唯一好处吗?它还有其他有趣的应用程序吗?
答案 0 :(得分:17)
(N.B。这与我和@ Gabriel上面的评论结合起来。)
Fix
的{{1}} ed点的每个居民都有可能是无限的,即Functor
是let x = (Fix (Id x)) in x === (Fix (Id (Fix (Id ...))))
的唯一居民}。 Fix Identity
立即与Free
不同,因为它确保至少有一个有限居民Fix
。事实上,如果Free f
有无限的居民,那么Fix f
就会拥有无限多的有限居民。
这种无限制的另一个直接副作用是Free f
不再是Functor f => Fix f
。我们需要实施Functor
,但fmap :: Functor f => (a -> b) -> (f a -> f b)
已填充Fix
中曾经包含f a
的所有漏洞,因此我们不再拥有a
a
1}}将我们的fmap
'd函数应用于。
这对于创建Monad
很重要,因为我们想要实施return :: a -> Free f a
并且比如说这个法律持有fmap f . return = return . f
,但它甚至没有意义Functor f => Fix f
。
那么Free
如何“修复”这些Fix
点的弱点?它使用Pure
构造函数“扩充”我们的基础仿函数。因此,对于所有Functor f
,Pure :: a -> Free f a
。这是我们保证有限的类型居民。它还立即为我们提供了return
的良好定义。
return = Pure
所以你可能会认为这个添加是由Functor
创建的嵌套Fix
的潜在无限“树”并混合在一些“活”芽中,由{{1}表示}。我们使用Pure
创建新芽,这可能被解释为稍后“返回”到该芽的承诺并添加更多计算。事实上,这正是return
所做的。给定可以应用于类型flip (>>=) :: (a -> Free f b) -> (Free f a -> Free f b)
的“延续”函数f :: a -> Free f b
,我们向下递归我们的树,返回到每个a
并将其替换为计算为Pure a
的连续。这让我们“成长”了我们的树。
现在,f a
显然比Free
更为通用。要开车回家,可以看到任何类型Fix
作为相应Functor f => Fix f
的子类型!只需选择我们Free f a
所在的a ~ Void
(即,无法构造的类型,是空类型,没有实例)。
为了更清楚,我们可以使用data Void = Void Void
打破Fix
'd Functor
,然后尝试使用break :: Fix f -> Free f a
将其反转。
affix :: Free f Void -> Fix f
首先请注意,break (Fix f) = Free (fmap break f)
affix (Free f) = Fix (fmap affix f)
不需要处理affix
案例,因为在这种情况下Pure x
因此 不能存在,所以{{1我是荒谬的,我们会忽略它。
另请注意,x :: Void
的返回类型有点微妙,因为Pure x
类型仅出现在返回类型break
中,因此a
的任何用户都无法访问它{1}}。 “完全无法访问”和“无法实例化”给我们第一个提示,尽管类型Free f a
和break
是反转的,但我们可以证明它。
affix
应该显示(共同归纳,或者只是直觉,或许)break
是一个身份。另一个方向完全相同。
所以,希望这表明所有(break . affix) (Free f)
=== [definition of affix]
break (Fix (fmap affix f))
=== [definition of break]
Free (fmap break (fmap affix f))
=== [definition of (.)]
Free ( (fmap break . fmap affix) f )
=== [functor coherence laws]
Free (fmap (break . affix) f)
(break . affix)
大于Free f
。
那么为什么要使用Fix f
?好吧,由于分层Functor f
的一些副作用,有时您只需要Fix
的属性。在这种情况下,调用它Free f Void
可以更清楚地表明我们不应该尝试f
或Fix f
类型。此外,由于(>>=)
只是fmap
,编译器可能更容易“编译”Fix
层,因为它只会起到语义作用。
newtype
和Fix
如何是同构类型,以便更清楚地了解Void
和forall a. a
的类型是如何和谐的。例如,我们affix
为break
,absurd :: Void -> a
为absurd (Void v) = absurd v
。但这些有点傻。答案 1 :(得分:7)
有一种深刻且“简单”的联系。
这是adjoint functor theorem的结果,左邻接保留了初始对象:L 0 ≅ 0
。
从分类上看,Free f
是一个类别到其F代数的仿函数(Free f
与另一个方向的健忘仿函数相邻)。在 Hask 中工作,我们的初始代数是Void
Free f Void ≅ 0
和F代数类别中的初始代数是Fix f
:Free f Void ≅ Fix f
import Data.Void
import Control.Monad.Free
free2fix :: Functor f => Free f Void -> Fix f
free2fix (Pure void) = absurd void
free2fix (Free body) = Fix (free2fix <$> body)
fixToFree :: Functor f => Fix f -> Free f Void
fixToFree (Fix body) = Free (fixToFree <$> body)
类似地,右邻接(Cofree f
,一个来自 Hask 的仿函数到F- co </ strong>代数的类别)保留最终对象:R 1 ≅ 1
在 Hask 中,这是单位:()
,F- co </ strong>代数的最终对象也是Fix f
(它们在 Hask )所以我们得到:Cofree f () ≅ Fix f
import Control.Comonad.Cofree
cofree2fix :: Functor f => Cofree f () -> Fix f
cofree2fix (() :< body) = Fix (cofree2fix <$> body)
fixToCofree :: Functor f => Fix f -> Cofree f ()
fixToCofree (Fix body) = () :< (fixToCofree <$> body)
看看定义有多相似!
newtype Fix f
= Fix (f (Fix f))
Fix f
是Free f
,没有变数。
data Free f a
= Pure a
| Free (f (Free f a))
Fix f
是Cofree f
,带有虚拟值。
data Cofree f a
= a <: f (Cofree f a)