Category Archives: Haskell

Haskell basics – Functions

Note: I’ve writing these Haskell blog posts whilst learning Haskell, so do not take them as expert guidance. I will amend the post(s) as I learn more.

Functions are created using the format

function-name params = function-definition

Note: function names must be camelCase.

So for example, let’s assume we have a Calculator module with the functions, add, subtract, multiply and divide might look like this

module Modules.Calculator where

add a b = a + b
subtract a b = a - b
multiply a b = a * b
divide a b = a / b

Function can be created without type information (as shown above) however it’s considered good practise to specify type annotations for the functions, so for example let’s annotate the add function to say it takes Integer inputs and returns an Integer result

add :: Int -> Int -> Int
add a b = a + b

Now if we try to use floating point numbers with the add function, we’ll get a compile time error. Obviously its more likely we’d want to handle floating point numbers with this function, so let’s change it to

add :: Double -> Double -> Double

Creating types in Haskell

In most programming language we need ways to declare our own types. These help with readability, modularity etc. Haskell is no different in offering these capabilities.

We can create a simple data type using the data keyword, followed by either of available values (in C# this would be similar to an enum). So here’s the definition of a Boolean type

data Bool = False | True

The values after the = are the value constructors with the | being like an or, hence this Bool have either False or True value.

Note: The type name and the value constructor must be capital cased.

We can also declare types more like structs/classes in that we can define the field types that make up the type. For example here’s a Point type

data Point = Point Integer Integer

In this example we could create a point as follows

pt = Point 1 2

Accessing these values can be a little verbose (especially when we might have lots of fields) because of the lack of any names, hence we’d use pattern matching, i.e.

xpt :: Point -> Integer
xpt (Point x _) = x

-- example of printing the x value of value pt of type Point
print (xpt pt)

Note: The type annotation is not required and the underscore is a discard, hence is ignored.

We can combine types just as we combined False and True into essentially a union. So for example here we have a data type Shape with constructors for Circle and Triangle.

data Shape = Triangle Int Int Int | Circle Int

triangle = Triangle 1 2 3
circle = Circle 4

It’s not clear what these fields of the constructors mean, so let’s add some names which also allows us to more easily access specific values from the data (i.e. fixes the issue with using pattern matching – which ofcourse is still a valid way of doing things).

data Shape = Triangle { 
   hypotenuse :: Int, 
   opposite :: Int, 
   adjacent :: Int 
} | Circle { 
  radius :: Int 
}

We can declare instances of this type using names or without names, for example

-- not using names
triangle = Triangle 1 2 3
-- with names
triangle1 = Triangle { opposite = 5, hypotenuse = 6, adjacent = 7 }

Instead of creating a function to extract a value (like we did with the xpt function we created) we can now use the following

print (hypotenuse triangle)

and Haskell essentially creates the function for us, i.e. using the REPL along with :t hypotenuse we get the following

hypotenuse :: Shape -> Int

Haskell is immutable, so how do we make changes to an instance of data? Well we “copy and update”. Thankfully Haskell makes this easy (so we don’t have to literally create copies of every field on our data and then change values).

If you’ve used JavaScript it’s like using a spread operator of in F# and with.

newTriangle = triangle { hypotenuse = 10 } 

In this case newTriangle is a copy of the triangle data, with the hypotenuse changed.

Unit testing Haskel code with HUnit

As with most languages nowadays, we want to have some unit testing libraries/frameworks. Obviously Haskell is no different and we have tools such as HUnit.

Let’s first create a simple little bit of code to test, here’s my Calculator.hs file (abridged just to show the code I intend to write tests for)

module Modules.Calculator where

factorial :: (Integral a) => a -> a  
factorial 0 = 1  
factorial n = n * factorial (n - 1) 

We’ll need to have installed the HUnit package, if you haven’t already then run

cabal install --lib QuickCheck HUnit

We’ll store our tests in a folder named test off of our root folder (the name of the folder can be altered if you prefer).

Here’s our test code (my file is named CalculatorTests.hs)

module Main (main) where

import Test.HUnit
import System.Exit

import Modules.Calculator as Calc

testZeroCase = TestCase(assertEqual "Factorial 1" (1) (Calc.factorial 1))
testNonZeroCase = TestCase(assertEqual "Factorial 10" (2) (Calc.factorial 10))

main :: IO ()
main = do
    counts <- runTestTT ( test [
        testZeroCase,
        testNonZeroCase
        ])
    if (errors counts + failures counts == 0)
        then exitSuccess
        else exitFailure

In the above code we import our library code and have two tests, the first tests our factorial code with a 0 value, the second tests a non-zero. In this instance I’ve purposefully written a breaking test.

Before we run these and/or fix the tests let’s see what we’re doing with the test code. We create a function for the TestCase and within the TestCase we have our assertion along with a message prefix (the string), next we have the expected value and finally the actual value or in this case the function call which returns the actual value.

In main we basically create the test runner and supply an array of the test functions to be run, finally we exit the runner with success or failure.

Now before we can run our tests we need to declare a Test-Suite within the .cabal file. We add the following to the file

Test-Suite test-Calculator
  type:                exitcode-stdio-1.0
  hs-source-dirs:      test
                       .
  default-language:    Haskell2010
  main-is:             CalculatorTests.hs
  other-modules:       Modules.Calculator
  build-depends:       base >=4.14 && <4.15, HUnit

This tells cabal the folder to look for the tests (test in this case) along with . for the current folder (otherwise our tests won’t find out Calculator module). We then state what the main application for the cabal to run for the tests, include other modules etc.

Now run

cabal test

If all goes well you’ll get something like the following

Running 1 test suites...
Test suite test-Calculator: RUNNING...
### Failure in: 1
test\CalculatorTests.hs:9
Factorial 10
expected: 2
 but got: 3628800
Cases: 2  Tried: 2  Errors: 0  Failures: 1
Test suite test-Calculator: FAIL
Test suite logged to:
D:\Development\somefolder\test\HaskellBasics-0.1.0.0-test-HaskellBasics.log
0 of 1 test suites (0 of 1 test cases) passed.
cabal.exe: Tests failed for test:test-Calculator from
Calculator-0.1.0.0.

Obviously the test with the prefix message “Factorial 10” failed with the expected value 2 but the actual value 3628800, so we can fix that test case so it looks like this

testNonZeroCase = TestCase(assertEqual "Factorial 10" (3628800) (Calc.factorial 10))

and now all our tests will pass.

I’ll leave it to the reader to look into the other assertions etc. For example, checkout Test.HUnit.Base

Other References

HUnit: A unit testing framework for Haskell

Getting Started with Cabal

Cabal is the Haskell package and build tool.

The following are a few of the “getting started” commands.

What version do you have of Cabal?

Run the following

cabal --version

Check for updates to Cabal

You can check

cabal new-install Cabal cabal-install

Check for package updates

This command will pull the latest package list from hackage.haskell.org

cabal update

Creating a new project

We can create a new Haskell project using

cabal init

This will create a Main.hs, Setup.hs and a .cabal file for your configurations. We can then edit the .cabal file or we can run the interactive version and supply details using

cabal init -i

Building our project

We can build our project by simply using

cabal build

Running our project

To run (or build and run)

cabal run

Cleaning our project

cabal clean

Running the ghci repl

You can run ghci by simply typing

ghci

Or you can run the ghci that’s available via cabal using

cabal repl

Adding a library

We can download and install package from The Haskell Package Repository using the command

cabal install --lib QuickCheck HUnit

This installs the packages QuickCheck and HUnit from the package repository. The –lib is used for packages which do not contain an executable (i.e. a library package). We can also install .gz packages by specifying a file location or URL.

Packages are then stored on Windows in

%USERPROFILE%\AppData\Roaming\cabal\store\ghc-8.10.1

References

Welcome to the Cabal User Guide

Haskell basics – Modules

Note: I’ve writing these Haskell blog posts whilst learning Haskell, so do not take them as expert guidance. I will amend the post(s) as I learn more.

Before we begin, let’s use cabal init to create a starter project, then we’ll add our code to this project as part of this post. This will create a Main.hs with the classic “Hello World” type of sample along with the Setup.hs and the .cabal file.

Modules

Apart from the simplest applications we would probably want to package functions, types etc. into modules. Modules exist in most languages, such as those in F#, TypeScript or similar to namespaces in C# & Java. In Haskell each module is stored within it’s own file and the file begins with the following declaration

module ModuleName where

Note: module names should be in PascalCase.

Obviously replacing the ModuleName with your module name and, similar to Java, the ModuleName should be made up of the path to the module file, ending with the module name. For example let’s assume we’re going to create a Calculator module and our application folder is HaskellBasics, let’s assume we’ve created a Modules folder off of this and within that a Calculator.hs file, hence the ModuleName should look like this

module Modules.Calculator where

We need to add the module to the .cabal build file using the other-modules key, for example

executable HaskellBasics
  main-is:             Main.hs
  other-modules:       Modules.Calculator

Note: We don’t include the file extension for the Calculator module.

If we add more modules then we simply add as comma separated values in the other-modules field in the .cabal file, i.e.

  other-modules: Modules.Calculator, Modules.Output

To import modules into our Main.hs we use the import keyword, like this (we’re importing two modules here).

import Modules.Output
import Modules.Calculator

There’s a lot more we can do in terms of importing modules, for example importing only some functions, like this

import Modules.Calculator (add, sub)

We can also import all function except for some, essentially hiding the module functions, like this

import Modules.Calculator hiding (sub)

The ability to specify the functions available and those not available is useful to help with name clashes etc. Another way to handle such name clashes is to use the module qualified names, for example

main = print (Modules.Calculator.add 10 6)

We can also enforce qualified module names for each function within a module be importing out module like this

import qualified Modules.Calculator

and hence all uses of the functions within this module must be fully qualified.

This can end up requiring a lot more verbosity than we really want, so we can import a qualified module and alias it like this

import qualified Modules.Calculator as C

meaning we can use the function like this

main = print (C.add 10 6)

Note: The alias for the module name must start with a capital letter.

Setting up a Haskel development environment on Windows 10

I wanted to try out Haskell on Windows 10 and figured I’d probably use Visual Code for writing code, here’s the steps to get everything installed and configured.

The simplest way to get everything installed seems to be using chocolatey, see Getting Started.

  1. Install chocolatey
  2. Run Powershell in admin mode and then run choco install haskell-dev
  3. Finally, from Powershell run refreshenv

If all of these steps worked, then just run ghc from the Powershell or a command prompt and you should see some output from ghc.

Let’s now set-up Visual Code (I’ll assume you’ve already got Visual Code installed). There’s a few plugins/extensions for Visual Code for use with Haskell. From Visual Code extensions option…

  • Add Haskell syntax highlighter
  • Also add Haskell-linter
  • To configure the linter, from Visual Code select, File | Preferences | Settings, switch to the code view and add the following
    "haskell.hlint.executablePath": "C:\\ProgramData\\chocolatey\\lib\\ghc\\tools\\ghc-8.10.1\\bin\\ghc.exe"
    

    Chocolatey installs the files into C:\ProgramData\chocolatey\lib, however if you’ve installed ghc by some other means of within another folder then replace the folder with your location.

Finally, let’s write a Haskell “Hello World”. Within Visual Code create the file HelloWorld.hs and add the following code

main = putStrLn "Hello, world!"

Open the Visual Code integrated terminal or open your preferred terminal/shell and from the folder that you saved your HelloWorld.hs file, run

ghc HelloWorld.hs

If all goes to plan you’ll compile the Haskell file to a HelloWorld.exe and you’re done for now.