0% found this document useful (0 votes)
32 views

Functional Programming 2.1

Functional programming focuses on pure functions that produce predictable outputs from given inputs without side effects. It emphasizes expressions and declarations over statements and avoids shared state/mutable data. Some key characteristics include decomposing problems into functions, using recursion and immutable data. Foundational languages include Lisp from the 1960s and ML in the late 1970s which introduced concepts like algebraic data types and pattern matching still used in modern functional languages today.

Uploaded by

Anurag Sharma
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views

Functional Programming 2.1

Functional programming focuses on pure functions that produce predictable outputs from given inputs without side effects. It emphasizes expressions and declarations over statements and avoids shared state/mutable data. Some key characteristics include decomposing problems into functions, using recursion and immutable data. Foundational languages include Lisp from the 1960s and ML in the late 1970s which introduced concepts like algebraic data types and pattern matching still used in modern functional languages today.

Uploaded by

Anurag Sharma
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 103

Functional Programming

• What is Functional Programming?


• Functional programming (also called FP) is a
way of thinking about software construction
by creating pure functions. It avoid concepts
of shared state, mutable data observed in
Object Oriented Programming.
• Functional langauges empazies on expressions
and declarations rather than execution of
statements. Therefore, unlike other
procedures which depend on a local or global
state, value output in FP depends only on the
arguments passed to the function.
Characteristics of Functional
Programming
• Functional programming method focuses on results,
not the process
• Emphasis is on what is to be computed
• Data is immutable
• Functional programming Decompose the problem into
‘functions
• It is built on the concept of mathematical functions
which uses conditional expressions and recursion to
do perform the calculation
• It does not support iteration like loop statements and
conditional statements like If-Else
History of Functional programming

• The foundation for Functional Programming is Lambda


Calculus. It was developed in the 1930s for the
functional application, definition, and recursion
• LISP was the first functional programming language.
McCarthy designed it in 1960
• In the late 70’s researchers at the University of
Edinburgh defined the ML(Meta Language)
• In the early 80’s Hope language adds algebraic data
types for recursion and equational reasoning
• In the year 2004 Innovation of Functional language
‘Scala.’
Functional Programming Languages

The objective of any FP language is to mimic the mathematical functions.


However, the basic process of computation is different in functional
programming.
Here, are some most prominent Functional programming languages:
• Haskell
• SML
• Clojure
• Scala
• Erlang
• Clean
• F#
• ML/OCaml Lisp / Scheme
• XSLT
• SQL
• Mathematica
Basic Functional Programming
Terminology and Concepts
What is functional programming?

• Functional programming is a way of writing


software applications using only pure
functions and immutable values.
Elements of functional programming
• Pure functions and side effects
• Referential transparency
• First class functions & higher order functions
• Anonymous functions or lambda
• Immutability
• Recursion & tail recursion
• Statements
• Strict and non-strict (lazy) evaluation
• Pattern matching
• Closures
Why functional programming?

• If you understand the benefits that those ten items


bring on the table, you know the answer to your
question. The greatest benefit of FP is that it brings a
different approach and facilities to solve your
problems. By the end of this section, you will learn
many of those facilities and the overall approach.
When you start practicing FP, you will realize that the
approach fits better in many scenarios. Library design
and data crunching are among those problems. That's
the main reason the Spark creators picked Scala to
implement Spark libraries.
So, with the stage set, let's take these top ten FP
elements one by one.
What is a pure function?

• Let me ask you a simple question. What is a


function? Let's take the definition of function
from the mathematics because all
mathematical functions are always pure.
A function relates an input to an output. So,
there are three main parts.
• The input
• The relationship
• Output
• Pure function
• A ‘Pure function’ is a function whose inputs
are declared as inputs and none of them
should be hidden. The outputs are also
declared as outputs.
• Pure functions act on their parameters. It is
not efficient if not returning anything.
Moreover, it offers the same output for the
given parameters
• Here is an example.
• Math.sqrt(4.0)
• Example:
• Function Pure(a,b)
• {
• return a+b;
• }
• Impure functions
• Impure functions exactly in the opposite of
pure. They have hidden inputs or output; it is
called impure. Impure functions cannot be
used or tested in isolation as they have
dependencies.
• Example
• int z;
• function notPure()
• {
• z = z+10;
• }
There are few observations about this
mathematical function.
1. The Input solely determines the output -
I mean, there is no other thing like a global variable or the content
of a file, or input from the console that determines the output. It's
only the input parameter value, nothing else. No matter how
many times or where do you invoke this function, as long as the
input parameter value is same, you are going to get the same
output. That's the first feature of a pure function.
2. The function doesn't change its input -
In some of the programming languages, you might have heard
about the call by value or call by reference. All that confusion is
out of the scope from a pure function's standpoint because it
guarantees that the input value remains unchanged. It never
modifies the Input value. That's the second quality of a pure
function.
3. The function doesn't do anything else except computing the
output -
I mean, it doesn't read anything from a file or console. It doesn't
print anything on the console or write some data to a file. It
doesn't read or modify a global variable or for that matter
anything outside the function. In fact, it doesn't perform any I/O.
A pure function is like a special purpose machine. Takes the input,
computes the output, returns it and that's all. No other work. If it
does anything else that impacts the outside world or is visible to
outside world, we call it a side effect of the function. A side effect
is like doing something other than your primary purpose. So, a
function is pure if it is free from side effects. That's the third
quality.
Note - That's all. If a function qualifies on these three conditions, it is
a pure function.
Is there an easy method to validate the purity of a function?
Yes. Test it for the referential transparency.
• Is there an easy method to validate the purity
of a function?
• Yes
• Test if for the referential transparency.
What is referential transparency?

• A function is said to be referentially transparent if we


can replace it with a corresponding value without
changing the program's behaviour.
• So, can you replace all references of the Math.sqrt(4)
with its corresponding value 2 in your code.
• The answer is obviously Yes. If we know that input
value is 4, we can replace Math.sqrt(4) with 2.
• Will it change the behaviour of your program? The
answer is obviously No.
• So, the sqrt is a pure function because we can change
its references with the output value as long as input
value is same.
Pure function summary

A pure function follows these rules.


• The output is only dependent on input
parameter values.
• The function doesn't modify the Input
parameter values.
• The function doesn't have a side effect.
Side Effects

• Side effects are any state changes which occur


outside of a called function. The biggest goal
of any FP programming language is to
minimize side effects, by separating them
from the rest of the software code. In FP
programming It is vital to take away side
effects from the rest of your programming
logic.
Why pure functions?

• Pure functions encourage safe ways of programming



I think you already agree with this statement because
side effects are surprising for everyone except the
original coder. Pure functions are small, precise,
simple, safe and easy to reuse because you know that
they take input and give output based on the input
values, that's it. They don't do anything else. They
don't surprise you.
• Pure functions are more composable or modular –
It is very common in FP to combine many functions
into a simple solution. For example, you’ll often see FP
code written as a chain of function calls, like this:
• Pure functions are easy to test –
I think you will agree with this as well. Since there are
no side effects and the output only depends on input,
your test cases are straight forward. You just pass the
known value and assert for the expected value,
whereas simulating the side effects is a real challenge
for testing. For example, you have two functions. The
first function is not pure, so it prints 'Hello World' on
the console. The second function is pure, so it doesn't
print anything on console but returns 'Hello World'.
You can quickly assert the return value, however,
asserting the console output will be a complex thing.
• Pure functions are memoizable –
The memoization is nothing but caching of
deterministic functions. If you know that your function
is pure, and you will need the results again, you can
cache the output, or your compiler can also do that as
an optimization. But if you have side effects, you can't
cache the results for later use.
• Pure functions can be lazy –
This one is a big advantage, and we have listed it as a
separate topic in our top 10 list. I will cover it later
when we reach to the laziness.
What is a first-class Function?

• If you can treat a function as a value, it is a first-class


function.
That's a simple definition but what does it mean?
That means you should be able to do everything with
the function that you can do with a value.
• You can assign it to a variable - You can assign a value
to a variable, so you should be able to assign a
function to a variable.
• You can pass it as an argument to other functions -
You can pass a value to a function as an argument, so
you should be able to pass a function as well.
• You can return it as a value from other functions -
You can return a value from a function, so you
should be able to return a function also.
• If you can do those three things with a function,
it's a first-class function. In Scala, all functions are
first class functions by default. That's a feature of
Scala. You don't need to test if your function is a
first class or not. We will see some examples but
before that, let me cover the next jargon.
• //Let's assign it to a variable.
• val d = doubler(_)
• // Now you can use the variable d as it is a
function.
• d(5)
• d(3)
• val r = 1 to 10
• r.map(doubler)
• Or we can write this
• r.map(d)
What is a Higher Order Function?

A function that does at least one of the following is a


Higher Order Function.
• Takes one or more functions as arguments.
• Returns a function as its result.
• So, let's assume I have a function F1. By default, F1 is a
first-class function, so I should be able to do those
three things with F1. Right?
Now, let's assume I have another function F2. If F2 can
take F1 as an argument, F2 is a higher order function.
Right? That's what the definition says.
Now, let's take a working example. You can ignore the
syntax for now and just focus on the concept.
What is an Anonymous Function?

• A standard function has a name, a list of


parameters, a return type, and a body. Right?
If you don't give a name to a function, it's an
anonymous function. Simple, isn't it? Let me
show you one simple example.
• (i:Int) => { i*2 } :Int
• The above code is the syntax for the
anonymous function. First part is the list of
parameters, then body and return type. That's
it. No name. The return type is optional. So, if
you leave it, Scala will automatically infer it.
What is the purpose of an anonymous
function?
• Why would you want to create an anonymous
function? Well, the answer is simple.
There might be scenarios where you want to
create an inline function for a one-time usage,
and giving them a name doesn't make any sense,
because you don't want to use them anywhere
else. In those cases, creating an anonymous
function is quite convenient. Let me show you an
example. I am going to create a function that
returns a function. Pay a little attention to the
syntax because the syntax for creating a function
that returns another function is little tricky.
What is Immutability?

• The literal meaning of Immutability is unable to change. In


a simple sense, In an FP world, we create values or objects
by initializing them. Then we use them, but we don't change
their values or their state. If we need, we create a new one,
but we don't modify the existing object's state.
Let me give you some examples in Scala. Scala has two
types of variables.
• var
• val
• The var stands for a variable, and the val stands for value.
You can initialize a var, and later you can reassign a new
value to the var.
• var s = "Hello World!“
• //After initializing it once, you can change it
later.
• s = "Hello Scala!“

• val v = "You can't change me.“


• //Let me try to reassign it.
• v = "Let me try."
• And above all of that even if we try to do it, I
wonder what the benefits are? Why do we want
to adopt the limitation of immutable? So, behind
all such arguments, we have just two questions.
• How can we program without a variable?
• What are the benefits?
• Let's start with the benefits. At this stage, I can
give you two advantages.
• It helps us to adopt a mathematical approach.
• It helps us to avoid various problems.
• V=3+1

• Immutability helps us to adopt a mathematical


approach.

• sum(3, 1) returns 4
• Immutability helps us to avoid various
problem.
• Immutable objects are more thread safe.
• If you are in concurrent programming
immutability makes your task simple and easy.
What is Recursion?

• A recursion is a programming technique in which


a function calls itself.
• You can replace most of the loops with a
recursion. We use recursion extensively in
functional programming. I know, recursion is one
of the toughest things to use. But with some
practice, you should be able to use it as a
powerful tool.
Let me convert the factorial function to a
recursive solution.
• def rFactorial(n: Int): Int =
• {
• if (n <= 0)
• return 1
• else
• return n * rFactorial(n - 1)
• }
What is a Trail recursion?

• You already know recursion. We just talked


about it. But there is another concept
associated with recursion. The tails recursion.
So, What is the Tail recursion?
Let's first understand the Tail.
• If we have list
• (54321)
• 5 is head and ( 4 3 2 1 ) is Tail.
How to approach recursion
When I need to think something recursively, I
follow these three steps.
• Identify the List.
• Implement the termination condition.
• Compute the head and recurs with the tail.
Is a loop better than a recursion?

• If you consider memory requirements and performance, the


answer is a definite Yes. So, loops are more efficient than
recursion. Scala compiler knows this and tries to optimize the
recursive calls.
But we must redesign the recursion is a way that there are no
unfinished operations for the next recursive call. That is where the
tail recursion come in.
So, By definition, A tail call or a tail recursion is a function call
performed as the last action.
What does it mean?
It means that your recursive call should be the last operation in
your function.
If we look at the factorial function, it waits for the recursive call
and then multiplies the result with n. So, the multiplication is the
last action.
To make it a tail recursive, we need to change it in a way that
recursive call becomes the final action instead of the
multiplication.
How to implement tail recursion?

• Not that simple but yes, you can do it by applying


some trick.
In the old logic, the first multiplication happens
when we reach the termination condition, and we
get one as the last number. Instead of returning
one at the termination, we can take it in the
beginning and perform the multiplication before
the recursion call.
To implement this trick, you need two input
parameters.
.
• Here is the code.
Statements

• A statement is the smallest stand alone


element that expresses some action to be
carried out. For example.
• println("Hello ")

• This one is a statement. There is another


common term. Definition or Declaration. For
Example.
• var r = ""
• We often call it a definition. A definition is also
a kind of statement. It is a statement that
defines something. With this information, we
can say that a program is nothing but a
sequence of statements.
• We can now say that a program is nothing but
a sequence of statements that modify some
program state.
Functional Statements
• In FP model, we have definitions and
statements, but every statement should have
a capability to return a value. That's the
ground rule for functional statements. This
rule is even valid for print statement. Let me
show you.
• val x = println("Hello")
• So, the println function returns a Unit. The
unit is like void in Java programming. But it is
not exactly same as void. The void means
nothing whereas unit has a value. We
represent the unit value using () symbol.
• the benefits of a statement returning a value. I mean, we learned
that functions return a value but why do we want every statement
to return a value. What is the benefit?
When you start practicing FP approach, you will realize that using
functional statements allows us to reduce the number of variables
in our code. Removing the variables from your code helps you to
achieve immutability. I mean, if you don't have a variable, you
don't need to mutate it. Right?
Let's come back to the myResult function. This code is imperative.
Let's make it functional.

• def myResult(m:Int) = if(m >= 50) "passed" else "failed"

• That's it. You can print your result by calling this function.
• println(myResult(65))
• The 'if' statement returns a value hence we
don't need a variable. The functional version
of the code is concise, and the returning
statement helps us to eliminate variables and
achieve immutability.
Strict and Lazy Evaluations

• Lazy evaluation is an important idea in


Functional Programming world. The idea is
simple to understand. However, it is a bit
difficult to implement. The Strict means,
evaluate the expression now and the lazy
means, evaluate it on the first use.
Strict Evaluations

Let me start with the strict evaluation and show


you some examples. I will try to explain the strict
behaviour in three different scenarios.
• Variable assignment
• Function parameters
• Higher order functions
The first two scenarios are simple, and we have
been using it for a long time. Let’s take simple
example.
• The default method is the strict evaluation. That
means Scala will evaluate the expression
immediately. There is one performance issue with
the strict evaluation in the case of a higher order
function. If the parameter is a function, Scala
evaluates it on every use. You can overcome this
limitation by caching the parameter in a val.
Now the lazy evaluation.
You can make an expression lazy by using a lazy
val. There is no explicit syntax to make a function
parameter lazy. However, you can make a lazy
function by caching it to a lazy val.
pattern matching?

• When we talk about pattern matching, the


first thing that comes to our mind is a string
matching or a regular expression. But in the
context of functional programming, this
terminology takes a new meaning. Instead of
regular expression matching, the Functional
Programming is going to look into matching
objects against other objects.
• What does it mean?
The most fundamental example of object
comparison is a type checking. Assume you
got an object, and you want to test it for
following possibilities.
• Does it match with a String Object?
• Does it match with an Integer Object?
• Does it match with a Double Object?
• Or Is it something else?
• val messageList = List(
Message("[email protected]", "Message text 1"),
Message("7742394590", "Message text 2"),
Message("8326192398", "Message text 3"),
Message("[email protected]", "Message text 4"),
Message("[email protected]", "Message text 5"),
Message("[email protected]", "Message text 6"))
• Take two messages from the list. (Deconstruct)
• Extract the sender's id from both messages. (Deconstruct)
• Check if both are email addresses. (Type test)
• If yes, ignore the domain part and extract only the
username. (Deconstruct)
• Check if both usernames are same. (if Condition)
• If yes, return a positive answer. (Action)
• If no, remove the head of the list and repeat with the tail of
the list. (Deconstruct and repeat)
• If you reach the end of this list, return a negative answer.
(Action)
Closures
• A closure is a function. Like any other Scala
function, a closure may be pure or impure. It
may be named or anonymous, but it is
primarily a function. You might be wondering
then why we call it a Closure. There is a small
difference.
• def getHike(salary:Double) = salary * p/100

• Let's look at the above function definition. It takes


your salary as input. Then it calculates and returns
your hike. Right?
Have you noticed that p? What is that? That's the
percentage of the hike. But from where is it
coming? I mean, we are not passing p as a
parameter. We haven't defined p as a local value.
The p doesn't have any definition or meaning
within the function.
• So, what is p?
The p is a free variable for this function.
A Function that uses one or more free
variables is known as a closure. That's the only
difference between a Function and a Closure.
A closure uses one or more free variables. If
you try to compile the above function, you
willget an error as compiler can't find p.
So, we still have one pending question. Where
will the compiler find the p?
• The answer is straightforward. The Scala
compiler investigate the so-called nearest
local lexical environment, in which that
function was defined and tries to find a
binding. So, let me define p outside the
function and compile the function once gain.
• Function Composition
• Function composition is combining 2 or more
functions to make a new one.
• Shared States
• Shared states is an importance concept in
OOP programming. Basically, It’s adding
properties to objects. For example, if a
HardDisk is an Object, Storage Capacity and
Disk Size can be added as properties.
The benefits of functional
programming
• Allows you to avoid confusing problems and errors in
the code
• Easier to test and execute Unit testing and debug FP
Code.
• Parallel processing and concurrency
• Hot code deployment and fault tolerance
• Offers better modularity with a shorter code
• Increased productivity of the developer
• Supports Nested Functions
• Functional Constructs like Lazy Map & Lists, etc.
• Allows effective use of Lambda Calculus
Limitations of Functional
Programming
• Functional programming paradigm is not easy, so
it is difficult to understand for the beginner
• Hard to maintain as many objects evolve during
the coding
• Needs lots of mocking and extensive
environmental setup
• Re-use is very complicated and needs constantly
refactoring
• Objects may not represent the problem correctly
Functional Programming vs.
Object-oriented Programming
Imperative Vs Functional Languages
• Imperative Languages
– These languages contain variables
– Programs written using imperative languages maintains
“state”
– There are side effects
– Repetition is through iteration

• Functional Languages
– Pure functional languages does not have variables
– There is no “state” in functional programs
– There are no side effects
– Repetition is through recursion
Functional Language Examples
• LISP
• Scheme
• ML
• Haskell
• OCaml
• F#
Mathematical Function
• A mathematical function is a mapping of
member in domain set to a member in range
set.
Simple Functions
• Function definitions are written as function
name followed by a list of parameters in
parentheses.

Ex: cube(x) = x * x * x

Ex: cube(2) = 2 * 2 * 2 = 8
Simple Functions (cont...)
• A lambda expression is a method for defining nameless functions.

• The lambda expression is the function itself, which is nameless.

Ex: λ(x)x * x * x

• The formal computation model for defining function, function


application and recursion using lambda expressions is called as
lambda calculus.

Ex: Application of lambda expression:

(λ(x)x * x * x)(2) gives 8


Functional Forms
• A functional form or higher order function is a
function that takes one or more functions as
parameters or produces a function as its
result, or both.

• Examples of functional forms:


– Function composition
– Apply-to-all
Function Composition
• A function composition is a functional form which
has two functional parameters and yields a
function whose value is the first function applied
to the result of the second.

Notation: h=fog

Example: f(x) = x + 2, g(x) = 3 * x


then h is defined as:
h(x) = f(g(x)) = (3*x) + 2
Apply-to-all
• Apply-to-all is a functional form which takes a
functional parameters, applies it each value in
a list and yields a list or sequence as result.

Notation: α(f, (val1, val2, ....))

Ex: h(x) = x * x
then α(h, (2, 3, 4)) yields (4, 9, 16)
LISP
• LISP is the first functional programming language.

• Developed by John McCarthy at MIT in 1959.

• Two categories of data items in LISP are: atoms and lists.

• Atoms are either symbols, in the form of identifiers, or


numeric literals.

• List elements are pairs, where the first part is the data of
the element and second part can be a pointer to an atom, a
pointer to another element, or the empty list.
LISP (cont...)
• Lists are specified in LISP by delimiting their
elements with parentheses.

Ex: (A B C D) is a simple list.

(A (B C) D (E (F G))) is a nested list.

• Lists are internally maintained as linked lists in


LISP as shown in next slide.
LISP (cont...)
LISP Interpreter
• A universal LISP function is a function which can evaluate any
other function in LISP.

• Function calls were specified in a prefix list form originally called


Cambridge Polish notation:

(function_name arg1 ... argn)

• For example, if + is a function that takes two or more numeric


parameters, then:

(+ 5 7) yields 12
(+ 3 4 7 6) yields 20
LISP Interpreter (cont...)
• The lambda notation was chosen to specify function
definitions.

(function_name (LAMBDA (arg1 ... argn) expression))

• LISP functions specified using the above notation are called


as S-expressions or symbolic expressions.

• An S-expression can be either an atom or a list.

• McCarthy developed a universal function capable of


evaluating any other function. It was named EVAL.
LISP Interpreter (cont...)
• Later EVAL was used to construct LISP
interpreter.

• LISP’s only notation was S-expressions.

• Scoping used in LISP is dynamic scoping.


Scheme
• Scheme is a dialect of LISP, developed at MIT by
Sussman and Steele in 1970s.

• Scheme is very small in size and is popular in


academic circles.

• Scheme is type less.

• Scheme uses static scoping.


Scheme : Primitive Numeric Functions
• Scheme includes primitive functions for the basic arithmetic
operations like +, -, *, /.

Ex: Expression Value


42 42
(* 3 7) 21
(+ 5 7 8) 20
(- 5 6) -1
(-15 7 2) 6
(-24 (* 4 3)) 12

• Other numeric functions in Scheme are: MODULO, ROUND, MAX,


MIN, LOG, SIN and SQRT.
Scheme : Defining Functions
• A Scheme program is a collection of function
definitions.

• In Scheme, a nameless function is defined using the


LAMBDA word:

(LAMBDA (x) (* x x))

• The above function can be applied as:

((LAMBDA (x) (* x x)) 7) yields 49


Scheme : Defining Functions (cont...)

• Scheme’s special function DEFINE serves two


fundamental needs:
– To bind a name to a value
– To bind a name to a lambda expression

• Examples of DEFINE to bind a name to a value:


(DEFINE pi 3.14159)
(DEFINE two_pi (* 2 pi))
Scheme : Defining Functions (cont...)
• Syntax of DEFINE to bind a name to a lambda
expression is:

(DEFINE (function_name params)


(expression))

Ex:
(DEFINE (square number)
(* number number))

(square 5) yields 25
Scheme : Numeric Predicate Functions

• A predicate function is one that returns a Boolean value.

• Some predicate functions in Scheme:


Function Meaning
= Equal
<> Not equal
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
EVEN? Is it an even number?
ODD? Is it an odd number?
ZERO? Is it zero?

• Boolean values in Scheme are #T and #F (or #t and #f).


Scheme : Control Flow
• Scheme uses three different constructs for control flow:
– IF
– COND
– Recursion

• The syntax of IF selector function is:


(IF predicate the_expr else_expr)

Ex:
(DEFINE (factorial n)
(IF (<= n 1) 1
(* n (factorial (- n 1)))
))
Scheme : Control Flow (cont...)
• Example on COND selector:

(DEFINE (leap? year)


(COND
((ZERO? (MODULO year 400)) #T)
((ZERO? (MODULO year 100)) #F)
(ELSE (ZERO? (MODULO year 4))) ))
Scheme : List Functions
• The primitive function QUOTE returns the parameter without change.

Ex: (QUOTE A) returns A


(QUOTE (A B C)) returns (A B C)

• The CAR function accepts a list and returns the first element in the list.

Ex: (CAR ‘(A B C)) returns A


(CAR ‘((A B) C D)) returns (A B)

• The CDR functions accepts a list and returns remaining elements in the list except
the first element.

Ex: (CDR ‘(A B C)) returns (B C)


(CDR ‘((A B) C D)) returns (C D)
Scheme : List Functions (cont...)
• The CONS function accepts two parameters and creates a list
of those parameters.

Ex: (CONS ‘A ‘(B C)) returns (A B C)


(CONS ‘(A B) ‘(C D)) returns ((A B) C D)

• The LIST accepts multiple parameters and creates a list of


those parameters.

Ex: (LIST ‘apple ‘orange ‘grape) returns


(apple orange grape)
Scheme : Predicate Functions for Symbolic
Atoms and Lists
• The EQ? function takes two atoms as
parameters and #T if they point to same
memory location. Otherwise, it returns #F.

Ex: (EQ? ‘A ‘A) returns #T


(EQ? ‘A ‘B) returns #F
Scheme : Predicate Functions for
Symbolic Atoms and Lists (cont...)
• The EQV? function takes two atoms as
parameters and compares the content instead
of pointers. It #T if they are same or #F if they
are not same.

Ex: (EQV? ‘A ‘A) returns #T


(EQV? 3.4 (+ 3 0.4)) returns #T
(EQV? 3.0 3) returns #F
Scheme : Predicate Functions for
Symbolic Atoms and Lists (cont...)
• The LIST? function returns #T if its single
argument is a list and #F otherwise.

Ex: (LIST? ‘(X Y)) returns #T


(LIST? ‘X) returns #F
Scheme : LET
• LET is a function used to create a local scope in which
names are temporarily bound to the values of expressions.

• LET is often used to factor out common sub-expressions


from more complicated expressions.

• LET is a shorthand for a LAMBDA expression applied to a


parameter.

Ex: (LET ((alpha 7)) (* 5 alpha))


((LAMBDA (alpha) (* 5 alpha)) 7)
Meta Language [ML]
• ML is a static scoped language like Scheme.

• ML was developed by Milner in 1990.

• Differences between ML and Scheme are:


– ML is a strongly typed language.
– ML uses syntax that is close to imperative language.

• A table called evaluation environment stores the


names of all declared identifiers in a program along
with their types.
ML (cont...)
• Function declaration in ML:
fun function_name(params) = expression;

Ex:
fun circum(r) = 3.1415 * r * r;
fun times10(x) = 10 * x;
fun square(x) = x * x;

• Function call:
square(2.75);
ML (cont...)
• Types can be specified as follows:

fun square(x : real) = x * x;


fun square(x) = (x : real) * x;
fun square(x) = x * (x : real);
ML (cont...)
• The if statement in ML is as follows:
if expression then then_expr else else_expr

Ex:
fun fact(n : int) : int = if n <= 1 then 1
else n * fact (n – 1);
ML (cont...)
• ML supports lists and list operations.

• The hd, tl and :: are ML’s versions of Scheme’s


CAR, CDR and CONS.

• Names are bound to values with value declaration


statements of the form:
val new_name = expression;

Ex: val distance = time * speed;


ML (cont...)
• Normal use of val is in let expression:

let
val radius = 2.7;
val pi = 3.14159;
in
pi * radius * radius;
end;
ML (cont...)
• The map function takes a single parameter, which
is a function. The resulting function takes a list as
a parameter and applies the function to each
element in the list.

Ex:
fun cube x = x * x * x;
val cubeList = map cube;
val newList = cubeList [1, 3, 5];

You might also like