£199.99 £139.99 for one year of Unlimited learning. Offer ends on 28 February 2023 at 23:59 (UTC). T&Cs apply

How do we structure Haskell programs, particularly as they get larger and more complex? In this article, Dr Jeremy Singer outlines scoping constructs.

Scoping is an important way to keep your programs tidy. It involves limiting the region of the program in which names ‘exist’ and can be used.

In Haskell, a let expression provides local scope.
A let expression has a series of equations defining
variable values and a final expression
(after the in keyword) that computes a value
with those variables in scope.

Here is an example:

let x = 2in x*x

What will this expression evaluate to?

Multiple variables can be defined in a single let

let x = 2 y = 3in x+y

What will this expression evaluate to?

Note that the variable names line up
underneath one another
This is good formatting practice, but is also needed
for Haskell to interpret the code correctly.
Like Python, whitespace is important in Haskell,
certainly in let expressions anyway!

Sometimes in a let, one of the variables might
depend on another — in the function below, gallons depends on
milespergallon:

journeycost :: Float -> Float -> Floatjourneycost miles fuelcostperlitre =  let milespergallon = 35 litrespergallon = 4.55 gallons = miles/milespergallon in (gallons*litrespergallon*fuelcostperlitre)

Here’s a geometric example:

let diameter = 2*radius circumference = pi*diameterin (diameter, circumference)

By the way, pi is a defined constant in the Haskell Prelude.

### Where clauses

There is another syntax for introducing local variables, called
the where clause.
As we have seen, Haskell is the Swiss army knife of programming languages: there are lots of ways of doing things.

The where keyword, inside an equation, provides
definitions for variables that are used in the equation.

Here are a couple of examples

squareplusone :: Int -> Intsquareplusone x = xsquared + 1 where xsquared = x*x

Note that where must be indented more than the start of the
enclosing equation.

Like let, we can have multiple variables inside a where
clause. Look at this temperature conversion function:

cel2fahr :: Float -> Floatcel2fahr x = (x*scalingfactor) + freezingpoint where scalingfactor = 9.0/5.0 freezingpoint = 32

Again, notice that the variables defined in the where clause all
line up underneath each other.

You might think let and where are very similar!

• Both introduce a local scope.
• Both allow any number of equations to be written.
• Both allow the equations to be written in any order, and variables defined in any equation can be used (“are in scope”) in the other equations.

However there are some important differences:

• let expressions are expressions; let can be used anywhere an expression is allowed.
• where clauses are not expressions; they can be used only to provide some local variables for a top level equation.

In which circumstances do you think it might it be better to use let instead of where? Or the other way round? Please write suggestions in the comments below.