很明显为什么想要懒惰的函数式编程语言需要纯粹。我正在考虑相反的问题:如果一种语言想要纯洁,那么懒惰是否有很大的优势?哈斯克尔的一位设计师提出的一个论点就是它消除了诱惑;也许,但我正试图权衡更具体的优势。
鉴于您想要进行函数式编程,内置惰性可以让您更清楚,简单或简洁地表达事物的用例是什么?
简单地说:为什么懒惰如此重要以至于你想把它变成语言?
(我正在寻找更多面向应用程序而不是演示的用例 - 我知道你可以通过过滤无限的自然数列表来创建无限的素数列表,但谁写了 十次午餐...)
答案 0 :(得分:19)
“在另一个地方需要之前,没有任何东西被评估”是一个简化的比喻,它没有涵盖懒惰评估的所有方面(例如它没有提到严格性现象)。
从理论的角度来看,在设计纯语言时有三种方法可去(当然,如果它基于某种lambda演算,而不是基于更奇特的评估模型):严格,非严格和总体。
每个都有其优点和缺点,因此您需要阅读相应的研究论文。
总语言是这三种语言中最纯粹的。在另外两个中,非终止可以被视为副作用,因此必须构建严格性和总体分析器以保持实现有效。这两种分析都是不可判定的,因此分析仪永远不会完整。
然而,总语言的表达力度最低:总体语言不可能是图灵完整的。获得足够好的表达能力的一种常见方法是使用内置的证明系统进行有根据的递归,这种方法比非语言分析器更容易构建。
从实际角度来看,非严格语义使您可以更轻松地定义控件抽象,因为控制结构本质上是非严格的。在严格的语言中,您仍然需要一些具有非严格语义的地方。例如。 if
构造即使在严格的语言中也具有非严格的语义。
因此,如果您的语言严格,控制结构就是一种特殊情况。相反,非严格的语言可以统一为非严格 - 它在严格的结构中没有固有的需要。
至于“谁在午餐前写了十次” - 任何使用Haskell进行项目的人都可以。我认为使用一种语言开发一个非玩具项目(在你的情况下是一种非严格的语言)是掌握其优点和缺点的最佳方法。
以下是非玩具示例所示的一些懒惰的通用用例:
控制流程很难预测的情况。想想属性语法,当没有懒惰时,你必须对属性进行拓扑排序以解决dependensies。每次更改依赖关系图时重新排序代码都是不切实际的。在Haskell中,您可以在没有显式排序的情况下实现属性语法形式,并且在Hackage上至少有两个实际的实现。属性语法在编译器构造中有广泛的应用。
“生成和搜索”方法可以解决许多优化问题。在严格的语言中,你必须交错生成和搜索,在Haskell中你只需要编写单独的生成和搜索函数,你的代码在语法上保持模块化,但在运行时交错。想想旅行商问题(TSP),当您生成所有可能的旅程,然后使用分支定界算法搜索它们。请注意,分支绑定算法仅检查巡视中的某些第一个城市,仅生成路线的必要部分。即使在最纯粹的配方中,TSP也有多种应用,例如规划,物流和微芯片的制造。稍微修改后,它似乎是许多领域的子问题,例如DNA测序。
惰性代码具有非模块化控制流,因此单个函数可以根据其执行的环境具有许多可能的控制流。这种现象可以看作是某种“控制流多态”,所以懒惰控制流抽象比它们的严格对应物更通用,并且高阶函数的标准库在惰性语言中更有用。想想Python生成器,循环和列表迭代器:在Haskell列表函数中涵盖所有三个用例,控制流由于懒惰而适应不同的使用场景。它不仅限于列表 - 想想Data.Arrow和iteratees,状态monad的懒惰和严格版本等。另请注意,非模块化控制流程既有优点也有缺点,因为它使得对性能的推理更加困难。
懒惰可能无限的数据结构在玩具示例之外是有用的。查看Conal Elliott关于使用try来记忆高阶函数的作品。无限数据结构在Python意义上显示为无限搜索空间(参见2),无限循环和永不耗尽的生成器(参见3)。
答案 1 :(得分:2)
Mac OS X的核心图像是懒惰评估的一个很好的实例。
基本上,Core Image可让您创建图像生成器和滤镜的有向无环图。直到过程的最后一步才实际进行评估:实现。当您请求实现核心图像图形时,最终图像帧将通过图形的变换向后传播,从而最大限度地减少需要评估的实际像素值的数量。
答案 2 :(得分:2)
在休斯的经典Why Functional Programming Matters中对这一点进行了广泛的讨论。其中,休斯认为,懒惰允许使用一些可访问的示例来改进模块化。