我对2020年哈斯克尔的思考

我在2019年10月做了一次演讲坚持简单Haskell。我花了很多精力编写和准备它。不少人来找我说他们真的很喜欢它。他们不是你通常的怀疑对象:那些永远不会放弃类型级编程的人。相反,他们是普通的软件开发人员。像我和其他许多人一样,他们希望自己的日常工作少一些垃圾。我认为演讲中的一些想法值得传播,或者至少值得谈论。诸如Boring Haskell宣言我们需要更多的新鲜空气。这是我的小小贡献。


30岁

2020年,哈斯克尔将满30岁。从一开始,语言设计者就希望Haskell用于:

  • 函数编程教学
  • 创新和推进程序设计语言研究
  • 构建真实应用程序和大型系统

让我们忽略教学,专注于其他两个方面。

编程语言研究人员喜欢Haskell,因为GHC可以通过Extensions进行扩展。GHC是尝试新想法的最佳场所。如今,打开一个文件并收到一堵扩展墙是很常见的:

{-#LANGUAGE数据种类#-}{-#LANGUAGE灵活实例#-}{-#LANGUAGE灵活上下文#-}{-#LANGUAGE种类签名#-}{-#语言GADTs#-}{-#LANGUAGE范围类型变量#-}{-#LANGUAGE Standalone派生#-}{-#LANGUAGE类型系列#-}{-#LANGUAGE类型运算符#-}{-#LANGUAGE无法确定实例#-}

这些从根本上改变了语言和类型系统。你可以和一个更复杂、更有能力的野兽一起工作。您更接近于依赖类型。在编译时可以得到更多的保证,因为可以在类型中编码更多的信息。我们通常将这些称为花式款式.

然而,自1998年以来,“Haskell the language”并没有太大发展。从广义和不准确的角度来看,Haskell98是在没有启用扩展的情况下运行GHC时得到的。您可以获得简单的数据类型、类型类和一个懒惰的纯功能内核。


在Haskell中构建应用程序

PL研究人员全力以赴研究花俏类型。但我不是PL研究员。我是一名软件开发人员。回想一下,设计师的目标之一是让Haskell在行业中使用,编写真实世界的应用程序。Fancy Haskell适合吗?

纸上看起来不错

编写生产软件与编写论文有很大不同。

一旦一份研究报告出来了,它就不必被维护。在PDF上看起来不错的代码示例中显示的技术和技巧并不能说明它们会如何影响真正的代码库。研究人员可以依赖实验特性和未经测试的扩展。他们可以通过较慢的编译时间来应付。

包容性

编写生产软件是一项团队工作。

我想与不同的人群一起工作。我不想让博士学位成为与Haskell合作的必要条件。我想要一个所有技能级别都可以访问的代码库。应用程序中的大多数业务逻辑都不是火箭科学。没有理由让应用程序变得比需要的更复杂。

自私地说,作为一家押注哈斯克尔的公司,你可以通过降低进入门槛来获得更广泛的人才库。

边际效益

花哨的类型给了我们更多的保证,所以我们可以编写更安全、更正确的代码。我们为什么要放弃呢?

我不相信这样的好处有那么大。从主流编程语言跳转到Haskell是相当明显的。有一个编译器可以帮助你编写正确的代码。那可能还不是最正确的代码。这是真的,有了花式类型,我可以获得额外的x%的信心。复杂性值得吗?

为了务实,我们需要跳出框框看一看。我们需要认识到类型是一个无价的工具,但它们不是我们可以使用的唯一工具。类型不是避免测试的借口。


简单Haskell

我并不是建议我们忽视1998年之后发生的一切。

Haskell98是一个很好的基础,但我们可以改进它。核心思想是接受类型系统现状和关注人机工程学。使语言更好,而不是类型系统。

这归结为使用Generics和实现符合人体工程学的扩展。

Haskell98中没有泛型。他们去除样板非常棒。您可以使用generic-lens自动派生JSON实例和透镜。

某些扩展是无害的,并且使Haskell更适合使用。

{-#LANGUAGE块参数#-}{-#LANGUAGE Lambda案例#-}{-#LANGUAGE重载字符串#-}{-#LANGUAGE类型应用程序#-}

应用程序架构

大多数应用程序只应构建为:

应用程序::环境->IO()

哪里环境表示依赖项注入容器。

数据环境=环境{用户缓存::TVar[用户],postgresConnection::PG.连接,日志::严重性->文本->IO(),fetchUser::UserId->IO(可能是用户),storeFile::Filename->ByteString->IO(任一文本())}

我的直觉决定了某物是否属于环境:

  • 这个东西是整个应用程序共享的资源吗?(即Postgres连接)
  • 我想提供不同的实现吗?(即测试期间的模拟)

当类型变大时,可以进一步拆分。

数据用户服务=用户服务{fetchUser::UserId->IO(可能是用户),updateUser::User->IO(任一UserServiceError()),deleteUser::UserId->IO(UserServiceError之一())}数据环境=环境{userService::用户服务, ...}

当应用程序变大时,可以重构以使用ReaderT设计模式.


2020年哈斯克尔

尽管哈斯克尔试图取悦两种不同的人群:编程语言研究人员和软件开发人员,但他还是成功了。30年后,我们有了一种仍然被两个群体使用的语言。但他们的需求有着天壤之别。

作为软件开发人员,我们需要了解哪些Haskell特性在编写应用程序时有用。更重要的是,哪些功能有害或无效。PL研究人员和软件开发人员共享相同的工具,但这并不是盲目采用Fancy类型的借口。

作为Haskell的开发人员,我们需要认识到,并不是所有问题都应该变成一篇论文。您可以选择:

  • 开始4个小时的探索,在类型级别找到一个漂亮的解决方案。
  • 花10分钟用无聊的方式解决它。甚至可以写一个测试。

为了包容性而牺牲一点类型安全是可以的。我们仍然可以通过测试来确保我们的软件是正确的。这就是Simple Haskell的真正含义。术语Boring Haskell的效果更好。

写简单/无聊的哈斯克尔是一种乐趣。具体的代码是如此自由。抽象很好,但无聊的哈斯克尔更好。

下次初学者与你谈话时:

  • 不要推荐仆人。斯科蒂干得很好。类型安全路由可以等待。
  • 不要推荐任何效果系统。大量抽象可以等待。
  • 不要糟蹋榆树。或者去。你什么都没有完成。
  • 不要推荐尼克斯。Stack很管用。是的,无论您花了多少小时来获得nix-build,都要按照您的预期去做。
  • 继续与他们交谈,即使他们没有博士学位。

2020年,离开象牙塔。写信给Boring Haskell。


在这篇帖子发表几小时后,事情发生了意想不到的转折,马特·帕森斯发表了一篇很好的文章来宣传《少年守则》。如果你喜欢这篇文章,你应该读一读。如果你不喜欢这个帖子,那么它是必读的。


你可以在推特上关注我 @_羊驼.