COS 333: Chapter 15, Part 1

Willem van Heerden
26 Aug 202056:44

Summary

TLDRThe lecture introduces the shift in focus to chapters 15 and 16 on functional and logic programming languages, emphasizing their significance in exams due to heavy weighting. It delves into functional programming, highlighting its theoretical underpinnings with mathematical functions, the avoidance of side effects for referential transparency, and the pure functional approach eschewing variables. The discussion transitions to LISP and Scheme, exploring their history, basic constructs, and function applications, setting the stage for deeper exploration in subsequent lectures.

Takeaways

  • ๐Ÿ“š The course is focusing on chapters 15 and 16 of the textbook, which cover functional programming languages and logic programming, as these are heavily emphasized in exams.
  • ๐Ÿ”„ The professor has reordered the chapters to allow more time for practicals and lectures on the critical chapters, enhancing understanding and preparation for assessments.
  • ๐ŸŽฏ Students are urged to concentrate on the material in chapters 15 and 16, as they carry significant weight in the grading for both semester tests and the final exam.
  • ๐Ÿ’ป Practical tasks, especially coding implementations similar to examples in chapters 15 and 16, are crucial for exam preparation and should be thoroughly practiced.
  • ๐Ÿ”— Chapter 15 references earlier chapters (5-8), and students are advised to consult both the textbook and the slides for a comprehensive understanding.
  • ๐Ÿšซ The entire Chapter 15 will not be covered; the focus will be on specific functional programming languages: Lisp and Scheme.
  • ๐Ÿ’ก Functional programming is based on mathematical functions, aiming to provide a more natural and theoretical approach to programming compared to imperative programming languages.
  • ๐Ÿ”— In functional programming, the concept of 'side effects' is generally discouraged as it introduces ambiguity and complexity in program execution.
  • ๐ŸŒ Referential transparency, a property where equivalent expressions can be substituted without affecting program behavior, is a key advantage of functional programming languages.
  • ๐Ÿ”‘ Scheme, a dialect of Lisp, is emphasized for its simplicity and modernity, and is the primary focus for the course's functional programming section.
  • ๐Ÿ“ Scheme uses a universal function 'eval' for interpreting expressions, demonstrating the power and flexibility inherent in functional programming languages.

Q & A

  • Why has the order of chapters in the textbook been changed for the lectures?

    -The order of chapters has been changed to allocate more lecture time and practical work to chapters 15 and 16, which are heavily emphasized in the semester tests and the exam.

  • What is the significance of focusing on chapters 15 and 16 in the course?

    -Chapters 15 and 16 cover functional programming languages and logic programming, which are important due to their significant weight in the exams and their practical applications.

  • What is the expectation for students regarding the practical tasks related to chapters 15 and 16?

    -Students are expected to complete all practicals related to these chapters, as the ability to implement code similar to examples given in these chapters may be required in exams.

  • How does the reference to earlier chapters in chapter 15 impact the preparation for the lectures?

    -Students should refer to both the textbook and the slides when preparing for chapter 15, as the slides provide additional information on topics from earlier chapters that are referenced.

  • Why are only certain topics within chapter 15 being covered in the lectures?

    -The lectures will focus only on Lisp and Scheme from chapter 15 because of their foundational importance in functional programming languages.

  • What is the fundamental concept behind functional programming languages?

    -Functional programming languages are based on mathematical functions, aiming to provide a more natural and mathematical way of expressing programs.

  • What is the primary concern for imperative programming languages?

    -The primary concern for imperative programming languages is efficiency, as they are designed to reflect the computing hardware they were made for.

  • What is a functional side effect and why are they generally considered bad?

    -A functional side effect is any change outside the scope of the function, such as modifying a parameter by reference or a global variable. They are considered bad because they lead to ambiguity in program execution.

  • What are the two approaches to solving the problem of ambiguity introduced by functional side effects?

    -The two approaches are disallowing functional side effects entirely, which increases language purity but reduces flexibility, and demanding a fixed operand evaluation order, which can limit compiler optimizations.

  • What is referential transparency and why is it important in programming?

    -Referential transparency means that equivalent expressions can be substituted for one another without affecting the program's behavior. It is important because it simplifies program semantics and eliminates ambiguity.

  • What is the difference between a pure functional programming language and an imperative one in terms of variable usage?

    -A pure functional programming language does not support variables at all, eliminating global variables and parameter modifications, whereas imperative programming languages rely heavily on variable management.

  • Why is the lack of support for variables in a pure functional programming language important?

    -The lack of support for variables ensures the elimination of functional side effects and state-associated functions, enforcing referential transparency and simplifying program semantics.

  • What are the two types of atoms in Lisp and how do they differ?

    -The two types of atoms in Lisp are symbols and numeric literals. Symbols are identifiers or names for concepts, while numeric literals are simply numeric values.

  • What is the significance of the list notation in Lisp and how is it used?

    -List notation in Lisp is used to represent both data structures and function applications. It allows for an ambiguous but powerful representation where the same list can be interpreted as data or a function call.

  • How does the lambda notation work in Lisp and what is its purpose?

    -Lambda notation in Lisp is used to define nameless functions. It specifies a set of parameters and an expression that defines what the function does, allowing for the creation of functions that can be used immediately where they are defined.

  • What is the relationship between Scheme and Lisp?

    -Scheme is a dialect of Lisp that was developed to be a cleaner, more modern, and simpler version of Lisp. It retains the same basic atoms and lists as Lisp but introduces other features and improvements.

  • How does the eval function work in Scheme and why is it significant?

    -The eval function in Scheme evaluates expressions. It is significant because it allows for the Scheme interpreter to be used as a function within the programming language, enabling powerful meta-programming capabilities.

  • What are the differences between the evaluation process for the define function and standard primitive functions in Scheme?

    -The define function in Scheme does not evaluate its first parameter as standard functions do. This is because the first parameter is typically a name that has not been bound yet, and evaluating it would result in an error.

  • Why are explicit input and output considered theoretical anomalies in pure functional programming languages?

    -Explicit input and output are considered anomalies in pure functional programming languages because input operations change the state of the program, and output operations can be seen as side effects, both of which contradict the stateless nature of pure functional programming.

  • How do arithmetic operations work in Scheme and what functions are provided for them?

    -In Scheme, all arithmetic operations are implemented as functions. The language specification provides a set of primitive arithmetic functions such as addition (+), subtraction (-), multiplication (*), division (/), and others like absolute value, square root, remainder, and min/max functions.

  • What is the purpose of the define function in Scheme and how does it differ from variable assignment in imperative languages?

    -The define function in Scheme is used to bind a name to a value or a lambda expression. It differs from variable assignment in imperative languages because once a name is bound to a value in Scheme, it cannot be rebound to a different value, making it more like a constant than a variable.

  • What are the built-in output functions provided by Scheme and how are they typically used?

    -Scheme provides built-in output functions like display, which prints the result of an evaluated expression, newline, which prints a new line character, and print, which allows formatted output. However, these functions are often not used directly, as the interpreter's normal behavior is to print the result of any top-level function application.

Outlines

00:00

๐Ÿ“š Course Reordering and Emphasis on Functional Programming

The lecturer announces a change in the course syllabus to focus on chapters 15 and 16, which cover functional programming languages and logic programming, respectively. This reordering is strategic to allocate more time for these chapters, as they carry significant weight in both semester tests and the final exam. The emphasis is on Lisp and Scheme, the first functional programming languages, with a brief introduction to functional programming concepts and their theoretical underpinnings. The lecturer also clarifies that while chapter 15 refers to earlier chapters, necessary topics are expanded upon in slides and audio lectures to ensure comprehensive coverage.

05:02

๐ŸŒ Theoretical Foundations of Functional Programming

This section delves into the theoretical aspects of functional programming, contrasting imperative programming languages that mirror hardware architecture with functional languages grounded in mathematical functions. Functional programming is characterized by a solid theoretical foundation and a user-centric design that prioritizes natural mathematical expression over machine efficiency. The concept of 'function' in programming is explored, including parameterized computations and the avoidance of side effects for clarity and unambiguity in programming.

10:02

๐Ÿ”„ Ambiguity and Side Effects in Functional Programming

The potential for ambiguity in functional programming due to side effects is discussed. Side effects occur when a function alters a parameter by reference or modifies a global variable, leading to unpredictable outcomes based on the order of operand evaluation. Two solutions are presented: disallowing functional side effects to enhance language flexibility or enforcing a fixed operand evaluation order, which may limit compiler optimizations.

15:04

๐Ÿ”— Referential Transparency and Its Significance

Referential transparency, a property where equivalent expressions can be substituted without affecting program behavior, is introduced. The breakdown of referential transparency due to side effects or differing function call outcomes is illustrated through examples. The advantage of referential transparency is its contribution to more comprehensible program semantics, which is particularly beneficial in functional programming languages.

20:06

๐Ÿ› ๏ธ Pure Functional Programming and Its Principles

The concept of a pure functional programming language is introduced, where variables are entirely absent, eliminating functional side effects and state-associated complexities. Pure functional languages enforce referential transparency, simplifying program understanding. The fundamental difference between computation in imperative and functional programming is highlighted, with the latter avoiding variable management and associated ambiguities.

25:07

๐ŸŒŸ Introduction to the LISP Programming Language

The lecture provides an overview of LISP, the first functional programming language, focusing on its data objects and structures, namely atoms and lists. LISP's original typeless nature and its use of symbols and numeric literals are discussed. The notation for lists and function applications, which share a common list form, is explained, setting the stage for more advanced concepts in Scheme, a dialect of LISP.

30:10

๐Ÿ“ Function Definitions and Evaluation in LISP

This section covers how functions are defined in LISP using lambda notation, creating nameless functions applied to a set of arguments. The use of lambda expressions for both unnamed and named functions is demonstrated, highlighting the flexibility of LISP's functional approach. The historical context of LISP as a universal programming model is also briefly mentioned.

35:13

๐Ÿš€ Scheme: A Modern Dialect of LISP

Scheme is introduced as a cleaner, simpler, and more modern dialect of LISP, developed in the 1970s. It retains LISP's atoms and lists but adopts static scoping and treats all elements as functions, including operators and statements. Scheme's function-first nature is emphasized, with functions being first-class entities that can be passed and returned from other functions, enabling the creation of meta-programming techniques.

40:14

๐Ÿ“Š Evaluating Expressions and Arithmetic in Scheme

The evaluation process in Scheme is detailed, explaining how literals evaluate to themselves and function applications are handled by evaluating parameters without regard to order due to the absence of side effects. Arithmetic operations are presented as functions, with Scheme providing a set of primitive arithmetic functions that can be applied to multiple parameters, adhering to the functional programming paradigm.

45:14

๐ŸŽ›๏ธ Lambda Expressions and Function Application in Scheme

Lambda expressions in Scheme are explored, allowing the definition of nameless functions with parameters and a body that determines the function's result. The process of applying lambda expressions and binding parameters to these expressions is demonstrated. The use of the 'define' function to associate names with values or functions is introduced, illustrating how constants and function names are created in Scheme.

50:16

๐Ÿ“‘ Output Functions and Theoretical Considerations in Scheme

Scheme's built-in output functions, such as 'display' and 'newline', are mentioned, along with the interpreter's default behavior of printing top-level results. The theoretical perspective on input and output in pure functional programming is discussed, noting that while they represent side effects, they are necessary for practical use and do not significantly compromise the purity of the language.

Mindmap

Keywords

๐Ÿ’กFunctional Programming Languages

Functional programming languages are a category of programming paradigms that treat computation as the evaluation of mathematical functions and avoid changing-state and mutable data. In the video, the instructor emphasizes the importance of understanding functional programming languages, particularly LISP and Scheme, due to their significant role in chapters 15 and 16 of the textbook and their focus in exams.

๐Ÿ’กReordering of Chapters

The concept of reordering chapters refers to the instructional strategy of changing the sequence of textbook chapters to allocate more time and emphasis on certain topics. The script mentions that chapters 15 and 16 are reordered to allow for a deeper dive into functional and logic programming, which are heavily weighted in the exams.

๐Ÿ’กPractical Implementation

Practical implementation in this context involves applying theoretical knowledge to execute tasks, such as writing code. The script stresses the importance of completing all practicals related to chapters 15 and 16, as the ability to implement code similar to the examples given is a requirement in the semester tests and the exam.

๐Ÿ’กReferential Transparency

Referential transparency is a property of a function or expression in which the expression can be replaced with its value without changing the program's behavior. The script explains that referential transparency is crucial for ensuring the predictability and reliability of a program, and it is a fundamental aspect of functional programming languages.

๐Ÿ’กFunctional Side Effects

Functional side effects are changes that occur outside the scope of a function, such as modifying a global variable or a parameter passed by reference. The script discusses the problems caused by functional side effects, such as ambiguity in the order of operand evaluation, and how they can be mitigated in functional programming languages.

๐Ÿ’กPure Functional Programming Language

A pure functional programming language is one that strictly adheres to the principles of functional programming, including the absence of mutable state and variables. The script explains that in pure functional programming languages like Scheme, functions rely solely on their inputs to determine their outputs, which enforces referential transparency.

๐Ÿ’กLambda Notation

Lambda notation is a way of defining anonymous (nameless) functions in functional programming languages. The script describes how lambda notation is used in LISP and Scheme to create functions on-the-fly, which can then be used immediately or passed as arguments to other functions.

๐Ÿ’กScheme

Scheme is a dialect of the Lisp programming language, designed to be a cleaner and simpler version of Lisp. The script discusses Scheme extensively, highlighting its features such as the use of atoms and lists, first-class functions, and its role as a primary focus for the lectures.

๐Ÿ’กFirst-Class Functions

First-class functions are a concept where functions are treated as first-class citizens in a programming language, meaning they can be assigned to variables, passed as arguments, and returned from other functions. The script explains that in Scheme, functions are first-class entities, allowing for powerful programming constructs.

๐Ÿ’กEval Function

The eval function in Scheme is a built-in function that evaluates expressions. The script mentions that the Scheme interpreter uses the eval function to interpret expressions, demonstrating the reflective and meta-programming capabilities of Scheme.

๐Ÿ’กDefine Function

The define function in Scheme is used to bind a name to a value or a lambda expression, effectively creating named constants or functions. The script illustrates how define is used to create named functions and emphasizes that names bound using define cannot be rebound, akin to constants rather than variables.

Highlights

The course material will focus on chapters 15 and 16, which cover functional programming languages and logic programming, due to their heavy emphasis in exams.

Practical implementation of code similar to examples in chapters 15 and 16 is essential for exam preparation.

Chapter 15 references earlier chapters, and the่ฎฒๅธˆ has expanded on these topics in slides and audio lectures.

Only specific functional programming languages, LISP and Scheme, will be in-depth discussed.

Functional programming is based on mathematical functions and is more theoretically solid compared to imperative programming.

Functional side effects, such as modifying parameters by reference or global variables, are generally discouraged due to ambiguity.

Referential transparency, where equivalent expressions do not affect program action, is a key concept in functional programming.

Pure functional programming languages enforce referential transparency by eliminating variables and side effects.

LISP, the first functional programming language, uses a typeless system with atoms and lists as its core data structures.

In LISP and Scheme, function applications and data representation share the same list notation, leading to powerful expressions.

Scheme, a LISP dialect, simplifies and modernizes LISP, emphasizing cleanliness and simplicity.

Everything in Scheme is a function, including operators and statements, which are first-class citizens.

Scheme's eval function allows for the evaluation of expressions provided by the programmer.

Arithmetic operations in Scheme are implemented as functions, differing from traditional operators in imperative languages.

Lambda expressions in Scheme define anonymous functions with bound variables, unlike variables in imperative languages.

The define function in Scheme binds names to values or functions, but unlike variables, these names are immutable.

Scheme provides built-in output functions like display and newline, but top-level expressions are typically printed directly by the interpreter.

Although input and output seem to violate the principles of pure functional programming, they are necessary for practical applications.

Transcripts

play00:00

we will now skip ahead to chapter 15 of

play00:03

the textbook which

play00:04

covers functional programming languages

play00:07

and we will spend the next

play00:08

three lectures on this material we will

play00:11

then move on to

play00:13

chapter 16 which deals with logic

play00:15

programming

play00:16

and prologue after which we'll move back

play00:18

to some of the earlier chapters

play00:21

now the reason that i've reordered the

play00:23

chapters in

play00:24

this way is so that we can spend more

play00:27

lecture time

play00:28

on chapters 15 and 16 and also dedicate

play00:33

more practical time to the concepts

play00:35

covered in these chapters

play00:36

the reason that this is important is

play00:39

that both the semester tests as well as

play00:41

the exam

play00:42

focus quite heavily on chapters 15 and

play00:44

16

play00:45

and there are quite a lot of marks

play00:47

dedicated to these chapters

play00:49

so it's very important that you focus on

play00:52

the material

play00:52

in these chapters when preparing it's

play00:55

also very important that you do

play00:58

all of the practicals related to the

play01:00

material

play01:01

covered in chapters 15 and 16.

play01:04

in particular please note that in the

play01:07

semester tests

play01:08

and the exam i can ask you to implement

play01:12

code um similar to the examples that we

play01:15

will be covering towards the end of

play01:17

chapter 15

play01:18

and the end of chapter 16. so be very

play01:22

sure that you can

play01:23

do the practical tasks that i set for

play01:26

you

play01:27

and those will prepare you well for the

play01:29

semester tests and the exam

play01:32

now because of this reordering chapter

play01:34

15 does refer

play01:35

to some shorter sections from some of

play01:38

the earlier chapters

play01:40

specifically chapters five to eight

play01:43

i've addressed this by fleshing out

play01:45

these topics

play01:46

in the slides and accompanying audio

play01:49

lectures

play01:50

so when you are preparing chapter 15 be

play01:53

sure

play01:54

to refer to both the textbook as well as

play01:57

the slides to make sure that you get a

play02:00

good coverage

play02:00

of all of the topics that we

play02:04

look at also important to note at this

play02:07

point

play02:08

is that we won't be covering the whole

play02:10

of chapter 15.

play02:11

chapter 15 goes into quite a lot of

play02:13

detail on a number of different

play02:15

functional programming languages

play02:17

and we will only be focusing on lisp

play02:20

and scheme

play02:23

these are the topics that we will be

play02:25

talking about in today's lecture

play02:28

we'll start off with a brief

play02:29

introduction just setting the scene

play02:31

for functional programming and why we

play02:34

are interested in functional programming

play02:36

we'll then move on to some of the

play02:38

theoretical

play02:39

underpinnings related to functional

play02:41

programming languages

play02:43

where we'll discuss mathematical

play02:45

functions mathematical functions being

play02:47

the basis

play02:48

of functional programming languages

play02:51

we'll then move on to

play02:53

a few fundamental concepts that exist

play02:56

across a variety of different functional

play02:58

programming languages

play03:00

and then we'll very briefly look at the

play03:02

lisp programming language which we've

play03:04

already

play03:05

touched on in chapter 2 and lisp was the

play03:08

very first

play03:09

functional programming language we'll

play03:11

then jump straight into scheme

play03:13

which as we discussed in chapter 2 is

play03:16

a dialect of lisp and this is

play03:20

the primary functional programming

play03:22

language that we will be focusing on

play03:24

for this chapter so we'll start off by

play03:26

looking

play03:27

at where scheme came from we'll then

play03:30

look at the scheme interpreter and how

play03:33

that

play03:34

operates then we'll look at function

play03:36

evaluation because

play03:38

scheme and all functional programming

play03:40

languages are based on the notion of a

play03:42

mathematical function

play03:44

we need to know how mathematical

play03:47

functions

play03:47

are evaluated within scheme then we'll

play03:50

look at

play03:51

some of the functions that are provided

play03:54

by

play03:54

scheme in terms of the primitive numeric

play03:58

functions which will allow

play04:00

us to perform simple arithmetic

play04:02

operations

play04:03

we'll then look at how we actually go

play04:05

about defining functions within scheme

play04:08

and then we'll finish off this lecture

play04:11

with a look

play04:12

at output functions that allow us to

play04:14

print results

play04:16

to a terminal

play04:19

as we saw in chapter one imperative

play04:22

programming languages

play04:24

are the most dominant kind of

play04:25

programming languages

play04:27

that we see in the world today and these

play04:30

programming languages

play04:31

are based directly on the computing

play04:34

hardware that they were designed for

play04:36

namely the von neumann architecture

play04:40

now i won't go into any further detail

play04:42

on the von neumann architecture again

play04:44

because we've already discussed that but

play04:47

the important points

play04:49

to recall is that for these imperative

play04:52

programming languages

play04:53

efficiency is the primary concern and we

play04:56

saw in chapter two

play04:58

that this was very well illustrated by

play05:02

the fortran programming language and

play05:05

these languages

play05:06

are very efficient because they are

play05:08

essentially

play05:09

direct reflections of what is happening

play05:11

on a hardware level

play05:14

so also then with imperative programming

play05:16

languages because they

play05:17

are designed with the computing hardware

play05:20

in mind

play05:20

the suitability of these programming

play05:23

languages for software development

play05:25

is far less important

play05:28

so if we look then at functional

play05:30

programming languages their design

play05:32

is based on mathematical functions and

play05:35

we'll discuss mathematical functions in

play05:37

more detail in a moment

play05:39

but because these functional languages

play05:42

are based then

play05:43

on mathematical principles they are

play05:46

defined by a much

play05:47

more solid theoretical basis than

play05:50

imperative programming languages

play05:52

are and they're also considered to be

play05:54

far closer to the user

play05:56

in the sense that mathematics is

play05:59

developed for

play06:00

people to understand rather than for

play06:03

machines to understand

play06:05

also in general functional programming

play06:07

languages

play06:08

are not very concerned with machine

play06:10

architecture

play06:11

they don't focus on things like

play06:14

efficiency of

play06:15

execution so they're very

play06:17

programmer-centric they try to give the

play06:19

programmer a much

play06:21

more natural mathematical way of

play06:24

expressing their programs

play06:28

now we've seen that functional

play06:30

programming languages are based on the

play06:32

idea

play06:33

of mathematical functions however we

play06:36

need to define

play06:37

what we mean when we talk about a

play06:39

function

play06:41

so a function is a kind of sub-program

play06:44

and a sub-program is a collection of

play06:47

parameterized computations

play06:49

so we can think of a sub-program as a

play06:52

package

play06:53

of related computational operations and

play06:56

the behavior of the computational

play06:58

operations is modified by sending

play07:00

different parameters

play07:02

to our sub-program now a function

play07:05

in particular produces results by means

play07:08

of returning

play07:09

a value now we also have the idea of

play07:14

side effects that can be caused by a

play07:16

function and we call

play07:17

these functional side effects

play07:21

so a functional side effect is any

play07:23

change

play07:24

that the function causes outside of the

play07:28

scope

play07:28

of the function and from a practical

play07:30

perspective what this means

play07:32

is that a functional side effect occurs

play07:34

whenever a function

play07:35

changes a parameter that has been sent

play07:38

to the function

play07:39

by reference or if it modifies a global

play07:42

variable

play07:44

now in general functional side effects

play07:46

are considered to be

play07:47

bad but why is this the case

play07:51

well essentially this is because it

play07:53

leads to ambiguity

play07:55

when we have an expression that

play07:58

references

play07:59

a function and then the function

play08:02

also modifies another operand within the

play08:05

same expression

play08:07

so let's look at a practical example

play08:10

over here to illustrate

play08:12

this concept here we have a variable a

play08:15

which has a value of 10

play08:16

assigned to it and then on the next line

play08:19

we have

play08:20

an expression which we can see over here

play08:23

the expression has an addition operator

play08:26

and this addition operator has two

play08:29

operands

play08:30

left operand which is just the value of

play08:32

the variable a

play08:34

and the right operand which is a call to

play08:36

our function

play08:37

fun and we can see that we have passed

play08:40

through

play08:40

this variable a as a parameter

play08:44

to our function the ampersand over here

play08:47

is c or c plus plus like notation

play08:50

and that indicates that we are passing

play08:53

through that variable

play08:54

by reference so in other words if the

play08:57

function

play08:58

modifies that parameter that has been

play09:00

sent through

play09:01

then it is actually modifying the value

play09:04

of the variable

play09:05

a so then once we have a result for our

play09:09

addition we then assign

play09:11

that value to a second variable which is

play09:13

called

play09:14

b now a potential ambiguity then

play09:18

arises when we consider the order that

play09:21

the operands

play09:22

of the addition operator are evaluated

play09:26

in

play09:27

so these operands can be evaluated in

play09:29

any order because we just

play09:31

need values for the two operands before

play09:34

we can compute the result

play09:35

of the addition now if we

play09:38

evaluate the left operand first

play09:41

then it will receive a value of 10

play09:45

and that will then be the value for the

play09:47

left operand the right operand then is

play09:50

our function call and we again then pass

play09:52

through the value of

play09:53

a which is 10 and then we will assume

play09:56

that this function then produces a

play09:58

result but before

play09:59

it produces that result by means of a

play10:02

return

play10:03

it also then modifies the value of

play10:06

a and let's just say then that it

play10:09

increments the value of

play10:10

a for argument's sake so what will then

play10:13

happen is after that function is called

play10:15

then a's value will have been

play10:17

incremented from 10 to 11.

play10:20

however that increment does not affect

play10:23

the

play10:23

left operand because it already has a

play10:26

value

play10:26

of 10. so in other words the result

play10:29

of the addition is then 10 plus

play10:33

whatever has been returned from the

play10:35

function let's say that the function

play10:36

returns a value of 5

play10:38

then we have a result of 15 which is

play10:41

assigned to

play10:43

b however now let's consider a situation

play10:46

where the

play10:47

right operand is evaluated first and

play10:49

then the left operand is evaluated

play10:52

so in this case we now call our function

play10:55

and once again

play10:56

our function will return a value of 5

play10:59

however before it returns the value of 5

play11:02

it then

play11:02

modifies the value of a by incrementing

play11:05

it so now a's value has been incremented

play11:07

from 10

play11:08

to 11. so this means then that the left

play11:12

operand will now get a value of

play11:14

11 because this value is determined

play11:18

after this function call in the

play11:21

right operand has taken place so in

play11:24

other words we now have a result

play11:26

of 11 plus 5 which gives us then the

play11:30

value of 16

play11:31

which is assigned to the variable b

play11:34

so we can see then that the expression

play11:37

evaluates to

play11:38

a different value and then results in a

play11:40

different value for the variable b

play11:42

depending on the order that we evaluate

play11:45

the operands

play11:47

for the addition operation

play11:50

and this ambiguity has arisen

play11:53

specifically because

play11:54

of the side effect that this function

play11:57

occurs

play11:57

which is the modification of the

play12:00

parameter that has been sent

play12:01

to the function by reference

play12:05

so consider now a situation

play12:09

where we have the same kind of

play12:11

expression

play12:12

however the function does not modify

play12:15

a parameter that has been sent to it

play12:17

instead

play12:18

it modifies a global variable

play12:21

so what i would like you to do at this

play12:23

point is pause the video

play12:25

and consider how that kind of situation

play12:29

would result in the same kind of

play12:32

side effect which would then result in

play12:35

the same kind of ambiguity

play12:40

there are two possible approaches we can

play12:42

use to solve this problem of ambiguity

play12:45

introduced by functional side effects

play12:48

and both of these approaches

play12:50

are based on the definition of the

play12:52

programming language we are using

play12:54

the first solution is that we can simply

play12:57

disallow

play12:58

functional side effects so this means we

play13:01

have to disallow any mechanism that a

play13:03

function can use

play13:05

to modify a variable value outside of

play13:08

the scope

play13:09

of the function this implies that we

play13:12

cannot have

play13:12

any support for two-way parameters

play13:16

for our functions and also we cannot

play13:19

allow any non-local references within

play13:22

functions

play13:22

because a non-local reference implies

play13:25

that a function can modify

play13:26

a global variable so of course this

play13:30

approach does work

play13:31

because we've eliminated the two

play13:32

possible ways that functional side

play13:34

effects can occur

play13:36

however the main disadvantage is that

play13:38

this reduces

play13:39

the flexibility of our programming

play13:42

language

play13:43

so we lose flexibility because we can

play13:46

only then support one-way parameters in

play13:49

other words we can only send data to a

play13:52

function by means of parameters

play13:54

we can't get data out of a function by

play13:57

means of

play13:58

parameters so in other words the only

play14:00

way that a function can produce a result

play14:03

is by means of its return value

play14:06

secondly we also lose flexibility

play14:09

because

play14:09

we no longer have any support for

play14:12

non-local references

play14:16

the second approach we can use to solve

play14:18

this problem of ambiguity introduced by

play14:20

functional side effects

play14:22

is that we can demand a fixed operand

play14:25

evaluation order

play14:27

so in other words we demand within the

play14:29

language specification

play14:31

that operands must at least appear to be

play14:34

evaluated

play14:35

in a particular order so java uses this

play14:39

approach

play14:40

the language specification requires that

play14:44

operands must at least seem to be

play14:46

evaluated from

play14:47

left to right now the main disadvantage

play14:50

of this approach

play14:52

is that it limits some compiler

play14:54

optimizations that could be performed

play14:56

sometimes compiler optimizations will

play14:59

result in operands being evaluated in a

play15:02

different order

play15:03

if that reordering of operand evaluation

play15:06

will improve the performance

play15:08

of the program's execution

play15:12

so this kind of compiler optimization

play15:15

then in certain cases will not be

play15:17

possible

play15:17

because we may have to evaluate

play15:21

a left operand before a write operand

play15:24

and we don't have any choice over that

play15:26

in certain other situations where the

play15:29

compiler can determine that no possible

play15:32

side effect can occur

play15:33

it may then allow the operands to be

play15:35

evaluated out of order

play15:37

and in that case a compiler optimization

play15:40

can be performed

play15:41

but the point is that compiler

play15:44

optimizations cannot be performed

play15:46

in every case where they would normally

play15:48

be possible

play15:51

a concept that is very closely related

play15:54

to functional side effects

play15:55

is the idea of referential transparency

play15:59

now we can say that a program has

play16:01

referential transparency

play16:02

if any two expressions that are

play16:05

equivalent

play16:06

can be substituted for one another in a

play16:08

program

play16:09

and this substitution does not affect

play16:12

the action of the program

play16:14

one cause of referential transparency

play16:17

breakdown

play16:18

is functional side effects but there are

play16:21

also other

play16:22

causes so this definition may seem like

play16:25

a bit of a mouthful

play16:26

so let's illustrate it in terms of a

play16:29

practical example

play16:31

over here we have two programs

play16:34

and let's look at the first program so

play16:37

over here we have an expression

play16:41

and we assign then the result of that

play16:43

expression to

play16:44

a variable called result one if we look

play16:48

at the expression in detail we see that

play16:50

there is

play16:51

a division operator and it has a

play16:54

left operand and a right operand

play16:58

so if we look at the left operand we can

play17:00

see that we have a call to a function

play17:02

and we pass through a parameter which is

play17:05

the variable a

play17:07

we then take the value returned by our

play17:10

function

play17:10

and we add to that the value of another

play17:13

variable

play17:14

b if we look at the right operand of the

play17:18

division we see

play17:19

a fairly similar structure there so we

play17:22

again

play17:23

have a call to the function with the

play17:25

same parameter the variable a

play17:28

and then we take the result returned by

play17:31

our function and we subtract from that

play17:34

the value

play17:35

of a variable a now

play17:38

if we look at this expression we can see

play17:41

that we have

play17:41

two repetitions of exactly the same

play17:45

function call

play17:46

with the same parameter in each case

play17:49

therefore it should be reasonable for us

play17:51

to separate this function call out

play17:53

as we do in the second program over here

play17:57

take the result returned by that

play17:59

function call and assign it to a

play18:01

variable called

play18:02

temp and then we just simply use

play18:05

temp within exactly the same expression

play18:09

rather than two separate calls to the

play18:12

function

play18:13

so what we have here now is two

play18:15

expressions

play18:16

this is the expression in our first

play18:19

program

play18:19

and this is the expression in our second

play18:22

program

play18:23

and these two expressions are equivalent

play18:26

to one another because they describe

play18:29

exactly the same

play18:30

kind of arithmetic operation

play18:34

so we now need to look at the values

play18:38

of the variables result 1 and result

play18:41

2. now if our function has no side

play18:44

effects

play18:45

then result 1 will be equal to result

play18:48

2. however if result 1 and result

play18:52

2 are not equal to each other due to a

play18:55

side effect

play18:56

then referential transparency has been

play18:58

violated

play19:00

right so how can referential

play19:02

transparency then

play19:04

break in this example right so let's

play19:07

assume

play19:08

first of all that our variable b

play19:11

in this case is a global variable

play19:15

and let's assume that the function in

play19:18

its implementation

play19:19

modifies this global b variable

play19:22

so now let's look at our first

play19:25

expression

play19:26

here we have two calls to our function

play19:30

and each of those calls modifies the

play19:33

global

play19:34

b variable so what this means then is

play19:37

that b

play19:38

has been modified twice in the second

play19:41

example we call

play19:42

our function only once meaning that it

play19:45

will only modify

play19:46

our global b variable one time

play19:50

now we can see that the result of

play19:53

our expressions in the two separate

play19:56

programs depend on the value of

play20:00

b so if b has been modified

play20:03

twice in the first case but only once in

play20:06

the second case

play20:07

this then means that the values of the

play20:10

two expressions must be different from

play20:12

one another

play20:13

meaning that result one and result two

play20:16

have different

play20:16

values and this means then that

play20:18

referential transparency

play20:20

has broken down another way

play20:23

that referential transparency can break

play20:25

down is if

play20:27

we modify the parameter value

play20:31

for the function so in our first example

play20:34

over here we can see that we call

play20:36

the function twice now if we assume

play20:39

that the function then modifies its

play20:42

parameter value

play20:43

we can see that we will be modifying the

play20:45

value of a

play20:47

twice because we have two calls to the

play20:50

function

play20:51

in the second case we are calling the

play20:53

function once

play20:54

meaning that we are only modifying the

play20:56

value of a

play20:58

one time so again this then means

play21:01

in the first expression we have modified

play21:04

a

play21:04

twice in the second expression we have

play21:07

modified

play21:08

a only once and therefore the

play21:11

results of the two expressions will be

play21:13

different and so

play21:14

once again the values of result 1 and

play21:17

result

play21:18

2 will differ and we again have then a

play21:21

breakdown

play21:22

of referential transparency

play21:25

so what i would like you to do at this

play21:27

point is to pause the video

play21:30

and try to determine if referential

play21:33

transparency can break

play21:34

in other ways so using this

play21:38

same example we can then see

play21:41

that we had a referential transparency

play21:44

breakdown

play21:45

occur because in the two

play21:48

separate programs we had different

play21:51

values

play21:51

for b or a that occurred

play21:55

due to the different number of function

play21:58

calls that took place

play22:00

so the only other way that breakdown of

play22:02

referential transparency can occur

play22:05

is if we have our function calls

play22:08

and for some reason the second function

play22:10

call

play22:11

produces a different value than the

play22:14

first

play22:15

function call in that case what will

play22:17

happen then

play22:18

is that we will have a different value

play22:22

for this function call in our first

play22:24

example over here

play22:26

whereas in the first case we will only

play22:29

have

play22:30

one call to our function meaning that

play22:33

temp

play22:33

will have the same value in both

play22:37

positions that it is

play22:38

used so what i would like you to do now

play22:41

is to think of

play22:42

ways that this kind of situation could

play22:45

arise in other words how could the

play22:47

second call to our function

play22:49

produce a different return value

play22:54

now that we've defined referential

play22:56

transparency

play22:57

what advantage is associated with it

play23:01

well what we see is that if a

play23:03

programming language enforces

play23:05

referential transparency then programs

play23:08

written in this language

play23:09

have semantics that are much easier to

play23:12

understand

play23:13

so if we look at the example on the

play23:16

previous slide

play23:17

we saw that there was ambiguity in terms

play23:20

of the value of the two expressions

play23:24

even though the two expressions should

play23:26

have been equivalent

play23:27

to one another this kind of ambiguity

play23:31

is completely eliminated if we use a

play23:33

programming

play23:34

language that ensures that referential

play23:37

transparency

play23:38

is maintained so this leads us directly

play23:42

to

play23:42

functional programming languages a pure

play23:45

functional programming language

play23:47

is a functional programming language

play23:49

that strictly adheres

play23:51

to the principles of functional

play23:53

programming

play23:54

now in a pure functional programming

play23:56

language

play23:57

there is no support for variables at all

play24:01

now this may seem a little strange and

play24:04

you

play24:04

may even wonder whether it is possible

play24:06

to write

play24:07

programs without the use of variables

play24:10

but as we will see

play24:11

through the remainder of this chapter it

play24:14

is entirely possible to write programs

play24:16

without

play24:16

using variables now why

play24:20

is the lack of support for variables

play24:22

important

play24:23

in a pure functional programming

play24:25

language

play24:26

well let's consider a function that we

play24:30

might implement now because variables

play24:34

don't exist

play24:34

global variables can't exist in the

play24:37

first place

play24:38

so in other words the function then

play24:40

can't modify

play24:41

global variables also parameters

play24:45

are not variables which means that

play24:47

parameter values cannot be modified by

play24:50

the function

play24:51

in other words they must be constant

play24:53

values that are simply

play24:54

passed into the function as inputs

play24:58

so what this means then is that we have

play25:00

eliminated the two possible sources

play25:03

for functional side effects we also

play25:07

see that functions cannot have a state

play25:10

associated with them

play25:12

and this is because state must always be

play25:14

stored within

play25:15

some kind of variable or set of

play25:19

variables so what this then means is

play25:22

that the value

play25:23

that a function returns depends entirely

play25:27

on its parameter values in other words

play25:30

if we have

play25:30

a function call and it receives

play25:34

a particular set of parameter values and

play25:37

then later we call the same

play25:38

function with the same set of parameter

play25:40

values then it is guaranteed that

play25:43

both of those function calls will return

play25:45

exactly the same

play25:47

value so this relates then to the

play25:51

question

play25:51

that i asked at the end of our

play25:54

discussion

play25:54

for the previous slide

play25:58

so in other words what this then means

play26:00

is

play26:01

that via the simple elimination of

play26:03

variables from our programming language

play26:06

we have actually then ensured that

play26:10

these languages then enforce referential

play26:13

transparency

play26:15

which then as we've seen ensures that

play26:18

the semantics of programs written in

play26:20

these languages

play26:21

are much easier to understand and follow

play26:27

so we see that the objective of a

play26:30

functional programming language's design

play26:33

is to mimic mathematical functions

play26:35

as closely as possible this means that

play26:39

the basic process of computation

play26:41

in a functional programming language is

play26:44

fundamentally different

play26:45

to what we see in an imperative

play26:48

programming language

play26:49

so in an imperative programming language

play26:53

expressions are evaluated these

play26:55

evaluations produce results

play26:57

and then the results are stored in

play26:59

variables and are used at a later stage

play27:03

this means that the management of

play27:06

variables then is a constant concern

play27:08

and it results in a lot of complexity

play27:12

associated with imperative programming

play27:16

in a purely functional programming

play27:17

language on the other hand

play27:19

variables are not used and this is also

play27:22

the case

play27:23

in mathematics so this means then that

play27:26

none of the complexities associated with

play27:28

variables have to be dealt with we don't

play27:30

need to manage variables

play27:32

and we also don't need to deal with the

play27:35

associated ambiguity

play27:37

that stems from the use of variables

play27:42

we'll now look fairly briefly at the

play27:44

lisp programming language which will

play27:46

lead us directly to

play27:48

a more detailed discussion on scheme

play27:51

which is

play27:52

a dialect of lisp so lisp as you should

play27:55

recall

play27:56

from chapter 2 was the very first

play27:59

functional programming language

play28:02

unfortunately it's fairly difficult for

play28:04

us to talk about lisp

play28:05

in its original form because the

play28:07

language has undergone

play28:09

so many changes and there are so many

play28:10

dialects of lisp

play28:12

that have existed over the years so

play28:15

we'll limit

play28:15

our discussion to features that are

play28:18

common within

play28:19

most dialects of lisp so first of all we

play28:22

need to talk

play28:23

about the data that we can represent

play28:26

within a

play28:27

lisp program so we will talk about

play28:30

data objects and data structures

play28:34

now notice that we talk about data

play28:36

objects and not data types

play28:38

and this is because lisp was originally

play28:41

a

play28:41

typeless language so

play28:44

data objects that we can manipulate

play28:47

within

play28:48

our lisp programs can only be either

play28:51

atoms or lists

play28:54

an atom is a single value that we can

play28:57

work with

play28:58

and there are two types of atoms namely

play29:01

symbols

play29:02

and numeric literals symbols have

play29:05

identifiers in other words names such as

play29:08

a and x now it's important to understand

play29:11

that a symbol is not a character or a

play29:14

string it's simply a

play29:16

name for a concept that the program

play29:19

can work with so a symbol may represent

play29:23

an abstract

play29:24

idea or it may represent some sort of

play29:26

object

play29:27

in an environment that lisp is

play29:30

processing somehow now the reason

play29:33

that we represent symbols in this way

play29:37

is that this kind of abstract concept

play29:40

processing

play29:41

was central to artificial intelligence

play29:43

research at the time that lisp was

play29:45

developed

play29:46

and as we saw in chapter 2 lisp was a

play29:49

programming language designed for

play29:51

artificial intelligence applications

play29:54

the second kind of atom that we have is

play29:57

the set of numeric literals and these

play30:00

are simply numeric values such as 4.8

play30:04

or 26 we then

play30:07

have lists which are stored internally

play30:10

as single linked lists and these linked

play30:13

lists then store

play30:15

sequences of atoms um

play30:18

as well as sub-lists and we can have any

play30:20

kind of mixture of different atoms

play30:24

with sublists in any particular kind of

play30:27

structure

play30:28

that we would like so here's an example

play30:31

then

play30:32

of list form notation within

play30:35

the lisp programming language we can see

play30:38

that

play30:38

the lists are delimited by means of

play30:42

parentheses

play30:44

so here we have the start of a list

play30:47

and here we have the end of a list

play30:50

so this list then contains four items

play30:54

the first two are atoms

play30:57

and they are symbolic atoms so we have

play31:00

a and we have b and these are both

play31:04

symbols then the third item

play31:07

in the list is another list so we can

play31:10

see that it's been again delimited by

play31:12

means of these parentheses

play31:14

and this nested list then contains once

play31:17

again two

play31:19

symbols and these symbols are

play31:22

c and d in this case and then the fourth

play31:26

element

play31:26

contained in the outer list is again

play31:29

then

play31:30

a symbol and in this case it is the

play31:32

symbol

play31:33

e in lisp terminology

play31:37

when we call a function we say that we

play31:40

apply that function

play31:42

now it turns out that data and function

play31:45

applications

play31:46

in lisp have exactly the same form and

play31:49

they both

play31:50

use a list notation so

play31:53

if we have this list notation over here

play31:56

which is

play31:57

a list that contains three

play32:00

symbols a b and c we can interpret this

play32:04

either as data or as a function

play32:07

application

play32:08

if we interpret it as data then we have

play32:11

a simple list containing three atoms

play32:15

so in other words the list contains only

play32:17

three atoms it doesn't include any

play32:19

sub-lists

play32:21

and then the three atoms that are

play32:23

contained within the simple list

play32:25

are the symbolic atoms a b

play32:28

and c alternatively we can interpret the

play32:31

same list

play32:32

as a function application and in this

play32:36

case

play32:36

we are applying a function which is

play32:39

named a

play32:40

to two parameters and the parameters are

play32:43

the symbolic atoms

play32:45

b and c now

play32:48

this ambiguous notation may seem

play32:51

counterproductive

play32:53

but we're going to see later on when we

play32:55

look at

play32:56

scheme that this in fact allows us

play32:59

to do incredibly powerful things within

play33:03

the

play33:03

programming language next we need to

play33:06

look at function definitions because of

play33:08

course in a functional programming

play33:10

language we have to be able to define

play33:12

functions and in lisp we use lambda

play33:15

notation in order to specify

play33:17

function definitions so over here we

play33:21

have

play33:21

a lambda expression and this represents

play33:25

a nameless function so this function has

play33:28

no name associated with it

play33:30

it is applied to a set of arguments and

play33:33

in this case

play33:34

we have n arguments they are separated

play33:37

by

play33:38

spaces we then have an

play33:41

expression and this expression then

play33:43

defines

play33:44

what the nameless lambda function

play33:47

actually

play33:48

does in c plus terminology the

play33:51

expression would be the body of the

play33:53

function

play33:54

now it may seem like a bit of a

play33:57

pointless thing

play33:59

to do to create functions that don't

play34:03

have names

play34:04

but very often these nameless functions

play34:06

on their own

play34:07

are fairly useful so sometimes we want

play34:10

to

play34:10

define a function and just simply use it

play34:14

where we define it in this case we then

play34:17

don't need to attach

play34:18

a name to the function at all so we can

play34:21

just simply

play34:22

use the lambda expression now we can

play34:26

also then attach a name to a lambda

play34:29

expression and we would then

play34:31

say that we are binding a name to a

play34:33

lambda expression

play34:34

which simply means that we are

play34:36

associating

play34:37

a name with the lambda expression so

play34:40

over here we have

play34:42

an example of what this kind of notation

play34:44

would look like

play34:45

we can see that we have our lambda

play34:48

expression over here which defines a

play34:51

nameless

play34:52

function again this function is applied

play34:55

to in arguments and we then have an

play34:58

expression which defines

play35:00

what the lambda function does we then

play35:03

additionally specify

play35:04

a function name and from this point on

play35:08

when we use that name

play35:10

we are now actually referring to this

play35:12

lambda

play35:13

expression now when we look

play35:16

at the first implementation of a

play35:19

lisp interpreter we see that

play35:23

it was in fact implemented entirely just

play35:27

to demonstrate the universality of

play35:30

the functional programming model

play35:34

so much in the same way as

play35:38

turing machines were used to reason

play35:41

about computability within imperative

play35:44

programming languages

play35:46

and one can define a universal turing

play35:49

machine

play35:50

that can be used to specify any other

play35:53

turing machine

play35:54

so we can also define a universal lisp

play35:58

function that can evaluate any other

play36:01

lisp function and so this universal

play36:04

lisp function then demonstrates the

play36:08

universality of the computation

play36:12

that lisp can perform

play36:16

we've now looked at lisp in fairly

play36:18

superficial terms

play36:19

and this is intentional because we will

play36:22

now

play36:22

transition into a much more detailed

play36:25

discussion

play36:26

on scheme and as we've previously

play36:29

pointed out scheme

play36:30

is a dialect of lisp so scheme was

play36:33

developed in the mid-1970s and it was

play36:36

designed to be

play36:37

a much cleaner more modern and far

play36:40

simpler version of lisp

play36:42

than the contemporary lisp dialects that

play36:44

were available

play36:45

at the time now scheme uses exactly the

play36:49

same

play36:49

atoms and lists as lisp does

play36:53

as far as atoms go scheme

play36:56

also uses numeric atoms as well as

play36:59

symbolic atoms exactly as we saw

play37:02

with the lisp programming language

play37:05

scheme

play37:06

also uses static scoping exclusively

play37:09

and static scoping is a very simple

play37:11

intuitive approach

play37:12

to scoping what's also important to

play37:15

understand for scheme

play37:17

is that everything within the

play37:19

programming language

play37:20

is a function so this includes things

play37:23

that would

play37:24

be operators or statements within an

play37:28

imperative programming language so for

play37:31

example we don't have

play37:33

an edition operator in scheme we have an

play37:36

addition function the same holds for

play37:39

selection structures so instead of

play37:41

having an if statement

play37:43

we have an if function

play37:46

functions in scheme are also first class

play37:49

entities

play37:50

so this means that expressions can

play37:53

evaluate

play37:54

two functions so we can have an

play37:56

expression that can compute a function

play37:58

in other words

play38:00

functions can also be elements within a

play38:03

list so we can have

play38:04

a list which is a sequence of functions

play38:08

and functions can also be passed as

play38:11

function parameters so we can send a

play38:13

function

play38:14

to another function by means of a

play38:17

parameter

play38:18

and then finally functions can also be

play38:21

returned

play38:22

from other functions this is a very

play38:25

powerful concept as we'll see

play38:26

towards the end of our discussion on

play38:29

this chapter

play38:30

because it means that we can write

play38:32

functions

play38:33

that can build other functions

play38:37

now scheme is an interpreted programming

play38:40

language

play38:41

in exactly the same way that the

play38:43

original lisp implementations

play38:45

were interpreted a scheme interpreter

play38:49

typically provides an interactive mode

play38:51

which is a kind of a

play38:53

terminal mode that operates in an

play38:55

infinite loop of

play38:57

read evaluate and print operations

play39:00

so in this interactive mode you would

play39:03

type in

play39:03

a scheme expression this expression

play39:06

would be read and it would then be

play39:08

evaluated

play39:09

and then the result of the evaluation

play39:12

would be printed out

play39:14

the interpreter would then wait for you

play39:16

to type in another expression and this

play39:18

whole process

play39:19

would repeat until eventually you

play39:22

terminate the

play39:23

interactive mode this form of

play39:25

interpreter is also used by python

play39:28

and ruby now most scheme interpreters

play39:32

also accept source files as input and

play39:35

in this case the source file is

play39:38

interpreted in

play39:39

a kind of a batch mode and this will be

play39:42

the approach

play39:43

that you will use for your practical

play39:46

implementations related to scheme

play39:50

now expressions in scheme are

play39:53

interpreted

play39:54

by a function called eval so what this

play39:57

means is that the scheme interpreter is

play39:59

actually available to the programmer

play40:02

by means of a scheme function and this

play40:04

is a very powerful

play40:06

concept which we'll look at in more

play40:08

detail later on

play40:10

when we discuss the eval function in

play40:13

more detail now literals in scheme

play40:17

evaluate to themselves so if the

play40:20

interpreter evaluates

play40:22

the literal 4 then the result will just

play40:25

be

play40:26

the literal 4 itself if the scheme

play40:29

interpreter

play40:29

evaluates the symbolic atom

play40:33

a for example then this will also

play40:37

evaluate to the symbolic atom a

play40:41

next we need to look at how the scheme

play40:44

interpreter

play40:45

evaluates function applications

play40:48

so when a function application is

play40:51

encountered

play40:52

first of all all of the parameters that

play40:54

the function

play40:55

is applied to must be evaluated

play40:59

and this parameter evaluation happens

play41:02

in no particular order now in this

play41:05

context

play41:06

no particular order does not mean that

play41:09

the parameters are evaluated in a random

play41:12

order

play41:12

rather it means that the order that the

play41:15

parameters are evaluated in

play41:17

is not important so why is this order

play41:21

not

play41:21

important well as we've seen a pure

play41:24

functional programming language such as

play41:27

scheme

play41:27

has no functional side effects so what

play41:30

this means then

play41:32

is that none of the parameter

play41:34

evaluations

play41:36

can affect any other parameter

play41:38

evaluations

play41:40

each parameter evaluation therefore is

play41:42

independent

play41:43

and so the order does not matter

play41:46

and cannot affect the final outcome of

play41:49

the evaluation

play41:52

so once all of the parameters have been

play41:54

evaluated

play41:55

we now have a value per parameter

play41:59

and these values are then substituted

play42:01

into

play42:02

the function body the function body is

play42:05

then evaluated

play42:07

and then very importantly the value of

play42:10

the last

play42:10

expression evaluated in the body is the

play42:13

value

play42:14

that the function defines you can think

play42:17

of the value that the function defines

play42:19

as the value that the function returns

play42:24

if we turn our attention to arithmetic

play42:27

operations in scheme

play42:29

we must remember that all of these

play42:31

operations

play42:32

are implemented as functions and this is

play42:35

because

play42:36

everything within scheme is a function

play42:40

now the scheme language specification

play42:42

provides a set of primitive

play42:44

arithmetic functions so we have

play42:47

addition subtraction multiplication and

play42:50

division and these use the same notation

play42:54

that we are used to from

play42:55

imperative programming languages such as

play42:58

c

play42:58

plus we also have a function that can

play43:01

compute

play43:02

an absolute value of a parameter

play43:05

a function that can compute the square

play43:08

root

play43:08

of a parameter a function that can

play43:12

compute the remainder of

play43:15

an integer division and then a

play43:18

min and a max function that

play43:21

will find respectively the minimum and

play43:24

the

play43:24

maximum value within a set of parameters

play43:30

now also at this point note that the

play43:32

notation that we will be using

play43:34

for these function names where they

play43:36

include alphabetic

play43:38

characters will be all uppercase and

play43:41

this is the convention that the textbook

play43:44

uses

play43:45

however a lot of scheme interpreters

play43:48

use lowercase letters for these function

play43:51

names

play43:51

so just keep this in mind when

play43:54

you get to actually implementing scheme

play43:57

programs

play43:59

so when we apply these functions we use

play44:02

the standard

play44:03

list notation that we saw when we were

play44:05

discussing

play44:06

the lisp programming language so over

play44:09

here we have

play44:10

a function application we are applying

play44:12

the

play44:13

plus function to two parameters where

play44:16

the first parameter is five

play44:18

and the second parameter is two the

play44:22

result of this function application is

play44:24

then simply the sum

play44:26

of the two parameter values so in other

play44:29

words this function application will

play44:31

then

play44:31

yield a value of 7.

play44:35

now all of these primitive arithmetic

play44:38

functions

play44:39

can be applied to multiple parameters

play44:41

except

play44:42

for the absolute value and square root

play44:44

functions

play44:45

both of which can only be applied to a

play44:48

single parameter value

play44:50

so over here we have an example of the

play44:53

application

play44:54

of the addition function to

play44:58

four parameter values and

play45:01

the result of this function application

play45:03

will then

play45:04

just be the result of adding 5 2

play45:07

8 and 9.

play45:11

we can also define lambda expressions in

play45:14

scheme

play45:15

and the form of this definition is based

play45:18

on lambda notation which we saw

play45:20

previously

play45:22

so over here we have a lambda expression

play45:25

in

play45:25

scheme you can see that we use the

play45:28

lambda function followed by a

play45:32

list of the parameters that the

play45:35

nameless lambda function will be applied

play45:38

to

play45:38

after the list of parameters we have the

play45:41

body of our lambda function and in this

play45:45

case

play45:46

the body then defines the result of

play45:49

our lambda function which is the single

play45:52

parameter value

play45:53

multiplied by itself in other words what

play45:56

we have defined here then

play45:58

is a nameless function that is applied

play46:02

to

play46:02

a single parameter and it yields

play46:06

that parameter value squared

play46:09

so x then in this case is referred to

play46:12

as a bound variable

play46:15

x has a value bound to it when

play46:19

this nameless lambda function is applied

play46:22

to a parameter and this binding then

play46:26

is unchanged from this point on so in

play46:29

other words

play46:30

x is not a variable in the sense that we

play46:33

are used to in

play46:34

an imperative programming language

play46:36

because it can't be assigned

play46:38

to after its initial binding

play46:42

so let's look at how a lambda expression

play46:45

then can be

play46:46

applied so over here we have the

play46:49

lambda expression that we previously

play46:51

defined an unnamed function

play46:54

that is applied to a single parameter

play46:56

and this unnamed function

play46:58

yields the parameter value multiplied by

play47:01

itself

play47:02

we now place this lambda expression

play47:05

inside

play47:06

another list starting there and ending

play47:08

there and then we provide

play47:11

after the lambda expression

play47:14

the parameters that we want to apply

play47:17

this nameless lambda function 2. so in

play47:20

this case

play47:21

we have provided then the single

play47:23

parameter value which

play47:24

is 7 and what this then means is that

play47:28

the value

play47:28

7 is bound to the bound variable x

play47:33

over here we can now no longer change

play47:36

the value of x after this point

play47:39

and so the application then of the

play47:41

lambda expression

play47:43

yields 7 which is the value of x

play47:47

multiplied by itself which then yields

play47:50

a result of 49

play47:54

now a lambda expression of course can

play47:56

have any number of parameters

play47:58

so over here we have a lambda expression

play48:01

that defines

play48:02

a nameless function which is applied to

play48:05

three parameters and the parameters are

play48:08

named a

play48:09

b and x respectively and then over here

play48:12

we have the

play48:14

body of our nameless lambda function

play48:17

the body computes a multiplied by

play48:21

x squared over here and then

play48:24

also b multiplied by x over there

play48:28

and then it adds these two values by

play48:31

an application of the plus function

play48:36

so we've seen that we can define a

play48:39

nameless function

play48:40

using a lambda expression however

play48:43

oftentimes we do want to associate

play48:46

a name either with a value or with

play48:49

a function in scheme and to do this

play48:52

we use a special function provided by

play48:55

the scheme

play48:56

language system called define

play48:59

so the define function can be used in

play49:02

two different ways

play49:03

the simplest way it can be used is to

play49:05

bind a name

play49:07

to the value of an expression so over

play49:10

here we have two examples

play49:12

of this usage over here we are applying

play49:15

the define

play49:16

function and we provide then first of

play49:19

all a name and then secondly

play49:22

an expression now in this first example

play49:25

the expression is

play49:27

simply a numeric literal 3.141593

play49:33

so that value then is bound to the name

play49:37

pi and what this means is from this

play49:39

point on

play49:40

in our scheme program the name pi means

play49:43

the value 3.141593

play49:48

so over here we have then a second use

play49:52

of the define function again we provide

play49:54

the function name define

play49:56

and we provide a name in this case 2 pi

play50:00

and then we provide an expression so

play50:03

over here we can see that we have

play50:05

an arithmetic expression it's an

play50:07

application

play50:08

of the multiplication function to

play50:12

the parameters 2 and pi

play50:16

so what this will then do is it will

play50:18

retrieve the value 3.141593

play50:22

for the name pi and it will multiply

play50:25

that

play50:25

by 2 producing a result which is the

play50:29

value of that expression

play50:31

and then that value is bound to 2

play50:34

pi now what's important to understand is

play50:38

that these name bindings only occur

play50:41

once so in other words we cannot bind a

play50:45

different value to pi

play50:47

or to 2 pi so what this then

play50:50

means is that these names do not work

play50:53

like variables

play50:54

in standard imperative languages like c

play50:57

plus in those languages we can reassign

play51:01

to a name if it is a variable

play51:04

in the case of a name in scheme we

play51:07

cannot

play51:08

rebind a different value to it so in

play51:11

other words these names behave

play51:13

more like constants so

play51:16

then the second use of the define

play51:19

function

play51:20

is to bind a name to a lambda expression

play51:23

in other words we are binding a name

play51:26

to a function and we're going to see

play51:29

this use

play51:30

of the define function repeatedly

play51:33

through

play51:33

the remainder of this chapter so here we

play51:37

have

play51:37

an example of that we apply

play51:40

the define function and we provide then

play51:44

first of all a list and this list

play51:47

contains

play51:48

the name that we want to bind to the

play51:51

function

play51:52

as well as the parameters that we want

play51:55

the function to be applied to

play51:57

in this case the function can only be

play52:01

then applied

play52:02

to one parameter then we have

play52:06

the function body which is expressed

play52:09

over here

play52:10

so essentially what we are doing then is

play52:12

we are binding the name

play52:14

square to a function

play52:17

which can be applied to a single

play52:20

parameter

play52:21

and the result of that function then

play52:25

is the result of multiplying the

play52:28

parameter

play52:28

by itself so now

play52:32

we can use the name in a function

play52:34

application

play52:35

which we do in this example over here so

play52:38

here we are

play52:39

applying the square function

play52:42

and we have bound the name square

play52:45

to the function that we've defined so we

play52:48

are applying that function

play52:49

to the parameter 5.

play52:52

what this then means is that this

play52:54

function application will be the result

play52:57

of multiplying

play52:58

5 by itself and so the result

play53:01

of the function application will be 25.

play53:05

now it's important to understand that

play53:07

the evaluation process for define

play53:09

is different to the standard primitive

play53:12

functions that we've spoken about before

play53:14

in the context of

play53:15

arithmetic functions so the first

play53:18

parameter is

play53:19

not evaluated as it would be in a normal

play53:22

function

play53:23

and the reason for this is that if it

play53:25

were evaluated then scheme

play53:27

would find that it was undefined so for

play53:30

example here if we were to evaluate the

play53:32

define

play53:33

function the way that we normally would

play53:36

evaluate a function

play53:38

then each of the parameters namely the

play53:40

name pi

play53:41

and then the value 3.141593

play53:44

would need to be evaluated however

play53:47

because the name

play53:48

pi has not been bound yet this would

play53:50

then be

play53:51

an invalid evaluation and this would

play53:55

produce

play53:55

an error which would be reported by the

play53:59

scheme

play53:59

interpreter so it's important to

play54:01

understand that define

play54:03

is a special kind of function that does

play54:05

not behave the way that

play54:07

a normal function in scheme does

play54:12

scheme also provides a number of built

play54:14

in output functions

play54:16

the first of which is display

play54:19

so the display function is applied to

play54:22

an expression and this expression

play54:25

is evaluated and then the result of the

play54:28

expression

play54:29

is printed out to the screen there is

play54:32

also

play54:33

a function called newline and the

play54:36

newline

play54:37

function is not applied to any

play54:39

parameters

play54:40

and simply prints out a new line

play54:43

character

play54:43

to the screen scheme also has

play54:47

a c like print if function which allows

play54:49

you to produce

play54:50

formatted output but we won't go into

play54:53

any detail on this

play54:55

function here in general however

play54:58

these output functions are not used

play55:02

and the reason for this is that programs

play55:04

can simply use

play55:06

the interpreter's normal output the

play55:09

interpreter's normal output

play55:11

behavior is that any top level function

play55:14

in other words

play55:14

a non-nested function that produces

play55:18

a result will simply then have that

play55:20

result

play55:21

printed to the screen now

play55:25

it's also important from a theoretical

play55:27

perspective to understand

play55:29

that explicit input and output should

play55:32

not

play55:32

actually be part of a pure functional

play55:35

programming language

play55:37

this is because input operations change

play55:40

the state

play55:41

of the program and as we've seen a pure

play55:44

functional programming language should

play55:46

not maintain any kind of state

play55:50

secondly output operations can be seen

play55:53

as

play55:53

side effects because they are producing

play55:56

a result

play55:57

outside of the bounds of the functions

play56:01

that are being executed

play56:03

so in a sense then a pure functional

play56:06

programming language is not really

play56:08

possible

play56:09

because input and output operations must

play56:13

be included

play56:14

for the programs to be useful however

play56:17

in practice inputs and output operations

play56:21

represent fairly benign

play56:24

side effects that do not to drastically

play56:28

affect the pure nature of a functional

play56:31

programming language so that concludes

play56:35

this lecture

play56:36

in the next lecture we will continue

play56:39

with our discussion on the scheme

play56:41

programming language

Rate This
โ˜…
โ˜…
โ˜…
โ˜…
โ˜…

5.0 / 5 (0 votes)

Related Tags
Functional ProgrammingScheme LanguageLISP DialectTheoretical UnderpinningsProgramming ConceptsMathematical FunctionsLambda NotationFirst-Class FunctionsInterpreter ModeEducational Material