关于比较:Haskell,Lisp和冗长

Haskell, Lisp, and verbosity

对于那些同时体验过Haskell和Lisp的人,我很好奇用Haskell和Lisp编写代码是多么"愉快"(用一个可怕的术语)。

一些背景:我现在正在学习haskell,之前曾与scheme和cl(以及对clojure的一点探索)合作过。传统上,您可以认为我是动态语言的爱好者,因为它们提供的简洁和快速。我很快就爱上了lisp宏,因为它给了我另一种避免冗长和样板文件的方法。

我发现Haskell非常有趣,因为它向我介绍了我不知道存在的编码方式。它肯定有一些方面看起来有助于实现敏捷性,比如部分函数的编写容易。但是,我有点担心丢失Lisp宏(我假设我丢失了它们;事实上我可能还没有了解它们?)以及静态打字系统。

在两个世界中都做了大量编码的人会不会对体验的差异、你喜欢哪种以及如果所说的偏好是情境性的进行评论?


简短回答:

  • 几乎任何你能用宏做的事情,你都可以用一个高阶函数(我包括单子,箭头等),但它可能需要更多的思考(但只是第一次,这很有趣,你会成为一个更好的程序员),以及
  • 静态系统是非常通用的,它永远不会妨碍您的方式,而且有点令人惊讶的是,它实际上"有助于实现敏捷性"(如您所说),因为当您的程序编译时,您几乎可以确定这是正确的,因此这种确定性让您尝试一些您可能害怕尝试的东西——编程时有一种"动态"的感觉。虽然这和Lisp不一样。

[注意:有一个"template haskell"允许您像在Lisp中一样编写宏,但严格来说,您不应该需要它。]


首先,不要担心丢失诸如动态输入之类的特殊功能。正如您熟悉公共Lisp(一种设计非常好的语言)一样,我假设您知道一种语言不能简化为它的特性集。这是一个连贯的整体,不是吗?

在这方面,哈斯克尔的光芒和普通的口齿不清一模一样。它的特性结合起来为您提供了一种编程方法,使代码非常简短和优雅。宏的缺乏在某种程度上可以通过更复杂的概念(同样,更难理解和使用)来缓解,比如monads和arrows。静态类型系统增加了您的能力,而不是像大多数面向对象语言那样阻碍您的工作。

另一方面,haskell中的编程与lisp的交互作用要小得多,而且像lisp这样的语言中存在的大量反射并不符合haskell预先假定的静态世界视图。因此,您可以使用的工具集在这两种语言之间是非常不同的,但很难相互比较。

我个人更喜欢Lisp的编程方法,因为我觉得它更适合我的工作方式。然而,这并不意味着你也一定要这么做。


与普通的Lisp相比,haskell对元编程的需求更少,因为许多元编程可以围绕monads进行,并且添加的语法使嵌入的DSL看起来不像树,但是始终有模板haskell,正如shrevatsar所提到的,甚至Liskell(haskell语义+Lisp语法),如果您喜欢括号的话。


关于宏,这里有一个关于它的页面:你好,哈斯克尔,再见,Lisp。它解释了哈斯克尔不需要宏的观点。它附带了一个简短的比较示例。

例如,需要lisp宏来避免对两个参数进行计算:

1
(defmacro doif (x y) `(if ,x ,y))

例如,如果haskell没有系统地评估这两个参数,而不需要宏定义:

1
doif x y = if x then (Just y) else Nothing

和Voice


我是一个普通的Lisp程序员。

一段时间前尝试过哈斯克尔,我的个人底线是坚持使用cl。

原因:

  • 动态类型(查看动态与静态类型-基于模式的分析帕斯卡·科斯坦扎)
  • 可选参数和关键字参数
  • 带有宏的统一同形列表语法
  • 前缀语法(不需要记住优先规则)
  • 不纯,因此更适合快速成型
  • 具有元对象协议的强大对象系统
  • 成熟标准
  • 各种编译器

当然,哈斯克尔也有其自身的优点,并且以一种根本不同的方式做了一些事情,但对我来说,这并不是长期的。


在Haskell中,可以定义if函数,这在Lisp中是不可能的。这是可能的,因为懒惰,它允许在程序中实现更多的模块化。这篇经典的论文:为什么FP很重要由约翰休斯,解释了如何懒惰提高可组合性。


使用Haskell中的宏(如果可能的话)可以在Lisp中实现一些非常酷的功能。以"memoize"宏为例(参见彼得·诺维格的paip第9章)。使用它,您可以定义一个函数,比如foo,然后简单地评估(memoize'foo),它将foo的全局定义替换为memoize版本。你能在具有高阶函数的haskell中达到同样的效果吗?


当我继续我的haskell学习之旅时,似乎有一件事可以帮助"替换"宏,那就是定义自己的中缀运算符并自定义它们的优先级和关联性。有点复杂,但系统很有趣!