关于使用Haskell进行启动的思考

2021-02-19 16:06:37

几乎恰好一年前,我退出了我的作业,以创建一个哈克尔启动作为独奏开发人员。我有大约20个想法,但最终解决了依赖项目健康跟踪的理念,致命的死亡。

自从大约2016年以来,我对Haskell有一个强烈的痴迷。在学习Haskell之前,我是一名经验丰富的OO风格的开发人员,但我并不真正知道如何继续提高我的原始编程能力。 Haskell向我推出了功能规划(FP)的世界,它具有几乎无限的概念来学习,其实际上有助于提高代码质量和应用架构。

Haskell挑战了解,但非常有趣。为了我自己的学习和乐趣,如果我的初创公司成功,我想做哈克尔。

此外,我认为Haskell是最好的通用编程语言(您可以在生产中使用)。特别是,Haskell擅长写作“无聊”的业务应用程序,通常是我的工作方式。 '为什么Haskell生产'对Haskell优惠的好处有更多细节。

可能是最具挑战性的部分,建立了一个骨架架构,以悬挂我的业务逻辑。我决定在Haskell,公平的仆人和融合效果的公平图书馆。

我花了很少的时间敲击我的脑袋,试图让这些图书馆齐心协力地工作。这主要是缺乏缺乏哈斯克尔能力的能力。我尽我所能准备,但哈斯克尔深处,我需要学习更多的时间来与它一起工作。我很幸运,最终找到一个婚姻与这两个图书馆一起结婚的例子,这是一个救生员。我相信我最终会得到那里的那个,但在那一点上我有点盯着我。

Haskell很棒,但与大多数语言一样有Cruft和遗留遗产。 Haskell有一个标准库,称为遗迹,遗憾的是,包括包括的不安全或不规则的功能。因此,我与另类标准库中的替代标准库中建立并改善了基础。在此之上,有许多核心库是我想要使用的标准库的一部分,并且周围有很好的模式。

此外,我正在部署到Google Cloud,因此需要从Haskell找出实现集成的良好模式。

设置工作非常具有挑战性。我花了大部分时间看着编译器错误。但是,只花了大约2周的时间便有了一个良好的代码基础就可以开始构建我的业务逻辑。

这是开始变得非常有趣的时候。我设定了核心模式,可以专注于建立管道。日复一日地将我的逻辑写成我组成的小型纯函数非常好。

Haskell具有令人印象深刻的自动魔术代码生成技术,以至于您花费更多的时间专注于应用程序有趣的逻辑上,而不是样板上。

数据HappinessLevel =悲惨|伤心|平均快乐HaskellDeveloper派生(Show,Eq,Ord,Bounded,Enum,ToJSON,FromJSON)-神奇的代码生成-可以,这不是真正的神奇,请考虑对配置的常规-您可以在其中生成合理的默认值,或者根据需要进行自定义

Deadpendency的许多逻辑正在解析。解析依赖文件或解析各种API响应。 Haskell有许多出色的解析库,最著名的是aeson for JSON。

为什么在Haskell这么好? “ monad”抽象非常适合处理具有很多失败条件(例如,解析)的代码,并且避免了“金字塔厄运”类型的代码。 Haskell在这一关键领域的表现非常出色。

编写《 Deadpendency》的另一个强项是测试。 Haskell具有鲜为人知的测试库样式,可以执行“基于属性的测试”(PBT)。

PBT允许您为数据类型编写值生成器,用于生成100或1000s测试用例。然后,对某些函数运行这些生成的值,并检查某些属性是否成立。

例如,Deadpendency逻辑的一部分是在最后生成HTML报告。我有一些toHtml :: Report->我要测试的HTML函数。所以我写了一个fromHtml :: HTML->报告功能的另一种方式(好的编写很痛苦)。然后,我的PBT测试将生成100个Report值,并检查report == fromHtml(toHtml报告)(称为“往返测试”)。通过此测试,我可以使用HTML报告生成逻辑找到许多边缘案例错误。

PBT还存在其他一些语言,但它起源于Haskell(我相信吗?),因此这些库非常出色。

与Haskell合作的一大挑战是缺少维护良好的库。具有讽刺意味的是,在我依赖的19个75(!)软件包中,Deadpendency将其标记为不健康(已弃用或不活动)。这意味着我经常没有要求图书馆维护者修复错误的奢侈行为。即使我PR了修复程序,有时该PR也将被忽略数月。

我认为这是使用像Haskell这样的小众语言的现实。明确地说,我认为图书馆开发人员不欠我任何东西,但是与更流行的语言相比,这是不利的。

值得庆幸的是,Haskell构建工具为从git加载软件包提供了良好的支持。这意味着您可以PR一些错误修复或功能,然后立即使用fork来解决此问题。

我以为可以这样称呼,因为这是我在Haskell周围经常看到的投诉。我遵循了一些很好的建议,这些建议可以使编译保持快速(除了我解决的一个有趣的极端情况)。

Dell 9570 XPS笔记本电脑-(Hex核心-第八代Intel Core i7-8750H CPU),32GB内存

这是在编译所有应用程序依赖项,这需要在编译应用程序代码之前完成。从头开始重建所有内容的情况很少发生,因为我的开发机器和CI都将缓存并且仅重建已更改的内容。

有时您确实会更新一个非常核心的软件包,这会触发许多依赖的软件包重新编译,这可能需要一段时间。虽然,我通常在喝咖啡的第一天开始时进行依赖更新,所以通常不会注意到。

同样,由于缓存,很少发生完全重新编译。因此,大多数代码编辑不会触发许多模块进行重新编译,而且速度很快。

此外,Haskell具有不错的“连续编译”工具,可在保存时触发。通常,到我实际查看终端编译时为止。

这通常在CI中运行。它与许多其他检查(例如运行我的测试)并行运行,这也需要几分钟。因此,时间并不会真正影响构建和部署时间。

暂挂方式的工作相对简单,但是这个问题有很多隐藏的复杂性。也就是说,它占应用程序的99%。在开发它时,我一直在意识到自己对模型的建模过于简单了,需要进行重构。

由于编译器具有类型安全性,Haskell的重构非常安全,这可能是最重要的。但是,Haskell并没有强大的工具来帮助重构,至少在我不断进行的重组方面。现有工具似乎更适合于通用代码的复杂重写,而不是重构模块或重命名标识符。

这样,我通过替换文本搜索手动完成了所有操作,或者只是更改了某些内容并修复了所有新的编译器错误。这有点费劲,有时使我延迟了所需的重构。

可惜Haskell没有重构工具来解决这种情况。梦想是将这些工具集成到IDE中。

话虽如此,应该指出的是,Haskell现在确实具有出色的IDE,其形式为Haskell Language Server(HLS)。该项目的发展势头令人发疯,我为开发人员称赞。 HLS的一个固定的痛点是它现在要进行自动导入,这曾经大大加剧了与Haskell合作的麻烦。我确定Haskell最终会到达那里。

这主要是我为此而抱怨的,但是当某个人非常痴迷于事物的新闪亮版本和Haskell时,等待新的GHC(GHC是Haskell编译器)版本可用一直很痛苦。在使用新的GHC版本之前,需要更新很多库和平台。有时,这些更新可能会拖累很多。

例如,GHC 9刚刚发布,但我仍无法升级到2020年3月首次发布的GHC 8.10。

所以在大约8个月的工作后,我准备开始让用户。我慢慢发射,在几个小渠道中推广它。我的Haskell博览会是如何在刺激?

我的核心Haskell很少有逻辑错误。这是因为默认情况下,Haskell非常安全,我选择了完全的类型,有助于捕获边缘案例。

例如,我使用了很多非空的列表,编译器保证不为空。要使用它们,您必须指定如何处理空的情况。 IE。如果死人无法找到任何依赖关系,我该怎么办?

当然,我有很多测试,因为编译器找不到所有的错误..还是......

一个大的痛点是包注册机器API对它们的结构(特别是NPM)的存在很多不一致。例如,对于NPM包,您可以通过获取DIST标记 - &gt来获取最新版本。最新的。那个没有释放的包怎么样?嗯,你得到了dist标签:{},除了它结果甚至根本没有dist标签键。

我很快意识到我需要优雅地处理像这样的解析失败,因为结构方差如此多。

此问题听起来像对静态键入的经典论点,但是在处理意外数据时,动态类型系统并不更好。动态类型的语言可能更优雅地忽略或延迟失败,但是当数据是意外的形状时,我更喜欢立即失败的Haskell哲学。

另一个大痛点是内存使用率。我使用的是Google Cloud Run,有点像AWS Lambda,您可以在其中指定所需的内存量。为了保持便宜并更好地了解应用程序的内存需求,我至少使用了256MB。直到我去生产并且Deadpendency试图检查更多种类的包装之前,这个数目似乎还不错。

核心问题是.. NPM再次具有一些罕见的程序包,这些程序包具有巨大的JSON有效负载,最坏的情况是未压缩的84MB。事实证明,aeson会先将所有JSON转换为AST,然后再尝试将其解析为您的类型。当JSON小或您正在加载JSON的大部分内容时,这很好。在我的情况下,当我只需要少量数据时,AST(或解析?)的内存显然是原始JSON的20倍。

最终,我意识到我应该使用一个旨在解析恒定内存的库,一切都很好。我可以解析84MB的文件,但只能看到使用了84MB。我可以更进一步,流式传输响应,但现在它可以正常工作。

作为一种懒惰的语言,Haskell由于未评估的表达式以意外的方式积累而导致内存问题。幸运的是,到目前为止,我已经避免了这些问题。

我通过使用StrictData扩展名将我的类型默认设置为严格来做到这一点。此外,Haskell具有惰性链接列表作为默认列表类型。相反,我使用了严格的列表类型。

在一年多的时间里,我能够构建支持11种语言的Deadpendency(并在它周围设置了许多云垃圾😉)。在这一点上,我认为它实际上是相当稳定的。我认为该项目非常成功。

其中很大一部分归因于Haskell及其出色的生态系统。当然,在我创业之前,我花了4年的时间与Haskell进行学习,但是一旦熟练了,它就会非常有效。我确实相信任何开发人员都可以学习Haskell,甚至可以在适当的环境中快速学习它。

下一步是什么? 我正在努力推广Deadpendency,并希望获得更多的用户和反馈。 我是否花了太多时间来研究Haskell,而没有足够的时间考虑这个想法? 我想我们会看到😊。 无论哪种方式,我都学到了很多东西,玩得很开心,所以我认为值得的经历。