17.3. More Features

17.3.1. Modules and Visibility

For now, we have used the definitions of the Prelude and defined our own in a test file which by default became the Main module. Haskell supports large scale development using modules and explicit export and import expressions. As a first step, we can give our test module test.hs a new name using the module construction. To be able to load the module by its module than (rather than its file name), we store the code in a file Test.hs corresponding to the module name.

module Test where

fac 0 = 1
fac n = n * fac(n-1)

When we now load the module, the prompt changes to Test.

Prelude> :load Test
Reading file "Test.hs":

Hugs session for:
/usr/share/hugs98/lib/Prelude.hs
Test.hs
Test> fac 5
120

We can now import the module Test from another module.

module NewTest where
import Test
f = fac

The import instruction makes all the public symbols of the imported module available in the importing module. Using the term "public symbols", it is clear that there must be a way to restrict the symbols exported by a module. All we need to do is place the list of exported symbols behind the name of the module.

module Test (x, y) where
x = 55
y = 66
z = 77

In this (trivial) example, only the symbols x and y are exported, whereas z is only visible by the module itself. Trying to use z in another module importing Test

module NewTest where
import Test
a = x
c = z

leads to an error:

Prelude> :load NewTest
Reading file "NewTest.hs":
Reading file "Test.hs":
Reading file "NewTest.hs":
Dependency analysis
ERROR NewTest.hs:4 - Undefined variable "z"

Haskell's import directive has a number of other options such as renaming the imported module, importing just a few symbols, or enforcing the use of qualified names (such as Test.fac). The Haskell report explain the module system in detail.

17.3.2. Lazy Evaluation