Posted 05 Apr 2013

Expressions vs. statements

If you are entirely new to programming please visit this Wikipedia entry about expressions. The first line contains a couple of terms wich will be our main vocabulary in this post.

3       -- Constant
"Hello" -- Constant
x       -- Variable
y       -- Variable
1 + 4   -- Expression, when evaluated, yields a value (5)

Haskell is an expression based language. There are no statements in it, like in imperative ones. The following example illustrates the difference between an expression and a statement.

// JavaScript
var condition = true
var x
if (condition) {
    x = 3
} else {
    x = 5
-- Haskell
let condition = True
    x = if condition
        then 3
        else 5

As you can see, in Haskell the if expression returns a value, while in JavaScript the if is a statement, and you modify the value of x from a branch of the if. Now, modifying values is something you do rarely in Haskell, because it is a "pure" language. Once you assigned a variable a value, you can never change it. In this sense, these variables are very close to their cousins in math. It is worthy to note that branches of an if expression must return values which has the same type. Thus, the following example causes a type error:

> if True then 1 else "Hello"

    No instance for (Num [Char])
    arising from the literal `1'
    Possible fix: add an instance declaration for (Num [Char])
    In the expression: 1
    In the expression: if True then 1 else "Hello"
    In an equation for `it': it = if True then 1 else "Hello"

The error itself may look scary for a Haskell beginner, but fear not, you will develop an intuition for them pretty soon. Please note that I use the ">" character to indicate the prompt in GHCi.

Function application

Function application may be quite unusual for programmers coming from imperative languages: there are no parentheses, only spaces:

// JavaScript
add(1, 2)
-- Haskell
add 1 2

This allows us to spare a couple of parens. You can evaluate an expression before passing it to a function too:

// JavaScript
add(1, sub(4, 3))
-- Haskell
add 1 (sub 4 3)

In Haskell, expressions are evaluated only when needed, which is called non-strict, or lazy evaluation. The following snippet does not actually raise an error, contrary to the intuition.

let list = [1, 2, error "Hey.", 3]

In a strict language, we could not even store the list, because the evaluation of the error would happen before constructing the list. In Haskell, this is not the case. The error will only be raised if that value will be used. This requires an entirely different mindset to predict the performance characteristics of a program, but at the same time it allows us to glue our existing functions in a more modular fashion.

One of the most enlightening example of the modularity provided by lazy evaluation is the following: imagine that we need to get the smallest element in a list and we have no minimum function, but we have a function which can sort us the list. Now, if we sort the list in ascending order, the first element will be the smallest, but sorting an entire list to only get the first element is entirely wasteful. It is, in strict languages. But in non-strict ones, the sorting will only happen if needed: if we only need the first value, the sorting algorithm will only do the sorting until it can produce that one value we need: it will do no unnecessary work! If that did not blow your mind I suggest you to stand in the corner until you realize how awesome it is.

Defining values

Defining values at top level is easy:

x = 3
y = "Hello"
z = y

Ignoring the precise type of those expression, I think you can guess that x will be a number, y is a string, and z has the same value and type as z.

Function definition

This is the exciting part! We will do actual computation here. The fact is, Haskell is an shamelessly concise language:

That's it! That line will add two numbers. Admittedly not too useful, but hey! We have to start somewhere. Using it is also as easy as ABC:

To venture deeper into the language we will have to befriend the types, but that is a story for an other day.

Previous: Using GHC, GHCi

After installing the Haskell platform we have access to both the Glasgow Haskell Compiler (GHC) and the interpreter, GHCi. Throughout this tutorial series I will use GHCi, because it is interactive and easy to use. Nevertheless, here is how you compile a Haskell program with GHC (for Windows users, we let Mac and Linux users figure out compilation on their own :P) :

Next: Types, type classes

Haskell has an incredibly rich type system which puts to shame almost all other langauges with the possible exception of dependently typed languages, which are under heavy development to become useful languages suitable for practical work (oh, not this practical story again!*).

All posts in the same series:

  1. Introduction
  2. Using GHC, GHCi
  3. Basics
  4. Types, type classes
  5. Lists
  6. Thinking recursively

Hi there! is a multi-author site for tutorials and articles. We support interactive code execution out of the browser, so your tutorial readers can learn without installing anything on their machine!

The website itself is completely open source, send a PR with improvements or your own content here.