Haskell/Variables and functions
All the examples in this chapter can be saved into a Haskell source file and then evaluated by loading that file into GHC. Do not include the "Prelude>" prompts part of any example. When that prompt is shown, it means you can type the following code into an environment like GHCi. Otherwise, you should put the code in a file and run it.
Variables[edit  edit source]
In the last chapter, we used GHCi as a calculator. Of course, that's only practical for short calculations. For longer calculations and for writing Haskell programs, we want to keep track of intermediate results.
We can store intermediate results by assigning them names. These names are called variables. When a program runs, each variable is substituted for the value to which it refers. For instance, consider the following calculation
Prelude> 3.141592653 * 5^2 78.539816325
That is the approximate area of a circle with radius 5
, according to the formula . Of course, it is cumbersome to type in the digits of , or even to remember more than the first few. Programming helps us avoid mindless repetition and rote memorization by delegating these tasks to a machine. That way, our minds stay free to deal with more interesting ideas. For the present case, Haskell already includes a variable named pi
that stores over a dozen digits of for us. This allows for not just clearer code, but also greater precision.
Prelude> pi 3.141592653589793 Prelude> pi * 5^2 78.53981633974483
Note that the variable pi
and its value, 3.141592653589793
, can be used interchangeably in calculations.
Haskell source files[edit  edit source]
Beyond momentary operations in GHCi, you will save your code in Haskell source files (basically plain text) with the extension .hs. Work with these files using a text editor appropriate for coding (see the Wikipedia article on text editors). Proper source code editors will provide syntax highlighting, which colors the code in relevant ways to make reading and understanding easier. Vim and Emacs are popular choices among Haskell programmers.
To keep things tidy, create a directory (i.e. a folder) in your computer to save the Haskell files you will create while doing the exercises in this book. Call the directory something like HaskellWikibook. Then, create a new file in that directory called Varfun.hs with the following code:
r = 5.0
That code defines the variable r
as the value 5.0
.
Note: make sure that there are no spaces at the beginning of the line because Haskell is sensitive to whitespace.
Next, with your terminal at the HaskellWikibook directory, start GHCi and load the Varfun.hs file using the :load
command:
Prelude> :load Varfun.hs [1 of 1] Compiling Main ( Varfun.hs, interpreted ) Ok, modules loaded: Main.
Note that :load
can be abbreviated as :l
(as in :l Varfun.hs
).
If GHCi gives an error like Could not find module 'Varfun.hs'
, you are probably running GHCi in the wrong directory or saved your file in the wrong directory. You can use the :cd
command to change directories within GHCi (for instance, :cd HaskellWikibook
).
With the file loaded, GHCi's prompt changes from "Prelude" to "*Main". You can now use the newly defined variable r
in your calculations.
*Main> r 5.0 *Main> pi * r^2 78.53981633974483
So, we calculated the area of a circle with radius of 5.0 using the wellknown formula . This worked because we defined r
in our Varfun.hs file and pi
comes from the standard Haskell libraries.
Next, we'll make the area formula easier to quickly access by defining a variable name for it. Change the contents of the source file to:
r = 5.0
area = pi * r ^ 2
Save the file. Then, assuming you kept GHCi running with the file still loaded, type :reload
(or abbreviate version :r
).
*Main> :reload Compiling Main ( Varfun.hs, interpreted ) Ok, modules loaded: Main. *Main>
Now we have two variables r
and area
.
*Main> area 78.53981633974483 *Main> area / r 15.707963267948966
Note
Note: The let
keyword (a word with a special meaning) lets us define variables directly at the GHCi prompt without a source file. This looks like:
Prelude> let area = pi * 5 ^ 2
Although sometimes convenient, assigning variables entirely in GHCi this way is impractical for any complex tasks. We will usually want to use saved source files.
Comments[edit  edit source]
Besides the working code itself, source files may contain text comments. In Haskell there are two types of comment. The first starts with 
and continues until the end of the line:
x = 5  x is 5.
y = 6  y is 6.
 z = 7  z is not defined.
In this case, x
and y
are defined in actual Haskell code, but z
is not.
The second type of comment is denoted by an enclosing { ... }
and can span multiple lines:
answer = 2 * {
block comment, crossing lines and...
} 3 { inline comment. } * 7
By combining single and multiline commenting, it is possible to toggle between executed and commentedout code by adding or removing a single }
character, respectively:
{
foo :: String
foo = "bar"
}
vs.
{}
foo :: String
foo = "bar"
}
We use comments for explaining parts of a program or making other notes in context. Beware of comment overuse as too many comments can make programs harder to read. Also, we must carefully update comments whenever we change the corresponding code. Outdated comments can cause significant confusion.
Variables in imperative languages[edit  edit source]
Readers familiar with imperative programming will notice that variables in Haskell seem quite different from variables in languages like C. If you have no programming experience, you could skip this section, but it will help you understand the general situation when encountering the many cases (most Haskell textbooks, for example) where people discuss Haskell in reference to other programming languages.
Imperative programming treats variables as changeable locations in a computer's memory. That approach connects to the basic operating principles of computers. Imperative programs explicitly tell the computer what to do. Higherlevel imperative languages are quite removed from direct computer assembly code instructions, but they retain the same stepbystep way of thinking. In contrast, functional programming offers a way to think in higherlevel mathematical terms, defining how variables relate to one another, leaving the compiler to translate these to the stepbystep instructions that the computer can process.
Let's look at an example. The following code does not work in Haskell:
r = 5
r = 2
An imperative programmer may read this as first setting r = 5
and then changing it to r = 2
. In Haskell, however, the compiler will respond to the code above with an error: "multiple declarations of r
". Within a given scope, a variable in Haskell gets defined only once and cannot change.
The variables in Haskell seem almost invariable, but they work like variables in mathematics. In a math classroom, you never see a variable change its value within a single problem.
In precise terms, Haskell variables are immutable. They vary only based on the data we enter into a program. We can't define r
two ways in the same code, but we could change the value by changing the file. Let's update our code from above:
r = 2.0
area = pi * r ^ 2
Of course, that works just fine. We can change r
in the one place where it is defined, and that will automatically update the value of all the rest of the code that uses the r
variable.
Realworld Haskell programs work by leaving some variables unspecified in the code. The values then get defined when the program gets data from an external file, a database, or user input. For now, however, we will stick to defining variables internally. We will cover interaction with external data in later chapters.
Here's one more example of a major difference from imperative languages:
r = r + 1
Instead of "incrementing the variable r
" (i.e. updating the value in memory), this Haskell code is a recursive definition of r
(i.e. defining it in terms of itself). We will explain recursion in detail later on. For this specific case, if r
had been defined with any value beforehand, then r = r + 1
in Haskell would bring an error message. r = r + 1
is akin to saying, in a mathematical context, that , which is plainly wrong.
Because their values do not change within a program, variables can be defined in any order. For example, the following fragments of code do exactly the same thing:
y = x * 2
x = 3

x = 3
y = x * 2

In Haskell, there is no notion of "x
being declared before y
" or the other way around. Of course, using y
will still require a value for x
, but this is unimportant until you need a specific numeric value.
Functions[edit  edit source]
Changing our program every time we want to calculate the area of new circle is both tedious and limited to one circle at a time. We could calculate two circles by duplicating all the code using new variables r2
and area2
for the second circle:^{[1]}
r = 5
area = pi * r ^ 2
r2 = 3
area2 = pi * r2 ^ 2
Of course, to eliminate this mindless repetition, we would prefer to have simply one function for area and then apply it to different radii.
A function takes an argument value (or parameter) and gives a result value (essentially the same as in mathematical functions). Defining functions in Haskell is like defining a variable, except that we take note of the function argument that we put on the left hand side. For instance, the following defines a function area
which depends on an argument named r
:
area r = pi * r ^ 2
Look closely at the syntax: the function name comes first (area
in our example), followed by a space and then the argument (r
in the example). Following the =
sign, the function definition is a formula that uses the argument in context with other already defined terms.
Now, we can plug in different values for the argument in a call to the function. Save the code above in a file, load it into GHCi, and try the following:
*Main> area 5 78.53981633974483 *Main> area 3 28.274333882308138 *Main> area 17 907.9202768874502
Thus, we can call this function with different radii to calculate the area of any circle.
Our function here is defined mathematically as
In mathematics, the parameter is enclosed between parentheses, as in or . Haskell code will also work with parentheses, but we omit them as a convention. Haskell uses functions all the time, and whenever possible we want to minimize extra symbols.
We still use parentheses for grouping expressions (any code that gives a value) that must be evaluated together. Note how the following expressions are parsed differently:
5 * 3 + 2  15 + 2 = 17 (multiplication is done before addition)
5 * (3 + 2)  5 * 5 = 25 (thanks to the parentheses)
area 5 * 3  (area 5) * 3
area (5 * 3)  area 15
Note that Haskell functions take precedence over all other operators such as +
and *
, in the same way that, for instance, multiplication is done before addition in mathematics.
Evaluation[edit  edit source]
What exactly happens when you enter an expression into GHCi? After you press the enter key, GHCi will evaluate the expression you have given. That means it will replace each function with its definition and calculate the results until a single value remains. For example, the evaluation of area 5
proceeds as follows:
area 5 => { replace the lefthand side area r = ... by the righthand side ... = pi * r^2 } pi * 5 ^ 2 => { replace pi by its numerical value } 3.141592653589793 * 5 ^ 2 => { apply exponentiation (^) } 3.141592653589793 * 25 => { apply multiplication (*) } 78.53981633974483
As this shows, to apply or call a function means to replace the lefthand side of its definition by its righthand side. When using GHCi, the results of a function call will then show on the screen.
Some more functions:
double x = 2 * x
quadruple x = double (double x)
square x = x * x
half x = x / 2
Exercises 


Multiple parameters[edit  edit source]
Functions can also take more than one argument. For example, a function for calculating the area of a rectangle given its length and width:
areaRect l w = l * w
*Main> areaRect 5 10 50
Another example that calculates the area of a triangle :
areaTriangle b h = (b * h) / 2
*Main> areaTriangle 3 9 13.5
As you can see, multiple arguments are separated by spaces. That's also why you sometimes have to use parentheses to group expressions. For instance, to quadruple a value x
, you can't write
quadruple x = double double x  error
That would apply a function named double
to the two arguments double
and x
. Note that functions can be arguments to other functions (you will see why later). To make this example work, we need to put parentheses around the argument:
quadruple x = double (double x)
Arguments are always passed in the order given. For example:
minus x y = x  y
*Main> minus 10 5 5 *Main> minus 5 10 5
Here, minus 10 5
evaluates to 10  5
, but minus 5 10
evaluates to 5  10
because the order changes.
Exercises 


On combining functions[edit  edit source]
Of course, you can use functions that you have already defined to define new functions, just like you can use the predefined functions like addition (+)
or multiplication (*)
(operators are defined as functions in Haskell). For example, to calculate the area of a square, we can reuse our function that calculates the area of a rectangle:
areaRect l w = l * w
areaSquare s = areaRect s s
*Main> areaSquare 5 25
After all, a square is just a rectangle with equal sides.
Exercises 


Local definitions[edit  edit source]
where
clauses[edit  edit source]
When defining a function, we sometimes want to define intermediate results that are local to the function. For instance, consider Heron's formula for calculating the area of a triangle with sides a
, b
, and c
:
heron a b c = sqrt (s * (s  a) * (s  b) * (s  c))
where
s = (a + b + c) / 2
The variable s
is half the perimeter of the triangle and it would be tedious to write it out four times in the argument of the square root function sqrt
.
Simply writing the definitions in sequence does not work...
heron a b c = sqrt (s * (s  a) * (s  b) * (s  c))
s = (a + b + c) / 2  a, b, and c are not defined here
... because the variables a
, b
, c
are only available in the righthand side of the function heron
, but the definition of s
as written here is not part of the righthand side of heron
. To make it part of the righthand side, we use the where
keyword.
Note that both the where
and the local definitions are indented by 4 spaces, to distinguish them from subsequent definitions. Here is another example that shows a mix of local and toplevel definitions:
areaTriangleTrig a b c = c * height / 2  use trigonometry
where
cosa = (b ^ 2 + c ^ 2  a ^ 2) / (2 * b * c)
sina = sqrt (1  cosa ^ 2)
height = b * sina
areaTriangleHeron a b c = result  use Heron's formula
where
result = sqrt (s * (s  a) * (s  b) * (s  c))
s = (a + b + c) / 2
Scope[edit  edit source]
If you look closely at the previous example, you'll notice that we have used the variable names a
, b
, c
twice, once for each of the two area functions. How does that work?
Consider the following GHCi sequence:
Prelude> let r = 0 Prelude> let area r = pi * r ^ 2 Prelude> area 5 78.53981633974483
It would have been an unpleasant surprise to return 0
for the area because of the earlier let r = 0
definition getting in the way. That does not happen because when you defined r
the second time you are talking about a different r
. This may seem confusing, but consider how many people have the name John, and yet for any context with only one John, we can talk about "John" with no confusion. Programming has a notion similar to context, called scope.
We will not explain the technicalities behind scope right now. Just keep in mind that the value of a parameter is strictly what you pass in when you call the function, regardless of what the variable was called in the function's definition. That said, appropriately unique names for variables do make the code easier for human readers to understand.
Summary[edit  edit source]
 Variables store values (which can be any arbitrary Haskell expression).
 Variables do not change within a scope.
 Functions help you write reusable code.
 Functions can accept more than one parameter.
We also learned about noncode text comments within a source file.
Notes
 ↑ As this example shows, the names of variables may contain numbers as well as letters. Variables in Haskell must begin with a lowercase letter but may then have any string consisting of letter, numbers, underscore (_) or tick (').