A CRUD journey in Haskell, part I, introduction

A CRUD journey in Haskell, part I, introduction

Series of writing a CRUD application in Haskell. Introduction and Hello World.

Leandro Proença's photo
Leandro Proença

Published on Aug 3, 2021

5 min read

In the past few days, I've been learning some Haskell after years of seeing people praising this programming language, be it about its expressiveness on writing programs or its pureness of being a "pure" functional programming language free of side-effects.

Expressiveness

After playing a bit with Haskell, I could realize how much I like such expressiveness in a programming language.

Additionally, I have backgound programming in Ruby since 2010 and Elixir since 2016. I always loved the way I can write declarative and readable code in those languages, which makes them my 2 preferred languages by far.

Then I met Haskell.

Types

In the late 2000's, I started my career programming in Java. Hence, I have some familiarity working with type declarations and how to use types to ensure some level of correctness.

However, when learning Haskell, I could realize there's much more I could learn on types. After more than a decade doing mainly duck type programming, I feel I have to do a "mental shift" in order to master types.

Thankfully, Haskell does a great job on type inference, which will ease my process on using types.

Hello World

In this blogpost series I'll assume you already have the Haskell environment installed. In case you want to save your time and your host storage utilization, you can use Docker.

A simple hello world using the ghc:

ghc -e 'putStrLn "Hello World in Haskell!"'

This is the most possible simple Hello World in Haskell.

ghc on the -e mode will interpret the expression putStrLn "Hello World in Haskell", compile it to native code and execute it, all in a single command.

What's ghc?

It stands for "Glasgow Haskell Compiler". It's the most used Haskell compiler implementation, having its initial release in the early 90's.

When we write a Haskell program in a .hs source file, we use the ghc to compile our program into native code. For instance, let's make another "Hello World" but using the compilation process manually:

Hello.hs

main = putStrLn "Hello World in Haskell!"

Then, compile the program using ghc:

ghc Hello.hs

It will compile and generate the binary code in the output file ./Hello, which can be executed successfully:

./Hello

"Hello World in Haskell!". YAY! 🎉

Skipping the manual compilation process

Remember our first Hello World example, skipping the manual compilation process? The -e option can be a valid Haskell expression, hence function calls are eligible for that:

ghc app/Hello.hs -e main

The same as calling:

ghc -e 'putStrLn "Hello World in Haskell!"'

A Quicksort example

Let's write a simple, not-optimized Quicksort algorithm:

Quicksort.hs

quicksort [] = []                                     
quicksort [pivot] = [pivot]                                

quicksort (pivot:tail) =                                   
  (quicksort [smaller | smaller <- tail, smaller <= pivot])
  ++ [pivot] ++                                            
  (quicksort [larger | larger <- tail, larger > pivot])

Now, in order to run the algorithm, it's only a matter of:

ghc Quicksort.hs -e "quicksort [5, 13, 8, 1, 2, 1, 3]"

# [1, 1, 2, 3, 5, 8, 13]

I'll skip explaining the Quicksort solution, but in Haskell we basically used the following features:

Using the REPL

Haskell comes with a great REPL called GHCi. From the GHCi interpreter, we can load modules and execute their functions right from there:

Enter the REPL:

ghci

Then, inside the REPL, do some math:

ghci> 1 + 1
2

Load the Quicksort module:

ghci> :load Quicksort.hs
[1 of 1] Compiling Main       ( Quicksort.hs, interpreted )
Ok, one module loaded.

ghci> quicksort [2, 3, 1]
[1, 2, 3]

And quit the REPL:

ghci> :quit

Prelude

Inside the REPL, Haskell loads automatically the basic Prelude module and some standard libraries, such as IO and Complex. Because of that, any function belonging to the Prelude module will be acessible via REPL.

# implicit
ghci> putStrLn "hello from GHCi"

# explicit
ghci> Prelude.putStrLn  "hello from GHCi"

Writing Unit tests

Testing is a good engineering practice agnostic to technology or tooling. Haskell is no different and the community created a great tool for Unit testing called HUnit, inspired by the Java JUnit.

Since it's an external package, we must download it and import the module Test.HUnit in order to run our first unit test in Haskell.

We will use Stack which comes with a good tooling for creating and managing complex projects in Haskell.

First of all, let's setup Stack so it will install a ghc in an isolated place and give us a buch of useful commands for using packages:

stack setup

Now we are able to install the HUnit package:

stack install HUnit

Following, the unit test:

Tests.hs

import Test.HUnit                                 

simpleTest = TestCase (assertEqual "1 equals 1" 1 1)

And then, we can run our test:

stack ghc -- Tests.hs -e "runTestTT simpleTest"

Which results in:

Cases: 1  Tried: 1  Errors: 0  Failures: 0
Counts {cases = 1, tried = 1, errors = 0, failures = 0}

Notes:

  • calling stack is needed so Stack will load the HUnit module into the ghc
  • the Test.HUnit module defines a function called runTestTT which we use to run our test

We can even define multiple TestCases at once:

import Test.HUnit                                              

simpleTest = TestCase (assertEqual "1 == 1" 1 1)                
mathTest  = TestCase (assertEqual "10 / 5 equals 2" (10 / 5) 2)

tests = TestList [
    TestLabel "simple" simpleTest, 
    TestLabel "math" mathTest]
stack ghc -- Tests.hs -e "runTestTT tests"

Or, if preferred, multiple assertions per TestCase:

import Test.HUnit                         

tests = TestCase $ do                     
  assertEqual "1 == 1" 1 1                
  assertEqual "10 / 5 equals 2" (10 / 5) 2

Wrapping Up

In this post we've seen a gentle introduction to the Haskell programming language, writing simple programs using GHC, GHCi and Stack. We also covered how to setup and write unit tests, which are very important to every real-world project.

I didn't cover type declarations, but it will be covered in future posts.

Moreover, in the upcoming posts, we'll do some Socket programming and create a simple TCP server in Haskell, being the base ground of our simple CRUD application.

 
Share this