我厌倦了用Python编写代码,我想要一些不寻常的东西。 我决定尝试Haskell。 我不懂该语言,但是我不想只编写控制台训练程序,例如计算阶乘。 在研究了大量有关Haskell及其在现实生活中的应用程序的文章之后,我意识到该语言受欢迎程度增长的潜在点之一就是编写Web应用程序。 奇怪的是,Haskell下有很多Web框架 。 我的选择落在Spock上 ,因为根据描述来看,这是一个简单快速的框架。 我有一些用Flask编写Web应用程序的经验,所以我认为比较这种不同的方法来解决类似的问题会很有趣。 在本文中,我将通过在Spock中编写基本的Web应用程序,尝试在学习Haskell方面给出最详尽的试验和错误方式。 对于那些怀疑是否要研究Haskell以及在现实生活中是否有用的人来说,这可能会有用。
关于Haskell以及如何烹饪的一些知识
每个开发人员在学习新语言时面临的第一件事就是选择和设置开发环境。 当然,您可以在笔记本上书写,但是如果您至少有一些开发生产项目的经验,则此方法将使您感到痛苦。 顺便说一句,Haskell是相当古老且通用的语言,并且支持大多数著名编辑和思想家。 我的朋友Haskellist使用emacs。 我已经习惯了普通的IDE,所以我安装了IntelliJ插件。
另外,对于开发而言,您需要stack ,它是现在的标准,并结合了编译器,程序包管理系统,构建和测试系统。
一切看起来都很友好,安装没有问题。 为了进行开发,我使用的是Mac OS,但尚未在其他系统上进行过测试,但是我怀疑在Linux下,一切都会顺利启动。
世界,您好!
准备工作
我们转到该教程,并尝试按照说明进行所有操作。 他们建议在那里首先通过堆栈创建一个标准项目: stack new MyLovelyProlect
。 标准项目是一个包含三个子文件夹的文件夹: app
, src
和test
。 看起来很合乎逻辑:一个文件夹用于主应用程序,一个文件夹用于辅助功能,第三个文件夹用于测试。 由于我们写的是“ Hello,world!”,因此我们不需要src
和test
文件夹,但是我们不需要删除它们,因为否则我们将必须仔细清除其他文件,例如HelloWorld.cabal
。
其实,代码
在本教程的进一步内容中,建议将一些代码复制到Main.hs
为了与烧瓶提供的功能进行比较,我们将对其进行简化。
{-# LANGUAGE OverloadedStrings #-} module Main where import Web.Spock import Web.Spock.Config app :: SpockM () () () () app = get root $ text "Hello World!" main :: IO () main = do cfg <- defaultSpockCfg () PCNoDatabase () let mw = spock cfg app runSpock 8080 mw
为了进行比较,我将在flask中给出相同的代码:
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" app.run()
从行数上来说,flask仍然是赢家:8比13。但是考虑到Haskell是一种静态类型化的语言,而2行占据了类型确定性,因此,我认为两者之间的差异很小。 至少上面的代码并没有让我进一步学习这种语言。
组装和发射
接下来,转到HelloWorld.cabal
并将executable HelloWorld-exe
添加到build-depends:
部分build-depends:
Line Spock >=0.13
。 在该站点的教程中,建议再包含2个依赖关系,但出于我的目的,它们尚不需要。 如果现在尝试使用stack build --fast --pedantic
构建应用程序, stack build --fast --pedantic
以下错误:
Error: While constructing the build plan, the following exceptions were encountered: In the dependencies for HelloWorld-0.1.0.0: Spock must match >=0.13, but the stack configuration has no specified version (latest matching version is 0.13.0.0) needed since HelloWorld is a build target. Some different approaches to resolving this: * Consider trying 'stack solver', which uses the cabal-install solver to attempt to find some working build configuration. This can be convenient when dealing with many complicated constraint errors, but results may be unpredictable. * Recommended action: try adding the following to your extra-deps in /Users/dkvasov/Documents/Haskell/Spock/HelloWorld/stack.yaml: Spock-0.13.0.0@sha256:8115862eb4fb84a26fb7bcd34f30acf036bd2e7c4eaf813c185c5562d138bba2 Plan construction failed.
很明显:stack不知道需要安装哪个版本的Spock,因此需要将其写入stack.yaml
文件中。 extra-deps
添加extra-deps
然后尝试再次构建。 还有一些其他类似的错误会弹出,结果,在stack.yaml
文件中,我得到了以下信息:
extra-deps: - Spock-0.13.0.0 - Spock-core-0.13.0.0 - reroute-0.5.0.0 - stm-containers-0.2.16 - focus-0.1.5.2
之后,一切都聚集了。 我的收集工件位于.stack-work/dist/x86_64-osx/Cabal-2.4.0.1/build
。
一切都从命令stack exec HelloWorld-exe
,在localhost:8080
我看到了欢迎的“ Hello,world!”。 不需要铃鼓跳舞。
我们正在尝试找出正在发生的事情。
到目前为止,我们尚未使用任何有关函数式编程(FP)和Haskell语言的特定知识。 我们使用常识和发展基础知识。 您无法再这样做。 为了进一步理解,我们需要了解有关AF的一些知识。 AF不是OOP的对立面。 熟悉Scala语言的人知道,这两个概念很容易共存。 FP的对立面是命令式编程。 尽管计算的功能模型依赖于功能的组成,但命令式模型依赖于系统状态的连续变化过程。 因此,在纯函数式语言(例如Haskell)中,假定函数是“纯”的,也就是说,除了返回值之外,它们不包含可变状态和“副作用”。 这使构建要素合成变得容易。 实际上,“纯度”的要求对现实世界中功能语言的使用施加了许多限制。 但是,由于Haskell上有生产应用程序,因此您仍然可以以某种方式在现实世界中使用纯函数。 让我们仔细看看我们的Main.hs
据我了解,应用程序app
是SpockM
类型的变量,它是monad 。 最有可能的是,如果您不熟悉编程的功能风格和类别理论,那么您将不会第一次了解它的含义以及为什么需要它。 但是,由于monads是应用Haskell语言的基础,因此至少必须在基本级别上进行处理。 关于这个主题,包括哈布雷,有很多不同程度的细节文章。 当然,我不会把它们带到这里。 到目前为止,我建议您考虑一下Monad如此神奇,它可以使您产生所谓的副作用。 我们的应用程序中还有另一个monad: IO
。 它的副作用是数据输入/输出。
SpockM还可以通过其他四种类型进行参数化。 它们对应于数据库连接,会话,状态和返回值。 对于空应用程序,不需要任何这些,因此我们将始终使用类型()
,称为Unit。 在app
内部app
我们将路径绑定到操作。 在这种情况下,我们确定了基本路径/
并且该操作 "Hello, world! get-
。
接下来,我们创建一个默认配置,并将其分配给cfg
。 接下来,使用spock
函数spock
为应用程序和cfg创建中间件,并将其与所需的启动端口一起传输到runSpock
。
结论
显然,这里描述的所有内容都非常简单,并且所有会说英语并配备大脑的人都可以通过查看最初的Spock教程来做同样的事情。 本文更多地是关于我如何熟悉Haskell语言的。 接下来是什么? 此外,几乎所有的学习资源都建议使用状态并编写待办事项应用程序,然后连接数据库,然后……也许将来我会写续集。
参考文献