s5cs50 functional programming
Yet another programming language, but it's OK because we'll be using Python as well as Haskell.

Yet another programming language (and only AQA), but it's OK because we'll be using Python as well as Haskell.
We are learning ...
About the meaning of the term function
How to implement functions in a functional programming language
Applications of functional programming
So that we can ...
State the format of a function as ⨍ : A ↦ B
- A ↦ B is the function type
- A is called the argument type or domain
- B is called the result type or co-domain
- A and B are always subsets of objects
Functions map one set of values (domain) to another set of values (drawn from codomainI have no idea what this means)
Describe a function as a first class objectI have no idea what this means in functional programming
State that a function application means a function applied to it's arguments
State what is meant by partial function application for one, two and three argument functions
State what is meant by composition of functions
Show experience of constructing simple programs in a functional programming language
- Map
- Filter
- Reduce / fold
Higher order functions
Be familiar with representing a list as a concatenation of a head and a tail
- The head is an element of a list
- The tail is a list
- The list can be empty
- Operations (return head, return tail, test for empty list, return length of list, construct empty list, prepend an item to a list, append an item to a list)
- Have experience writing programs for list operations in functional programming language
Application of functional programming to big data / distributed code
Up until now, we have mainly been learning procedural and object-oriented programming. We dabbled a little with a declarative programming language (SQL) but now, we learn the fundamentals of a fully blown declarative language!

We have met functions before. A function is a rule which relates an input to an output. It maps values in a domain to values in a codomain. The values in the codomain to which our function maps are in the range of the function. We met this definition before when we learnt about vectors.


Task 1.1 Maths is fun!
Visit the Maths is Fun website and read through the following pages ...
Make notes and answer questions 1 to 10 at the bottom of each page.
OUTCOME : Notes on functions, domain, range and codomain when applied to functions.


Function type
The domain and the codomain are also related to the function type. The type of a function states the data type of the argument the function requires (the value in the brackets) and the data type it will return. All elements in the domain must be of the same type as must all elements in the codomain.
The type of function f is written as ⨍ : A ⟼ B where A is the type of the domain and B is the type of the codomain.

Task 1.2 Function types
1
⨍ : Integer ⟼ Integer
2
⨍ : Integer ⟼ Real
3
⨍ : Char ⟼ Integer
4
⨍ : String ⟼ Integer
5
⨍ : Real ⟼ Bool
OUTCOME : Examples of function types stated.



Activity 2
Functional programming
Before you start, forget everything you already know about programming (eek!) You don't think imperatively and you don't code imperatively. Everything in functional programming is a function, just like everything in object oriented programming is an object (although the methods you code are imperative!) You have to think of the world as functions. Clearly, applications of functional programming lean towards mathematical problems which require lots of calculations.
In functional programming ...
functions are pure - they only modify their parameters and nothing else which prevents side effects;
functions with no arguments are called definitions (we'd know them as assignment);
useful functions return a value;functions are predictable - they always produce the same outputs given the same inputs;
there are no variables!
there are no loops so we have to use recursion 😄

OK, OK, keep calm ... here comes the maths!
Function application
In mathematics, function applicationI have no idea what this means is the act of applying a function to argument(s), drawn from the domain, A, so as to obtain the corresponding value from its range within the codomain, B.
To define a function to
double
a value, x
, in Python we could type ...def double(x): return x*2
...whereas in a functional programming language, like Haskell, we write it like this ...
double x = x * 2
In imperative languages, function application is denoted using parentheses whereas in functional languages it is denoted using a space. In each case,
x
, is an argument to the function. The function is called double
; its application is double(x)
in Python and double x
in Haskell.Task 2.1 Function application
In your notebooks : Explain the meaning of function application using suitable examples which are different than the examples I have used.
OUTCOME : Your own examples of function and partial function application


Practical functional programming
To learn some functional programming, we're going to use a very widely implemented platform called Haskell.
You may wish to spend a little time on the tutorial that you can access by typing help in the 'Try It' section next to the lambda (λ) symbol (derived from it's history in Lambda calculus) ... see how far you can get before you get confused! To progress further, you can either install Haskell yourself on your own computer or ask your teacher to install it on your lab computers at school.
On Windows, Haskell comes with an interactive tool called WinGHCi or the Glasgow Haskell Compiler (Interactive) into which you can issue Haskell commands interactively or load and execute scripts.

The Glasgow Haskell Compiler (Interactive) running on Windows

Task 2.2 Keep it simple, Haskell!
To be honest, learning a whole new programming paradigm might be a bit too much to ask at this stage. However, I would like you to have a go the first part of the first chapter of the book Learn You a Haskell for Great Good up to but not including 'An intro to lists' - we'll cover that part later.

Use WinGHCi to try out the commands that you are taught. Don't worry about evidencing what you have done but do answer the following questions in your notebooks ...
1
How do you carry out simple mathematical operations using Haskell?
2
What are the symbols for Boolean
AND
and Boolean OR
in Haskell?3
What are the symbols for equality and inequality in Haskell?
4
What does the Haskell
succ
function do?5
What do the Haskell
min
and max
functions do and how many arguments do they take?6
How do you create a simple function in Haskell? Can you give an example?
7
How do you use selection in functions in Haskell? Why is the
else
part mandatory?
I bought a copy, so should you!
OUTCOME : Simple Haskell examples to whet your appetite.

First class objects / values
A first class object / value is any object / value that can ...
appear in an 'expression';
be assigned to or named by a variable / identifier
be stored in a list or a dictionary
be passed as arguments to a function
be returned from a function
... which pretty much covers everything in most modern programming languages. As you can imagine, by these definitions, all simple scalarI have no idea what this means data types such as integer, float, bool and char are first class objects and so are data structures which contain them. In Python, objects also act as first class as we have seen before when we have included objects assigned to variables, stored in lists, passed as parameters to functions or returned from functions.
Task 2.3 First class objects
Your challenge is to come up with one example of either a scalar data type (EASY), a data structure (HARDER) or a simple object (HARDEST) and demonstrate it behaving like a first class object in Python. Present your findings using a suitable combination of screenshots and written explanation.
OUTCOME : A first class object behaving like a first class object.

Lambda functions

lambda
keyword and we can assign them to variables, store them in lists, pass them to functions and return them from functions. Truly 'first class'!
Try using Lambda to create an anonymous function.
>>> lambda x : x*2
<function <lambda> at 0x02FFCC00>
This is clearly a function (because it says so) but we have no way of 'holding' onto the lambda function - it is anonymous and has no identifier! So, let's give it one.
>>> double = lambda x : x*2
>>> double(4)
8
We've created a function called double and we've assigned it straight to a variable (first class) so it acts like a value! Nooo way!! We could have done this ...
>>> def double(x):return x*2
>>> double(4)
8
...so why bother with lambda? Mainly because it's easier and clearer but mainly because it is the only sensible way (not the only way) of including a function inside another function. Seriously, you'll see the benefit of using lambda later on. We can even pass a lambda function as a parameter to another function ...
>>> def fourtimes(function,parameter):
return function(parameter) * 2
>>> fourtimes(lambda x: x*2, 4)
16

Here, the function
fourtimes
is behaving as a higher order function - one which takes another function as a parameter. The most common (built in) higher order functions are map
, filter
and reduce
/ fold
. We'll meet these later. What about this?
>>> def exponent(n): return lambda m : m**n
>>> exponent(3)
<function exponent.<locals>.<lambda> at 0x02FFCD20>
So, the function exponent returns an actual function - this is clear (and totally first class). The function
exponent
is also behaving in a higher order fashion as it is returning a function. We certainly can't do this with normal functions (in Python at least).If you think about it, if
exponent
returns a function rather than a value then the function has not been fully applied This is known, unsurprisingly, as partial function application - the function exponent has been partially applied and is waiting for it's missing argument, m
.We can complete the application of the function by using it to create other functions, like this ...
>>> cube = exponent(3)
>>> cube(2)
8
>>> cube(3)
27
Wow! Like, wow! The function
cube
, completed the application of the function exponent by providing the final argument that it needs. Nice!Finally, let's create a list of anonymous functions and access them like value 🤯 to complete our proof of first classness!
>>> myfunctions = [lambda n:n*1,lambda n:n*2,lambda n:n*4]
>>> myfunctions[0](2)
2
>>> myfunctions[1](2)
4
>>> myfunctions[2](2)
8
Here, we've seen lambda functions behaving in a truly first class fashion. Nice.

So is mine!
Task 2.4 Lambda functions
Anonymous lambda functions are somewhat of a mystery to most beginning Python programmers, but not you! Try the following exercises, providing evidence of what you have done through a suitable combination of screenshots and written explanation.

Create a lambda function,
half
, which returns half of it's argumentCreate a lambda function,
reciprocal
, which returns the reciprocal of it's argumentCreate a new function,
polynomial
, which returns the value of 2x2+3x-4Using the existing function,
exponent
, create a new lambda function, squareroot
, which returns the square root of it's argument;Create a new function,
incrementer
, with one argument, n
, which returns a lambda function taking another argument, m
, which returns m + n
. Use this function to create two more lambda functions, successor
and predecessor
.Finished? Have a quick read through 'Yet Another Lambda Tutorial' - are you a convert?
OUTCOME : Some groovy lambda functions.


Luckily, if we use Haskell, we don't generally have to use lambda functions for simple operations like this because in Haskell, the way that functions are defines is quite lambda like anyway. The safest way to conduct the next set of exercises is to use a script rather than the interactive console. Often Haskell requires you to be more explicit about the function types that you are defining and you can't define function types in Haskell at the interactive prompt 😔

Using your favourite text editor, create the a script called activity2.hs and save it in a suitable place. Make sure that the file extension is correct and close down your text editor. Now open up WinGHCi (if you haven't done already) and choose 'File > Load' and load in the script you have created. These are the two toolbar buttons you'll need next ...

Once the file is loaded, hit the 'Run Text Editor' button and Notepad should appear, as if by magic. Use WinGHCi and the default text editor to carry out the following tasks. Edit the script in the text editor, click save and then click the 'Reload Script' button before executing the commands given at the interactive prompt. Here we go ...
We can define the same double function in Haskell like this ...
In the text editor ... (save)
-- Define double function
double :: Integer -> Integer
double x = x * 2
In WinGHCi ... (reload file and execute)
*Main> double 4
8
In the text editor, you declare the function type explicitly so that Haskell knows what data type to expect and what data type it's meant to return. We can then use this double function in another function ...
In the text editor ... (save)
-- Define double function
double :: Integer -> Integer
double x = x*2
-- Define quadruple function
quadruple :: Integer -> Integer
quadruple x = double (double x)
In WinGHCi ... (reload file and execute)
*Main> quadruple 4
8
This is a very simple example of a very common pattern you see in Haskell where basic functions are designed and combined to create more complex functions. Partial function application in Haskell is also straightforward. The exponent examples we saw before is easy but we need to make sure we declare a specific function type to allow us to perform all the operations we did with Python before.
In the text editor ... (save)
-- Define double function
double :: Integer -> Integer
double x = x*2
-- Define quadruple function
quadruple :: Integer -> Integer
quadruple x = double (double x)
-- Define exponent' function
exponent' :: Float -> Float -> Float
exponent' x y = x ** y
In WinGHCi ... (reload file and execute)
*Main> exponent' 2 3
9.0
*Main> cube = exponent' 3
*Main> cube 2
8.0
*Main> cube 3
27.0
Notice the the prime (') character after the
exponent'
function name. The prime has no special meaning in this context (although it is used to prevent laziness in functions when doing fierce recursion) and hence we can use it in function names. There is already a function built into Haskell called exponent so adding the prime after our function denotes that it's a slightly modified version. This example won't work without it being there because Haskell will try to use the built in function.
For the next section, unload the script by typing
:m
at the *Main>
prompt and pressing ENTER and you should have your Prelude>
prompt back again. Lambda functions in Haskell are declared similarly to the way they are in Python but since they are generally used 'on the fly', we'll define them at the interactive prompt rather than in a script. Lambdas are prefixed with a back slash (\) because it looks like a Greek letter lambda (λ), I suppose.Prelude> multiplier = \x n -> x*n
Prelude> doubler = multiplier 2
Prelude> doubler 4
8
Prelude> tripler = multiplier 3
Prelude> tripler 4
12
Prelude> halver = multiplier 0.5
Prelude> halver 4
2.0
Don't worry too much if you aren't really following this (I wouldn't be surprised). Later on when we look at the main higher order functions
map
, filter
and reduce
/ fold
, you'll see how these can be used in context.
Task 2.5 Haskell functions
Using the text editor and Haskell, your task is to try to construct some of the functions as you did using Python in Task 2.4. This will help you to practice constructing explicit function types in Haskell (which is like the ones you'll see on the examination!)

Create a Haskell function,
half
, which returns half of it's argumentCreate a Haskell function,
reciprocal
, which returns the reciprocal of it's argumentCreate a Haskell function,
polynomial
, which returns the solution to 2x2+3x-4Using the existing Haskell function,
exponent'
(notice the prime), create a new Haskell function, squareroot
, which returns the square root of it's argumentCreate a new function,
incrementer
, which returns a lambda function taking two arguments, m
and n
, which returns m + n
. Use this function to create two more lambda functions, successor
and predecessor
. (HINT : You have to surround negative numbers with brackets or else Haskell gets confused)OUTCOME : Some groovy lambda functions.



Yeah, right

Activity 3
Map, filter and fold
The three most common functions which facilitate a functional approach to programming in Python are
map
, filter
and reduce
. These three higher order functions form the backbone of most applications of functional programming in the field of big data, and they also make extensive use of the anonymous lambda functions we've just been learning about in the last activity (which is why we learnt about them!)
Ok, OK - here we go...
Map
The map function takes a list of values from a domain and applies a function to each value in the list, mapping the value to a range in the subdomain (remember them?)

The map function using a lambda function

The map function is built into the standard Python library so it's always available (noice). The blueprint is ...
map(function, *iterable) --> map object
The
map
object can be converted into list using the list(object)
function. The function passed to map
can be a built-in Python function. For instance, this map
operation converts a string to a list of ASCII codes.>>> list(map(ord,'Hello there!'))
[72, 101, 108, 108, 111, 32, 116, 104, 101, 114, 101, 33]
... and this one converts a list of numbers into a list of strings.
>>> list(map(str,[1,2,3,4,5]))
['1', '2', '3', '4', '5']
If the function takes more than one parameter, simply pass
map
multiple lists. For instance, for the power function, pow(x,y)
which takes two arguments (and is equivalent to x**y
) can be applied using a map function across two lists of values ...>>> list(map(pow,[5,4,3,2,1],[1,2,3,4,5]))
[5, 16, 27, 16, 1]
Consider this mathematical delight ...
>>> import math
>>> list(map(math.sin,[34,54,65]))
[0.5290826861200238, -0.5587890488516163, 0.8268286794901034]
As you can imagine, you can also put lambda functions in the
map
function to achieve more complex functional application. For example a simple map
function which squares every value in a list ...>>> list(map(lambda x:x**2, [1,2,3,4,5]))
[1,4,9,16,25]
It's common to separate the function from the map operation, which would be especially useful if the function was more complicated. For instance ...
>>> double = lambda x:x**2
>>> list(map(double,[1,2,3,4,5]))
[1,4,9,16,25]
We can also design a map function which capitalises every letter in a string.
>>> ''.join(list(map(lambda x:chr(ord(x)-32),'hello')))
'HELLO'
The list is literally endless ...

Task 3.1 Map in Python
Write map functions for the following situations. I've given you a hint on the first four ...
Generate a list of the lengths of names in this list
names = ['Charles','Andrew','Bob','Leanne','Beatrice']
;Generate a list of the hex codes for
[85,18,171,255,0]
;Generate a list of the types for
[1,True,'Hello',12.44]
;Generate a list of the absolute values for
[-5,12,-6,10,-7,-1]
;Use a suitable lambda function to generate a list of the successors to
Use a suitable lambda function to add together the two vectors [1,2,3,4,5]
.[3,5,4]
and [1,3,4]
.
OUTCOME : Evidence that you can use the map function in context.


It's worth noting that the result of the map function can be achieved using list comprehensions. For instance, both these statements are equivalent ...
>>> list(map(lambda x:x+1,[1,2,3,4,5]))
[2,3,4,5,6]
>>> [x+1 for x in [1,2,3,4,5]]
[2,3,4,5,6]

Neither!

In Haskell, the map function is structured like this ...
map (function) [list]
The function can be a simple partial application like
(+1)
or a lambda function like (\x -> x + 1)
. Both these functions are equivalent. Look ...Predule> map (+1) [1,2,3,4,5]
[2,3,4,5,6]
Prelude> map (\x -> x+1) [1,2,3,4,5]
[2,3,4,5,6]
Haskell has many built in functions which we can use in the map function. For instance ...
Prelude> map (odd) [1,2,3,4,5]
[True,False,True,False,True]
Unfortunately, you can only provide one list as an argument to the Haskell
map
function, unlike Python, so you have to engage in some pretty funky tricks to get it to handle more complex situations where you might want to give the function multiple parameters.Prelude> zip [5,7,6] [1,4,5]
[(5,1),(7,4),(6,5)]
The
zip
function takes two lists and creates a single list of tuples (you'll recognised the difference between lists and tuples from Python, I'm sure). Then, we can throw our list of tuples into the map
function to do some groovy things.Prelude> map (\(x,y) -> abs(x-y)) (zip [5,8,3] [6,6,6])
[1,2,3]
Prelude> map (\(x,y) -> maximum [x,y]) (zip [4,6,8] [1,2,9])
[4,6,9]
The first example generates a list of the absolute difference between related values in the lists and the second example generates a list of the maximum related values from the lists. Nice.

Task 3.2 Map in Haskell
Write map functions for the following situations in Haskell. I've given you a hint on some of them ...
Generate a list of the lengths of names in this list
names = ["Charles","Andrew","Bob","Leanne","Beatrice"]
;Generate a list of the even Bools (i.e. true/false) for
[1,2,3,4,5]
;Generate a list of the absolute values for
[-5,12,-6,10,-7,-1]
;Generate a list of the predecessors to
[1,2,3,4,5]
. Show two different ways of accomplishing this; one using a partial function and the other using a lambda function.Use a suitable lambda function and the
zip
operator to add together the two vectors [3,5,4]
and [1,3,4]
.Use a suitable lambda function and the zip operator to find generate a list containing the minimum values of the corresponding values in
[4,6,8
] and [1,2,9]
.
OUTCOME : Evidence that you can use the map function in context.


Once again, it's worth noting that the same can be achieved using list comprehensions. We haven't looked at list comprehensions in Haskell yet (and neither are we going to), however, you should be able to apply simple patterns using these examples. Notice the left facing '
<-
' which means 'is drawn from'.Prelude> [x+1 | x <- [1,2,3,4,5]]
[2,3,4,5,6]
Prelude> [maximum [x,y] | (x,y) <- (zip [4,6,8] [1,2,9])
[4,6,9]

You've already asked me that!
Filter
The
filter
function takes a list of values and applies a conditional filter to it using a lambda function. The value from the list will be returned if, and only if, the result of the function is true
.
The filter function through the application of a lambda function

The filter function is also built into the Python standard library, so it's always available. The blueprint is ...
filter(function, iterable) --> filter object
Where the function is a conditional statement which returns
true
or false
. Common uses include, extracting even numbers from a list ...>>> list(filter(lambda x:x%2==0,range(1,21))
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
... extracting odd numbers from a list ...
>>> list(filter(lambda x:x%2==1,range(1,21)))
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
You can truncate a list based in the value ...
>>> list(filter(lambda x:x>0,range(-5,5))]
[1,2,3,4]
... and even use string operations to search for matching values in a list or words.
>>> names = ['Anne', 'Amy', 'Bob', 'David', 'Carrie', 'Barbara', 'Zach']
>>> list(filter(lambda s: s.startswith('B'), names)
['Bob', 'Barbara']
>>> list(filter(lambda s: 'a' in s.lower(), names)
['Anne', 'Amy', 'David', 'Carrie', 'Barbara', 'Zach']


Task 3.3 The filter function in Python
Use a combination of
list
, filter
and lambda
to answer the following problems ...Write a
filter
function which returns every third item from a list of values from 12 to 57, inclusive;Write a
filter
function which returns only items of type bool
from the list [1,2,True,3,4,False]
. You can use the function body type(x) is bool in the lambda function to achieve this.Declare a list of names of various length ...
names = ['Odis','Jovan','Tinisha','Gerda','Ayesha','Dorsey','Elton','Laila','Petronila']
... and write a
filter
function which only contains names over 6 characters in length.OUTCOME : Various applications of the filter function


It is worth noting that you can achieve pretty much the same outcomes using list comprehensions. For instance, we can generate our list of even and odd numbers like this ...
>>> [x for x in range(1,21) if x%2 == 0]
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
>>> [x for x in range(1,21) if x%2 == 1]
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
... which some programmers find easier / clearer.

This is getting silly. Stop asking me!
In Haskell, we use the
filter
function in a similar way. This is the blueprint ...filter (predicate) [list]
... where the
predicate
is a conditional statements which returns true
or false
and is applied to each element of the list. If the predicate is true
, the list value is returned, if it is false
, it is not. The filter
function returns a list of values which satisfy the predicate.We can filter out all odd values from a list like this ...
Prelude> filter (even) [1,2,3,4,5]
[2,4]
Notice the result of the application of even in this case is very different to that of its use in the
map
function. The predicate can look like part of a conditional statement, as in these examples ...Prelude> filter (>0) [-5..5]
[1,2,3,4,5]
Prelude> filter (==3) [-5..5]
[3]

... but perfectly valid as long as the ranges are predictable!
Both these predicates are statements not functions (technically,
>
and ==
are functions but we'll ignore that for now) and take parameters in a prefix fashion, i.e. before the operator. So >0
is like x>0
. So, these are equivalent to the following filter operations using lambda functions ...Prelude> filter (\x -> x>0) [-5..5]
[1,2,3,4,5]
Prelude> filter (\x -> x==3) [-5..5]
[3]
Once again, it's worth noting that we can use list comprehensions in Haskell to achieve the same outcome ...
Prelude> [x | x <- [-5..5], x>0]
[1,2,3,4,5]
Prelude> [x | x <- [-5..5], x==3]
[3]

No, no no!
We could define a slightly more complicated (and totally pointless) even number filter in three slightly different ways (just to prove some points and introduce some other points) ...
a) Using the built in even function as the predicate. We've seen this before 😄
Prelude> filter (even) [1..10]
[2,4,6,8,10]
b) Using a lambda function. This uses the Haskell mod operator which prefixes it's arguments.
Prelude> filter (\x -> mod x 2 == 0) [1..10]
[2,4,6,8,10]
c) Using a lambda function. I've modified the mod operator to an infix operator by surrounding it with backticks.
Prelude> filter (\x -> x `mod` 2 == 0) [1..10]
[2,4,6,8,10]
d) Defining a separate function and using that as the predicate. Sometimes better, sometimes not.
Prelude> evens x = mod x 2 == 0
Prelude> filter (evens) [1..10]
[2,4,6,8,10]
e) Using function composition. You should have met function composition in Task 1.1...
Prelude> filter ((==0).(`mod` 2)) [1..10]
[2,4,6,8,10]
The predicate is evaluated from right to left. For each value in the list, the result of the
mod
operation is passed to the ==0
statement. If the output of that is True
, the value is added to the filtered list. 1 : 1 `mod` 2 = 1 -> 1 == 0 -> False
2 : 2 `mod` 2 = 0 -> 0 == 0 -> True -> Add 2
3 : 3 `mod` 2 = 1 -> 1 == 0 -> False
4 : 4 `mod` 2 = 0 -> 0 == 0 -> True -> Add 4
5 : 5 `mod` 2 = 1 -> 1 == 0 -> False
6 : 6 `mod` 2 = 0 -> 0 == 0 -> True -> Add 6
7 : 7 `mod` 2 = 1 -> 1 == 0 -> False
8 : 8 `mod` 2 = 0 -> 0 == 0 -> True -> Add 8
9 : 9 `mod` 2 = 1 -> 1 == 0 -> False
10 : 10 `mod` 2 = 0 -> 0 == 0 -> True -> Add 10

Even though we learnt that the open circle (∘) represents function composition, there is no open circle available to show this in Haskell (or any other language), so the dot is used. Sorry 😔
Task 3.4 The filter function in Haskell
Use the
filter
function from Haskell to solve the following problems ...Write a
filter
function which returns every odd number from the list [1..10]
;Write a
filter
function which returns every third item from a list of values from 12 to 57, inclusive. You should do this in three different ways, using a lambda
function, a separate function, threes
and using function composition. Declare a list of names of various length ...
names = ["Odis","Jovan","Tinisha","Gerda","Ayesha","Dorsey","Elton","Laila","Petronila"]
... and write a filter function to generate a list which only contains names over 6 characters in length. You should do this using a
lambda
function, a separate function, long
, and using function composition.OUTCOME : Various applications of the filter function in Haskell.

Reduce / Fold
Finally, the
reduce
(or fold
) function completes our toolkit of standard functional programming tools. Unfortunately, the reduce
function is not built into the standard Python library so we need to import it ...from functools import reduce
...before we can use it. Basically, reduce takes a list and, through the application of a function, reduces the list to a single value.

The reduce operation using a lambda function

The
reduce
function is not built into the Python standard library, so it requires importation before we can use it. The blueprint is ...reduce(function, iterable[, initialiser])
The function takes two arguments due to the way it operates on the list. Consider this simple
reduce
operation which adds up a list of numbers.>>> reduce(lambda x,y:x+y,[1,2,3,4,5],0)
15
The operation of
reduce
is essentially (((((0+1)+2)+3)+4)+5)
folding from the left side of the list (technically a left fold). The fold operation is recursive. Starting with the (optional) initialiser, the function adds this to the first item in the list, then adds the result of this addition to the second value in the list and so on until only one value is left which it returns.You can also use
reduce
to work out the maximum number in a list of values.>>> reduce(lambda a,b:a if a>b else b, [45,12,102,14,9])
102
The
reduce
function also works with strings.>>> names = ['British','Broadcasting','Corporation']
>>> reduce(lambda a,b:a+b[0],names,'')
'BBC'
Notice that the initialiser for this operation is an empty string. Try it without that and see what happens (lol).

Task 3.5 Reduce
Use a combination of
reduce
and lambda
to solve the following problems.Write a
reduce
function which finds the product of [1,2,3,4,5]
;Write a
reduce
function which finds the minimum number in the list [45,12,102,14,9]
;Write a
reduce
function to calculate the dot product of [12,6,6]
and [13,12,10]
;Write a
reduce
function which generates an acronym for the North Atlantic Treaty Organisation.
OUTCOME : Applications for the reduce function


There is no reduce function in Haskell 🙄; it's called
fold
. In fact there are two different fold functions in Haskell, foldl
(fold left) and foldr
(fold right). They both operate recursively eating up values from the left hand side and the right hand side of the list respectively.The blueprint for the functions is ...
foldl (function) initialiser [List]
foldr (function) initialiser [List]
The initialiser gives a special variable called the accumulator its initial value. The
foldl
function recursively adds the accumulator to each item in the list whereas the foldr
function recursively adds each item in the list to the accumulator (i.e. the reverse). READ THAT AGAIN.Here is a nice way of looking at both situations based on a common food chain.

Demonstrating a left fold
In a left fold, the food chain is accumulated from the owl to the grass; from left to right. The owl eats the mouse, then the owl (which has eaten the mouse) eats the grasshopper and finally the owl (which has eaten the mouse and the grasshopper) eats the grass.

Demonstrating a right fold
In a right fold however, the food chain is accumulated from the grass to the owl; from right to left. The grasshopper eats the grass then the mouse eats the grasshopper (which has eaten the grass) then the owl eats the mouse (which has eaten the grasshopper which has eaten the grass). In any event, the owl is full up.
Enough about food. Consider these two real examples ...
Prelude> foldl (+) 0 [1,2,3,4,5]
15
In essence, the
foldl
evaluates as (((((0+1)+2)+3)+4)+5) = 15
Prelude> foldr (+) 0 [1,2,3,4,5]
15
In essence, the
foldr
evaluates as (1+(2+(3+(4+(5+0))))) = 15
In this instance, the order of the fold makes no difference to the result since addition is commutative (order is unimportant). However, subtraction is not and the following two functions yield very different results.
Prelude> foldl (-) 0 [1,2,3,4,5]
-15
Prelude> foldr (-) 0 [1,2,3,4,5]
3
Why does this happen? Let's evaluate the expression manually to find out ...


Task 3.6 Folding with Haskell
Use a the Haskell
foldl
function to solve the following problems.Write a
foldl
function which finds the product (*) of [1,2,3,4,5]
. (Initialiser : 1);Write a
foldl
function which concatenates (++) the list ["H","E","L","L","O"]
. (Initialiser : "").
OUTCOME : Applications for the fold function



Do not attempt this section unless you are ready for a challenge!
This situation gets even more complicated with function composition and we are really getting way out of our depth here! However, when we were using Python, we managed to generate an acronym for the British Broadcasting Corporation using a Lambda function. However, the same process in Haskell is slightly more confusing (but I feel duty bound to tell you about it). The
foldl
and foldr
functions evaluate composed functions in the reverse order so, to get the same outcome, we have ...Prelude> foldl (\acc a -> acc ++ take 1 a) "" ["British","Broadcasting","Corporation"]
"BBC"
Prelude> foldr (\a acc -> take 1 a ++ acc) "" ["British","Broadcasting","Corporation"]
"BBC"
However, trying to use normal function composition causes problems ...
Prelude> foldl ((++).(take 1)) "" ["British","Broadcasting","Corporation"]
"BCorporation"
Prelude> foldr ((++).(take 1)) "" ["British","Broadcasting","Corporation"]
"BBC"
The
foldl
function evaluates like this :(take 1 (take 1 (take 1 "" ++ "British") ++ "Broadcasting") ++ "Corporation)
(take 1 (take 1 "British" ++ "Broadcasting") ++ "Corporation)
(take 1 "BBroadcasting" ++ "Corporation)
"BCorporation"
The
foldr
function evaluates like this :(take 1 "British" ++ (take 1 "Broadcasting" ++ (take 1 "Corporation")))
(take 1 "British" ++ (take 1 "Broadcasting" ++ "C"))
(take 1 "British" ++ "BC")
"BBC"
... which explains it all, quite clearly!

... and no task!

Activity 4
List operations
Back to Haskell for this last section and the fabulous 'Learn You a Haskell for Great Good' - my favourite book!
Task 4.1 Learn me a Haskell
Go back onto the web version of Learn You a Haskell for Great Good and continue working through the first part of the first chapter up to but not including 'Texas Ranges'.

Again, use WinGHCi to carry out the exercises in the book, reading the explanations carefully. Don't worry about evidencing your progress but do answer the following questions in your notebooks.
How do you define a name in Haskell?
What is the difference between the
++
operator and the :
operator?Describe how you would prepend and append an item to an existing list.
What does
[[],[],[]]
mean?Write a Haskell command to retrieve the 4th item from the list
[6,5,4,3,2]
.How do list comparisons work in Haskell?
Draw a picture to represent the
head
, tail
, init
and last
operations (don't use a caterpillar).Describe the purpose of the following list functions ...
-
length
-
null
-
reverse
-
take
-
drop
-
maximum
-
minimum
-
sum
-
product
-
elem
OUTCOME : Descriptions of common list operations



Hello little caterpillar!
Extension Activities
How about these?
Visit Haskell's documentation page to find a list of resources to help you learn Haskell.
Read and enjoy 'Learn you a Haskell for Great Good!' - Holy Sh*t!
Read this series of interesting articles on how to be a functional programmer.
What's next?
Before you hand your book in for checking, make sure you have completed all the work required and that your book is tidy and organised. Your book will be checked to make sure it is complete and you will be given a spicy grade for effort.
Before you hand your book in for checking, make sure you have completed all the work required and that your book is tidy and organised. Your book will be checked to make sure it is complete and you will be given a spicy grade for effort.
END OF TOPIC ASSESSMENT
Last modified: February 14th, 2024