【问题标题】:What is the justification for the type of unfoldr in Haskell?Haskell 中展开器类型的理由是什么?
【发布时间】:2016-11-09 04:37:23
【问题描述】:

unfoldr :: (b -> Maybe (a, b)) -> b -> [a]的文档中给出的例子:

unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10

可以很容易地用冗余对编写:

unfoldr (\b -> if b == 1 then Nothing else Just (b-1, b-1)) 11

unfoldr 需要 (a,b) 对做什么?为什么它的类型不是(a -> Maybe a) -> a -> [a]

【问题讨论】:

  • @jwodder,当我在 ghci 中测试时,两个片段都会导致 [10,9,8,7,6,5,4,3,2,1]...

标签: haskell types unfold


【解决方案1】:

有类型的函数

(a -> Maybe a) -> a -> [a]

将输出列表元素类型限制为与通过生成过程线程化的状态相同。 unfoldr 更通用,因为它允许使用独立类型的状态。

【讨论】:

  • 我的问题是这种理论上更通用的类型是否有任何实际用途,如果是的话,如果你能举一个很好的例子。当然,正如我在问题中所展示的那样,标准示例并没有使更复杂的类型的用处变得明显。
  • @hkBst 将Just (b, b-1) 替换为Just (show b, b-1)
  • @hkBst 一个更有用的例子是:repeat n x = unfoldr (\b -> if b == 0 then Nothing else Just (x, b-1)) nrepeat 的类型是Int -> a -> [a],所以repeat 5 1 == [1,1,1,1,1] 也是repeat 5 'a' == "aaaaa"。使用您的unfoldr 版本,您无法将repeat 实现为对unfoldr 的简单调用。
【解决方案2】:

利用一些理论,可以恢复递归类型的折叠/展开类型,包括列表,了解它们为何如此。

A 为固定类型。 “As的列表”类型满足同构

List ~~ Either () (A, List)

可以读作“列表值是一个特殊值(空列表),或者是 A 类型的值,后跟一个列表值”。

用更简洁的表示法,我们可以将Either 写成中缀+

List ~~ () + (A, List)

现在,如果我们让F b = () + (A, b),我们就有了

List ~~ F List

上面是一个不动点方程,在使用递归类型时总是会出现。对于T ~~ F T 定义的任何递归类型,我们可以 导出相关折叠/展开的类型(也称为cata/ana 或归纳/共归纳原理)

fold   :: (F b -> b) -> T -> b
unfold :: (b -> F b) -> b -> T

在列表的情况下,我们得到

fold    :: ((() + (A, b)) -> b) -> List -> b
unfoldr :: (b -> (() + (A, b))) -> b -> List 

也可以重写展开,注意到Maybe c ~~ () + c

unfoldr :: (b -> Maybe (A, b)) -> b -> List 

折叠可以改用

((x + y) -> z) ~~ (x -> z, y -> z)

得到

foldr :: (() -> b, (A, b) -> b) -> List -> b

那么,由于() -> b ~~ b

foldr :: (b, (A, b) -> b) -> List -> b

最后,自从(x, y) -> z ~~ x -> y -> z(currying),我们有了

foldr :: b -> ((A, b) -> b) -> List -> b

又一次:

foldr :: b -> (A -> b -> b) -> List -> b

最后一次翻转x -> y -> z ~~ y -> x -> z:

foldr :: (A -> b -> b) -> b -> List -> b

这些类型如何遵循(共)归纳原则?

域理论指出,最少不动点 (F(T)=T) 与最少 前缀点 (F(T)<=T),当 F 在某个特定偏序上是单调的。

归纳原理简单说明,如果T是最小前缀点,F(U)<=U,那么T<=U。 (证明。这是最少!。QED。) 在公式中,

(F(U) <= U)  ==>  (T <= U)

为了处理类型上的固定点,我们需要从姿势转换到类别,这使得一切都变得更加复杂。非常非常粗略地,每个&lt;= 都被一些态射替换。例如,F(U)&lt;=U 现在意味着存在一些态射F U -&gt; U。 “单调F”意味着F是一个函子(因为a&lt;=b暗示F(a)&lt;=F(b)现在变成(a-&gt;b)暗示F a -&gt; F b)。前缀点是 F 代数 (F u -&gt; u)。 “最小”变成“初始”。以此类推。

因此折叠的类型:(暗示也是-&gt;

fold   :: (F u -> u) -> (T -> u)

Unfold 源自共归纳原理,它处理最大后缀点 T(变成余代数)

(U <= F(U)) ==> (U <= T)

【讨论】:

  • 当然很有趣,但我觉得你在介绍折叠和展开的初始类型的部分没有给予足够的重视。这些类型如何遵循(共)归纳原则?
  • @hkBst 是的。编辑以阐明一些观点。
猜你喜欢
  • 2020-03-10
  • 2012-01-15
  • 1970-01-01
  • 2020-06-12
  • 2018-04-20
  • 2013-12-31
  • 1970-01-01
  • 2020-09-14
  • 2015-08-22
相关资源
最近更新 更多