Ed Kmett的递归方案包中Fix,Mu和Nu有什么区别?

31
在 Ed Kmett 的 recursion-scheme 包中,有三个声明:
newtype Fix f = Fix (f (Fix f))

newtype Mu f = Mu (forall a. (f a -> a) -> a)

data Nu f where 
  Nu :: (a -> f a) -> a -> Nu f

这三种数据类型有什么区别?


2
我不太了解理论,但我了解对于更加证明性的语言,“Mu”是最小不动点,“Nu”是最大不动点。在Haskell中,这三个都应该是等价的(我相信)。请注意,对于“Mu”,实现“cata”非常容易,而对于“Nu”,实现“ana”也非常容易。 - dfeuer
4
尝试解决这个 kata https://www.codewars.com/kata/folding-through-a-fixed-point/haskell - xgrommx
1个回答

35

Mu 以其折叠形式表示递归类型,Nu 以其展开形式表示递归类型。在 Haskell 中,它们是同构的,并且是表示相同类型的不同方式。如果假装 Haskell 没有任意递归,这些类型之间的区别就变得更加有趣: Mu ff 的最小(初始)不动点,而 Nu f 是其最大(终极)不动点。

f 的不动点是一种类型 T,其中包含 Tf T 之间的同构,即反函数对 in :: f T -> Tout :: T -> f T。类型 Fix 只是使用 Haskell 的内置类型递归来直接声明同构。但你可以为 MuNu 实现 in/out。

举个具体的例子,假设您暂时无法编写递归值。则 Mu Maybe 的成员,即值 :: forall r. (Maybe r -> r) -> r,是自然数 {0, 1, 2, ...};Nu Maybe 的成员,即值 :: exists x. (x, x -> Maybe x),是共自然数 {0,1,2,...,∞}。思考这些类型的可能值,看看为什么 Nu Maybe 有一个额外的成员。

如果您想对这些类型有一些直觉,可以尝试在没有递归的情况下实现以下内容(按难度递增的粗略顺序):

  • zeroMu :: Mu MaybesuccMu :: Mu Maybe -> Mu Maybe
  • zeroNu :: Nu Maybe:零型“Nu”是一个包含的Maybe类型的空列表。
  • succNu :: Nu Maybe -> Nu Maybe:将给定的“Nu Maybe”类型中的元素添加到尾部并返回新的“Nu Maybe”。
  • inftyNu :: Nu Maybe:无限制的“Nu Maybe”类型,其中包含无限个空值“Maybe”。
  • muTofix :: Mu f -> Fix f:将极小不动点“Mu f”转换为具有相同类型参数的不动点“Fix f”。
  • fixToNu :: Fix f -> Nu f:将不动点“Fix f”转换为最大不动点“Nu f”。
  • inMu :: f (Mu f) -> Mu f:将类型参数为“Mu f”的函数应用于类型为“f(Mu f)”的值,并返回一个新的“Mu f”类型的值。
  • outMu :: Mu f -> f (Mu f):从类型“Mu f”中提取出类型参数为“f”的函数,并返回一个新的类型为“f(Mu f)”的值。
  • inNu :: f (Nu f) -> Nu f:将类型参数为“Nu f”的函数应用于类型为“f(Nu f)”的值,并返回一个新的“Nu f”类型的值。
  • outNu :: Nu f -> f (Nu f):从类型“Nu f”中提取出类型参数为“f”的函数,并返回一个新的类型为“f(Nu f)”的值。
  • nuToFix :: Nu f -> Fix f:将最大不动点“Nu f”转换为具有相同类型参数的不动点“Fix f”。
  • fixToMu :: Fix f -> Mu f:将不动点“Fix f”转换为最小不动点“Mu f”。
  • 注意,因为“Mu f”是极小不动点,而“Nu f”是最大不动点,所以编写函数“:: Mu f -> Nu f”非常容易,但编写函数“:: Nu f -> Mu f”却很困难;这就像逆水行舟一样。

    这里可能需要更详细的解释,但超出了此格式的范围。


    3
    非常清晰的解释,谢谢!是否有更详细说明它的来源(文章/论文/书籍)?例如,术语级别的不动点的类比和为什么Nu(将递归类型表示为其折叠形式)是类型级别上的最小不动点。LFP与初始代数之间以及GFP与终结余代数之间也存在重要联系。 - Sergey Cherepanov
    1
    我肯定我漏掉了什么,但是 Nu Maybe 似乎有更多的居民 - 至少有许多 Haskell 愉快地进行类型检查。例如,Nu Just []Nu Just "abcd"Nu (const Nothing) 42 都似乎被正确地类型化了。我错在哪里了? - B. Mehta
    1
    等一下。自然数?那是真的吗? - paulotorrens
    2
    Sergey Cherepanov:我不知道有没有,但是这里有一个使用参数性证明初始性的证明。初始代数和初始不动点之间的联系有时被称为Lambek引理。 B. Mehta:就像id :: forall a. a -> a不能区分不同的输入一样,外部世界也不能区分Nu Just []和Nu Just 0。 paulotorrens:至少对于这种类型来说,这是一个惯用的名称,即自然数的一点紧致化,因为这个原因。 - shachaf
    1
    关于初始性的证明,请参考以下更新后的链接:https://fplab.bitbucket.io/posts/2008-01-26-parametricity.html。 - shachaf
    4
    谢谢您提供这个有趣的练习!如果有人感兴趣,我在这里上传了我的答案:https://gist.github.com/inamiy/7488380da6001cb0f778b59d9f7230cd - inamiy

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接