Haskell - part 1

Introduction, types and classesMaterial adapted from Erik Meijer’s Functional ProgrammingA Jupyter notebook version of this material is available online in Google Colab. Although Colab does not support Haskell, the notebook can be downloaded and run in Jupyter Notebook, if the IHaskell extension is installed.

Introduction

The software crisis 

Programming languages 

One approach to the software crisis is to design new programming languages that:

Permit rapid prototyping

What is a functional language?

Opinions differ, and it is difficult to give a precise definition, but generally speaking:

Example

Summing the integers 1 to 10 in Java:

             total = 0;
             for (i = 1; i ≤ 10; ++i)
                total = total+i;

The computation method is variable assignment

Example

Summing the integers 1 to 10 in Haskell:

sum [1..10]

The computation method is function application

Historical background

A taste of Haskell

f []     = []
f (x:xs) = f ys ++ [x] ++ f zs
    where
        ys = [a | a <- xs, a <= x]
        zs = [b | b <- xs, b > x]

First steps

2+3*4
(2+3)*4
sqrt (3^2 + 4^2)

The standard prelude

Haskell comes with a large number of standard library functions. In addition to the familiar numeric functions such as + and *, the library also provides many useful functions on lists

-- select the first element of a list:
head [1,2,3,4,5]
-- remove the first element from a list:
tail [1,2,3,4,5]
-- select the nth element of a list:
[1,2,3,4,5] !! 2
-- select the first n elements of a list:
take 3 [1,2,3,4,5]
-- remove the first n elements from a list:
drop 3 [1,2,3,4,5]
-- calculate the length of a list:
length [1,2,3,4,5]
-- calculate the sum of a list of numbers:
sum [1,2,3,4,5]
-- calculate the product of a list of numbers:
product [1,2,3,4,5]
-- append two lists:
[1,2,3] ++ [4,5]
-- reverse a list:
reverse [1,2,3,4,5]

Function application

Examples

Mathematics Haskell
\(f(x)\) f x
\(f(x,y)\) f x y
\(f(g(x))\) f (g x)
\(f(x,g(y))\) f x (g y)
\(f(x)g(y)\) f x * g y

Haskell scripts

Script example

double x    = x + x

quadruple x = double (double x)
quadruple 10
take (double 2) [1, 2, 3, 4, 5, 6]
factorial n = product [1..n]

average ns  = sum ns `div` length ns

Note:

factorial 10

average [1,2,3,4,5]

Naming requirements

Function and argument names must begin with a lower-case letter. For example:

myFun
fun1
arg_2
x’

By convention, list arguments usually have an s suffix on their name. For example:

xs
ns
nss

The layout rule

In a sequence of definitions, each definition must begin in precisely the same column:

a = 10
b = 20
c = 30
a = 10
 b = 20
c = 30
 a = 10
b = 20
 c = 30

The layout rule avoids the need for explicit syntax to indicate the grouping of definitions

-- implicit grouping
a = b + c
    where
        b = 1
        c = 2
d = a * 2

means

-- explicit grouping
a = b + c
    where
        {b = 1;
        c = 2}
d = a * 2

Useful GHCi commands

Command Meaning
:load name load script name
:reload reload current script
:edit name edit script name
:edit edit current script
:type expr show type of expr
:? show all commands
:quit quit GHCi

Exercises

  1. Try out the code covered so far using GHCi.
  2. Fix the syntax errors in the program below, and test your solution using GHCi
N = a ’div’ length xs
    where
        a = 10
       xs = [1,2,3,4,5]
  1. Show how the library function last that selects the last element of a list can be defined using the functions introduced in this lecture.
  2. Can you think of another possible definition?
  3. Similarly, show how the library function init that removes the last element from a list can be defined in two different ways.

Types and classes 

What is a type?

A type is a name for a collection of related values. For example, in Haskell the basic type Bool contains the two logical values: False and True

Type errors

Applying a function to one or more arguments of the wrong type is called a type error

1 + False
-- Error

1 is a number and False is a logical value, but + requires two numbers

Types in Haskell

If evaluating an expression e would produce a value of type t, then e has type t, written

e :: t

Every well formed expression has a type, which can be automatically calculated at compile time using a process called type inference

not False

:type not False

Basic types

Haskell has a number of basic types, including:

Type Description
Bool logical values
Char single characters
String strings of characters
Int fixed-precision integers
Integer arbitrary-precision integers
Float floating-point numbers

List types

A list is sequence of values of the same type:

[False,True,False] :: [Bool]

[’a’,’b’,’c’,’d’]  :: [Char]

In general: [t] is the type of lists with elements of type t

Note:

[False,True]       :: [Bool]

[False,True,False] :: [Bool]
[['a'],['b','c']] :: [[Char]]

Tuple types

A tuple is a sequence of values of different types:

(False,True)     :: (Bool,Bool)

(False,'a',True) :: (Bool,Char,Bool)

In general: (t1,t2,...,tn) is the type of n-tuples whose ith components have type ti for any i in 1...n.

Note:

(False,True)       :: (Bool,Bool)

(False,True,False) :: (Bool,Bool,Bool)
(’a’,(False,’b’)) :: (Char,(Bool,Char))

(True,[’a’,’b’])  :: (Bool,[Char])

Function types

A function is a mapping from values of one type to values of another type:

not     :: Bool → Bool

isDigit :: Char → Bool

In general: t1 → t2 is the type of functions that map values of type t1 to values to type t2

Note:

add :: (Int, Int) -> Int
add (x, y) = x + y
zeroto :: Int -> [Int]
zeroto n = [0..n]

Curried functions

Functions with multiple arguments are also possible by returning functions as results:

add' :: Int -> (Int -> Int)
add' x y = x + y

add' takes an integer x and returns a function add’ x. In turn, this function takes an integer y and returns the result x+y

Note:

add :: (Int, Int) → Int

add' :: Int → (Int → Int)
mult :: Int -> (Int -> (Int -> Int))
mult x y z = x*y*z

mult takes an integer x and returns a function mult x, which in turn takes an integer y and returns a function mult x y, which finally takes an integer z and returns the result x*y*z

Why is Currying useful?

Curried functions are more flexible than functions on tuples, because useful functions can often be made by partially applying a curried function

For example:

add' 1 :: Int → Int

take 5 :: [Int] → [Int]

drop 5 :: [Int] → [Int]

Currying conventions

To avoid excess parentheses when using curried functions, two simple conventions are adopted:

The arrow → associates to the right

Int → Int → Int → Int

Means Int → (Int → (Int → Int))

mult x y z

Means ((mult x) y) z

Unless tupling is explicitly required, ALL functions in Haskell are normally defined in curried form

Polymorphic functions

A function is called polymorphic (“of many forms”) if its type contains one or more type variables

length :: [a] → Int

for any type a, length takes a list of values of type a and returns an integer

Note:

length [False,True] -- a = Bool

length [1,2,3,4]    -- a = Int

For example:

fst  :: (a,b) → a

head :: [a] → a

take :: Int → [a] → [a]

zip  :: [a] → [b] → [(a,b)]

id   :: a → a

Overloaded functions

A polymorphic function is called overloaded if its type contains one or more class constraints

sum :: Num a ⇒ [a] → a

for any numeric type a, sum takes a list of values of type a and returns a value of type a

Note:

sum [1,2,3]       -- a = Int

sum [1.1,2.2,3.3] -- a = Float

sum ['a','b','c'] -- numeric type
Type Description
Num Numeric types
Eq Equality types
Ord Ordered types
(+)  :: Num a ⇒ a → a → a

(==) :: Eq a  ⇒ a → a → Bool

(<)  :: Ord a ⇒ a → a → Bool

Exercises

  1. What are the types of the following values?
['a','b','c']

('a','b','c')

[(False,'0'),(True,'1')]

([False,True],['0','1'])

[tail,init,reverse]
  1. What are the types of the following functions?
second xs     = head (tail xs)

swap (x,y)    = (y,x)

pair x y      = (x,y)

double x      = x*2

palindrome xs = reverse xs == xs

twice f x     = f (f x)
  1. Check your answers using Jupyter notebook, or GHCi.


Back to course home