两年前,我开始学习PureScript。我曾经对纯函数式编程很感兴趣,但是一两次都没学过Haskell。 PureScript似乎是对这个世界的一种更友好,更温和的介绍,同时保留了使Haskell着迷的纯净性的基本属性。作为mylearning过程的一部分,我重建了一个以前在go中编写的松弛bot 1。
一旦我学习了PureScript并且对纯函数习语变得更加熟悉,下一步的逻辑步骤似乎就是学习Haskell。我惊讶地发现通过学习PureScript我已经了解了多少Haskell,但是诸如惰性(PureScript是一种严格的语言)之类的核心功能却花了一些时间来适应。
我决定通过再次重写松弛机器人2once来结束Haskell的学习经历,这次是在Haskell。在这篇文章中,我将比较和对比在Haskell和PureScript中编写同一程序的经验。我构建的应用程序“真实”足以带来一些有趣的设计挑战。其中包括:
Haskell是一种惰性语言,而PureScript是一种严格的语言。我希望这种核心差异在编写这些应用程序时会不断显现,但实际上很少出现。我曾预料过很多事情会撞墙,以应对懒惰的虫子,但这只是没有发生。
我会说,我通常更喜欢PureScript是一种默认的严格语言。当需要懒惰时,有很多获取它的方法,但是它总是很明显的。
虽然与严格性没有直接关系,但是我在Haskell中没有遇到过的PureScript方面的一个痛点是堆栈安全性3。在PureScript中,确定您使用的操作是否是堆栈安全性常常会造成混淆。堆叠不安全,所产生的错误可能令人困惑且难以追查。我发现自己在PureScript中为堆栈安全而苦苦挣扎远远超过在Haskell中为懒惰而苦苦挣扎。
一两年前,我会简单地说PureScript具有令人难以置信的工具,而Haskell没有。由于在HaskellLanguage Server项目上所做的出色工作,这种差距开始缩小。
无论如何,PureScript仍然具有更好的工具。 Spago是令人难以置信的构建工具,它提供了许多与Stack相同的功能,同时又保持了用户友好性,PureScript的语言服务器非常出色且易于安装。
但是,PureScript当前在几个方面都在挣扎。首先,packageecosystem正在从凉亭转移到Github上托管的注册表。结果注册表似乎进展顺利,我相信结果对于语言来说将是令人难以置信的,但是目前的过渡状态是不幸的。我很高兴核心维护者花时间设计此注册表井,我坚信这会在6到12个月内对PureScript社区产生积极的影响。
其次,PureScript对于格式化程序来说不是一个很好的选择。尽管确实存在purty,但它似乎主要是维护模式,许多格式化选择对我来说都是令人沮丧的。特别是,自动删除函数中的空白行以及添加新行以让do分配给do都阻碍了可读性和作者意图。在Haskell方面,ormolu易于安装且“工作正常”。
说Haskell具有比PureScript更多的语言功能,这并不是有争议的说法。说要确定应使用这些功能中的哪些以及应启用哪些扩展功能的挑战也并不是什么新发现。这是我为我的项目启用的默认扩展名的列表:
我发现Haskell的派生功能比PureScript更强大,并减少了样板,尤其是在创建应用程序monad时。我也喜欢Haskell的类型类实例不需要名称。 PureScript要求的名称在可读性或作者意图方面几乎没有增加。
到目前为止,我觉得这两种语言之间最大的区别是它们处理记录的方式。同样,这不是一个新发现,但不能高估PureScript的记录比Haskell的记录好多少。基于行多态性的记录具有创建,更新和访问记录的专用语法,因此使用起来很愉快。 GHC确实有一个添加记录点语法的提议,可以解决许多这些问题,但是我认为基于行多态性的基础实现将继续使PureScript占据优势。
当我第一次学习PureScript时,我将其编译时间与其他静态类型的语言(如Rust,Go和Java)进行了比较。我发现它比其他语言要慢得多,尽管增量重建通常相当快。我假设转移到Haskell后,情况将是可比的或更好的。我错了。
Haskell中的编译时间比PureScript差很多,在编译项目的依赖项时,编译时间通常要差一个或两个数量级。我说这对Haskell毫无用处(这似乎是一个极具挑战性的问题),但更值得称赞PureScript社区在此方面所做的工作。
Haskell具有比PureScript大得多的软件包生态系统。 Haskell和PureScript是相互隔离的,Haskell具有非常丰富的软件包集合,这些软件包的质量通常很高。但是,PureScript在移植许多bestHaskell软件包方面做得很出色。
此外,PureScript使您可以通过FFI访问整个JavaScriptpackage生态系统。尽管社区中的许多人认为这是一个缺点,但从实际的角度来看,这是很棒的。例如,在为Haskell建立全文搜索时,我最终使用了全文搜索包。它具有完整的功能和全面的功能,但是需要大量的代码才能正常工作。
在PureScript方面,我围绕elasticlunr构建了一个简单的FFIwrapper。虽然我了解JavaScript生态系统具有质量可变的软件包,但确实有很多软件包可以解决许多问题,而PureScript的FFI使得以安全的方式利用此巨型生态系统变得极为容易。
对于我的PureScript应用程序,我选择了HTTPure。它重量轻且易于使用,同时感觉很惯用。这是一个相对简单的选择,因为PureScriptecosystem中的服务器端框架很少有包含我需要的功能(特别是中间件)的选项。
在Haskell方面,Web框架的选择更为复杂。我想要一些小巧轻便的东西,但是能够在我的custommonad堆栈中为我的应用程序工作。我最终使用了scotty,但是默认的中间件解决方案无法在您的应用程序的monad堆栈中运行,因此Ineeded明确为路由器中的每个端点提供了类似中间件的功能。
归根结底,这主要是语言之间的冲突,但是我希望Haskell生态系统在后端HTTP服务空间中会更加成熟。
要部署我的PureScript应用程序,我使用了ncc。尽管这要求我在部署环境中具有可用的节点,但是这使其他一切变得容易。 ncctool生成了一个单独的JavaScript文件,其中包含所有应用程序代码以及所需的依赖项。然后,我简单地将其部署到我的部署环境中,并与node一起运行。
在Haskell方面,我使用了堆栈的docker支持在与部署环境(debian)匹配的容器中构建应用程序的可执行文件,然后还通过scp交付了生成的可执行文件。该可执行文件是完全独立的。
总体而言,这两种方法在简便性方面都相当。在PureScript方面,在部署环境中需要节点有点令人沮丧。在Haskell方面,不得不使用docker进行交叉编译带来了额外的麻烦。总体而言,这两种经验都相当不错。
在阅读这篇文章时,我担心感觉就像我在选择Haskell一样-绝对不是。我非常清楚PureScript受Haskell的影响很大,并且站在巨人的肩膀上。 PureScript能够从Haskell的一些错误中吸取教训,并做出关于Haskell生态系统中有意偏离的选择,这些偏离更适合PureScript的预期用例。
我发现学习和使用Haskell和PureScript的经验非常有益。鉴于PureScript在服务器端HTTP领域与Haskell相比表现出色,我将对此感到惊讶,因为它目前主要集中在前端。我认为这两种语言都有光明的未来,我很高兴跟随他们的不断发展。