We write "Hello, world!" The application for the web on Haskell (Spock)

I was bored of writing in Python, I wanted something unusual. I decided to try Haskell. I don’t know the language, but I didn’t want to write console training programs, such as calculating factorial. After studying a fairly large number of posts about Haskell and its application in real life, I realized that one of the potential points of the language’s popularity growth is writing web applications. Oddly enough, there are quite a few web frameworks under Haskell . My choice fell on Spock, because, judging by the description, this is a simple and quick framework to develop. I have some experience writing web applications in Flask, so I thought it would be interesting to compare such dissimilar approaches to solving similar problems. In the article, I will try to give my most detailed way of trial and error in the study of Haskell using the example of writing the simplest Spock web application. Perhaps this will be useful for those who doubt whether to try to study Haskell and whether it will be useful in real life.

A bit about Haskell and how to cook it

The first thing every developer faces when learning a new language is choosing and setting up a development environment. Of course, you can write in a notebook, but if you have at least some experience in developing production projects, this method will cause you pain. By the way, Haskell is quite old and common language and has support for most famous editors and ide. My friend Haskellist uses emacs. I'm used to the normal IDE, so I installed the plugin for IntelliJ.

Also, for development, you need stack , which is now the standard and combines a compiler, a package management system, a build and testing system.

Everything looks pretty friendly, there were no problems with the installation. For development, I use Mac OS, I have not tested it on other systems, but I suspect that under Linux everything will start up without problems either.

Hello world!


We go to the tutorial and try to do everything according to the instructions. There offer first create a standard project through the stack: stack new MyLovelyProlect. Standard project represents a folder with three subfolders: app, src, test. It looks pretty logical: one folder for the main application, one for auxiliary functions, the third for testing. Since we write "Hello, world!", Folder src, and testwe do not need, but do not need to remove them, since otherwise have to carefully erase the other files, for example HelloWorld.cabal.

Actually, the code

Further in the tutorial it is proposed to copy into Main.hssome code. We’ll simplify it a bit more to compare with what flask offers.

{-# 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

For comparison, I will give the same code in flask:

from flask import Flask
app = Flask(__name__)
def hello():
    return "Hello World!"

By the number of lines, flask still wins: 8 versus 13. But considering that Haskell is a statically typed language and 2 lines occupy type determination, the difference, in my opinion, is small. At least the code above did not scare me from further learning the language.

Assembly and launch

Next, go to HelloWorld.cabaland add executable HelloWorld-exea build-depends:line to the section Spock >=0.13. In the tutorial on the site, it is proposed to include 2 more dependencies, but for my purposes they are not needed yet. If we try to build the application with help now stack build --fast --pedantic, we get the following error:

Error: While constructing the build plan, the following exceptions were encountered:
In the dependencies for HelloWorld-
    Spock must match >=0.13, but the stack configuration has no specified version  (latest matching version is
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:
Plan construction failed.

It’s pretty clear: stack doesn’t know which version of Spock needs to be installed, so it needs to be written in the file stack.yaml. We add it there extra-depsand repeat the assembly attempt. Several similar errors will still pop up, and as a result stack.yaml, the following appeared in the file :

  - Spock-
  - Spock-core-
  - reroute-
  - stm-containers-0.2.16
  - focus-

After that, everything gathered. My collection artifacts were in a folder .stack-work/dist/x86_64-osx/Cabal-

Everything started at my command stack exec HelloWorld-exe, and on localhost:8080I saw the coveted "Hello, world!". No dancing with a tambourine was needed.

We’re trying to figure out what’s going on.

So far, we have not used any specific knowledge of functional programming (FP) and the Haskell language. We used common sense and knowledge of the basics of development. You can’t do that further. For further understanding, we need to know some things about AF. AF is not the antipode of OOP. Those familiar with the Scala language know that both of these concepts coexist easily. The antipode of FP is imperative programming. While the functional model of calculations relies on the composition of functions, the imperative model relies on the process of successive changes in the states of the system. It follows that in purely functional languages ​​such as Haskell, it is assumed that the functions are "pure", that is, they do not contain a mutable state and "side effects", in addition to the return value. This makes it easy to build feature compositions. Actually, the requirement of "purity" imposes many restrictions on the use of functional languages ​​in the real world. However, since there are production applications on Haskell, then you can still somehow use pure functions in the real world. Let's take a closer look at ourMain.hs.

As I understand it, an application appis a type variable SpockM, which is a monad . Most likely, if you are not familiar with the functional style of programming and category theory, you will not understand the first time what it is and why you need it. However, it is necessary to deal with this at least at the basic level, since monads are the basis for the application of the Haskell language. There are quite a few articles of varying degrees of detail on this subject, including on Habré. Of course, I will not bring them here. So far, I propose to consider that monads are such magic that allows you to produce so-called side effects. In our application, there is another monad: IO. Its side effect is data input / output.

SpockM is parameterized by four other types. They correspond to the database connection, session, state, and return value. None of this is needed for an empty application, so we will use a type ()called Unit everywhere . Inside, appwe attach paths to action. In this case, we have defined the base path /and action верни мне текст "Hello, world! по get-запросу.

Next, we create a default config and assign it to cfg. Next, using the function, spockcreate middleware for the app and cfg and pass it runSpockalong with the desired launch port.


It is clear that everything described here is very simple, and everyone who speaks English and is equipped with a brain will be able to do the same thing by looking at the initial spock tutorial. This article was more about how I became acquainted with the Haskell language. What's next? Further, almost all learning resources suggest using state and writing a todo application, then connecting the database, then ... Maybe in the future I will write a sequel.


Also popular now: