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](https://en.wikipedia.org/wiki/Haskell_(programming_language), 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 theHUnit
module into theghc
- the
Test.HUnit
module defines a function calledrunTestTT
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.
Top comments (0)