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.