0% found this document useful (0 votes)
4 views62 pages

Lecture 7

This document covers the foundations of functional programming, focusing on functions as basic abstractions, their properties (injective, surjective, bijective), and practical implementations in Java. It includes examples of functions for adding integers, strings, and generic types, as well as iterating through collections using consumer interfaces. The document also discusses the use of functional interfaces from the Java Development Kit (JDK) to improve code efficiency.

Uploaded by

moh570kamil
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views62 pages

Lecture 7

This document covers the foundations of functional programming, focusing on functions as basic abstractions, their properties (injective, surjective, bijective), and practical implementations in Java. It includes examples of functions for adding integers, strings, and generic types, as well as iterating through collections using consumer interfaces. The document also discusses the use of functional interfaces from the Java Development Kit (JDK) to improve code efficiency.

Uploaded by

moh570kamil
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 62

CS 474: OOLE

Lecture 7: Foundations of
Functional Programming

Instructor: Dr. Mark Grechanik


University of Illinois at Chicago
Function as a Basic Abstraction

Department of Computer Science, the


University of Illinois at Chicago 2
Function as a Basic Abstraction
n val result = f(p1, p2, …)
n Function is a relation such that each element of one set is
associated with at most one element of another set
q Other names: mapping, transformation, and operator
q f: A ® B, the set A is the domain of f and the set B is the codomain of f; f
has type A ® B
n Any ordered sequence of objects is a function
n range(f) ={f(a) | a Î A} is the set of elements in the codomain B that
are associated with some element of A
n For any subset S A, the image of S, f(S), under f is the set of
elements in B that are associated with some element of S
q f(S) = {f(x) | x Î S}
n Preimage of T Ì B under f, f-1(T), is the set of elements in A that
associate with elements in T
q f-1(T) = {a Î A | f(a) Î T}

3
Properties of Functions
A B

n Injective
q One-to-one or embedding if a function maps distinct
elements of A to distinct elements of B A B

n Surjective
q Onto if the range of a function is the codomain of B
A B
n Bijective = injective + surjective
n Inverse Functions
q If f: A ® B is a bijection, then there is an inverse function
g: B ® A s.t. g(b) = a if f(a) = b
n If there is an injection from A to B, then there is a
surjection from B to A, and conversely

4
Modeling Function to Add Integers

interface AddIntegersFunction {
default Integer add(Integer a, Integer b){
return a+b;
}
}

public class ModelFunctions {


public static void main(String[] args) {
Integer result = new AddIntegersFunction() {
}.add(3,5);
}
Integer Integer
}
(a,b)

Department of Computer Science, the


University of Illinois at Chicago 5
Function to Add Integers
interface AddIntegersAbstractFunction {
Integer add(Integer a, Integer b);
}

public class ModelFunctions {


public static void main(String[] args) {
result = new AddIntegersAbstractFunction() {
@Override
public Integer add(Integer a, Integer b) {
return a+b;
}
}.add(2, 11);
System.out.println(result);
}
} Integer Integer
(a,b)

Department of Computer Science, the


University of Illinois at Chicago 6
Function to Add Integers
interface AddIntegersAbstractFunction {
Integer add(Integer a, Integer b);
}

public class ModelFunctions {


public static void main(String[] args) {
result = new AddIntegersAbstractFunction() {
@Override
public Integer add(Integer a, Integer b) {
assert (a>=0 && b >= 0);
return (b==0) ? a : add(++a,--b);
}
}.add(2, 11);
System.out.println(result);
} Integer Integer
}
(a,b)

Department of Computer Science, the


University of Illinois at Chicago 7
Function to Add Strings
interface AddStringsAbstractFunction {
String add(String a, String b);
}

public class ModelFunctions {


public static void main(String[] args) {
System.out.println(new AddStringsAbstractFunction() {
@Override
public String add(String a, String b) {
return a.concat(b);
}
}.add("Mark G. teaches ", "CS476"));

}
} String String
(a,b)

Department of Computer Science, the


University of Illinois at Chicago 8
Function to Add <Your Type Here>
interface AddGenericFunction<T> {
T add(T a, T b);
}

public class ModelFunctions {


public static void main(String[] args) {
System.out.println(new AddGenericFunction<Double>() {
@Override
public Double add(Double a, Double b) {
return a + b;
}
}.add(23.5, 7.5));
}
? ?
}
(a,b)

Department of Computer Science, the


University of Illinois at Chicago 9
Function to Add <Your Type Here>
interface AddGenericFunction<T> {
T add(T a, T b);
}

public class ModelFunctions {


public static void main(String[] args) {
System.out.println(new AddGenericFunction<SomeType>() {
@Override
public SomeType add(SomeType a, SomeType b) {
return a.SomeMethod(b);
}
}.add(23.5, 7.5));
}
? ?
}
(a,b)

Department of Computer Science, the


University of Illinois at Chicago 10
Add Values of Different Types

interface AddDiverseGenericFunction<A,B,C> {
C add(A a, B b);
}

public class ModelFunctions {


public static void main(String[] args) {
System.out.println(new AddDiverseGenericFunction<Integer, Double, String>() {
@Override
public String add (Integer a, Double b){
return a.toString().concat(b.toString());
}
}.add(10, 1.1E10));

}
}
Integer, Double String
(a,b)

Department of Computer Science, the


University of Illinois at Chicago 11
Do Functions Need Names?

interface AddDiverseGenericEmptyFunction<A,B,C> {
}

public class ModelFunctions {


public static void main(String[] args) {
System.out.println(new AddDiverseGenericEmptyFunction<Integer, Double, String>()
{
public String add (Integer a, Double b){
return a.toString().concat(b.toString());
}
}.add(10, 1.1E10));
}
}
? x ? x…x ? ?

Department of Computer Science, the


University of Illinois at Chicago 12
Loaner Pattern
import java.io.*;

interface FileHandler<T>{
T handler(File file);
}

public class LoanerPattern {


public Boolean processFile(FileHandler<Boolean> handle) {
return handle.handler(new File("cs474grades.txt"));
}
public static void main(String[] args) {
new LoanerPattern().processFile(new FileHandler<Boolean>() {
@Override
public Boolean handler(File file) {
try {
return file.isDirectory();
}
finally {
file.delete();
}
}
});
}
}

Department of Computer Science, the


University of Illinois at Chicago 13
Iterating Through Collections
import java.util.ArrayList;
import java.util.List;

class DecartesCoordinates {
private Integer x = 0;
private Integer y = 0;

public DecartesCoordinates(Integer x, Integer y) {


this.x = x;
this.y = y;
}

public Double distance() {


return Math.sqrt(x * x + y * y);
}
}

public class PuttingConsumerInterface2Use {


public static void main(String[] args) {
List<DecartesCoordinates> coordinates = new ArrayList<DecartesCoordinates>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));
for (DecartesCoordinates c : coordinates) {
System.out.println(c.distance());
}
}
}

Department of Computer Science, the


University of Illinois at Chicago 14
Iterating Through Collections
import java.util.ArrayList;
import java.util.List;
class GradePoint {
private Integer grade = 0;
private Double weight = 0e0;

public GradePoint(Integer grade, Double weight) {


this.grade = grade;
this.weight = weight;
}

public Double grade() {


return grade * weight;
}
}
public class ComputingGpaAverage {
public static void main(String[] args) {
List<GradePoint> grades = new ArrayList<GradePoint>();
grades.add(new GradePoint(5, 1.));
grades.add(new GradePoint(2, 0.2));
Double sum = 0e0;
for (GradePoint g : grades) {
sum += g.grade();
}
System.out.println(sum/grades.size());
}
}

Department of Computer Science, the


University of Illinois at Chicago 15
Iterating Through Collections
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

class GradePoint {
class DecartesCoordinates {
private Integer grade = 0;
private Integer x = 0;
private Double weight = 0e0;
private Integer y = 0;
public GradePoint(Integer grade, Double weight) {
public DecartesCoordinates(Integer x, Integer y) { this.grade = grade;
this.x = x; this.weight = weight;
this.y = y; }
}
public Double grade() {
public Double distance() { return grade * weight;
return Math.sqrt(x * x + y * y); }
} }
}
public class ComputingGpaAverage {
public class PuttingConsumerInterface2Use { public static void main(String[] args) {
public static void main(String[] args) { List<GradePoint> grades =
List<DecartesCoordinates> coordinates = new ArrayList<GradePoint>();
new ArrayList<DecartesCoordinates>(); grades.add(new GradePoint(5, 1.));
coordinates.add(new DecartesCoordinates(1, 2)); grades.add(new GradePoint(2, 0.2));
coordinates.add(new DecartesCoordinates(2, 10)); Double sum = 0e0;
for (GradePoint g : grades) {
for (DecartesCoordinates c : coordinates) {
sum += g.grade();
System.out.println(c.distance());
}
}
System.out.println(sum/grades.size());
}
}
} }

Department of Computer Science, the


University of Illinois at Chicago 16
Seeing a Pattern

listVariable.forEach( new SomeGenericInterface<Type>() {


public void accept( Type var ) {
var.method(p1,p2,…);
}
};
n Move the iterator inside collection classes/interfaces;
n Define some parameterized SomeGenericInterface as
the input to the iterator;
n Allow clients to define the behavior of the abstract
method accept to implement the needed functionality.
Department of Computer Science, the
University of Illinois at Chicago 17
Interface Consumer From the JDK
@FunctionalInterface
public interface Consumer<T> {

/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);

/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

Department of Computer Science, the


University of Illinois at Chicago 18
And Improve Using Consumer

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class PuttingConsumerInterface2Use implements Consumer<> {


DecartesCoordinates
public static void main(String[] args) {
List<DecartesCoordinates> coordinates = new ArrayList<>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));

coordinates.forEach(new PuttingConsumerInterface2Use());
}

@Override
public void accept(DecartesCoordinates decartesCoordinates) {
System.out.println(decartesCoordinates.distance());
}
}

Department of Computer Science, the


University of Illinois at Chicago 19
And Improve Using Consumer

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class PuttingConsumerInterface2Use implements Consumer<DecartesCoordinates> {

public static void main(String[] args) {


List<DecartesCoordinates> coordinates = new ArrayList<>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));

coordinates.forEach(new PuttingConsumerInterface2Use());
}

@Override
public void accept(DecartesCoordinates decartesCoordinates) {
System.out.println(decartesCoordinates.distance());
}
}

Department of Computer Science, the


University of Illinois at Chicago 20
And Improve Using Consumer

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class PuttingConsumerInterface2Use implements Consumer<DecartesCoordinates> {

public static void main(String[] args) {


List<DecartesCoordinates> coordinates = new ArrayList<>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));

coordinates.forEach(elem.mapsTo(elem.distance()));
}

@Override
public void accept(DecartesCoordinates decartesCoordinates) {
System.out.println(decartesCoordinates.distance());
}
}

Department of Computer Science, the


University of Illinois at Chicago 21
And Improve Using Consumer
DecartesCoordinates Double

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class PuttingConsumerInterface2Use implements Consumer<DecartesCoordinates> {

public static void main(String[] args) {


List<DecartesCoordinates> coordinates = new ArrayList<>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));

coordinates.forEach(elem.mapsTo(elem.distance()));
}

@Override
public void accept(DecartesCoordinates decartesCoordinates) {
System.out.println(decartesCoordinates.distance());
}
}

Department of Computer Science, the


University of Illinois at Chicago 22
And Improve Using Consumer
DecartesCoordinates Double

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
F: DecartesCoordinates => Double

public class PuttingConsumerInterface2Use implements Consumer<DecartesCoordinates> {

public static void main(String[] args) {


List<DecartesCoordinates> coordinates = new ArrayList<>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));

coordinates.forEach(elem.mapsTo(elem.distance()));
}

@Override
public void accept(DecartesCoordinates decartesCoordinates) {
System.out.println(decartesCoordinates.distance());
}
}

Department of Computer Science, the


University of Illinois at Chicago 23
And Improve Using Consumer
DecartesCoordinates Double

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
F: DecartesCoordinates => Double

public class PuttingConsumerInterface2Use implements Consumer<DecartesCoordinates> {

public static void main(String[] args) {


List<DecartesCoordinates> coordinates = new ArrayList<>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));

coordinates.forEach(elem=>elem.distance());
}

@Override
public void accept(DecartesCoordinates decartesCoordinates) {
System.out.println(decartesCoordinates.distance());
}
}

Department of Computer Science, the


University of Illinois at Chicago 24
And Improve Using Consumer
DecartesCoordinates Double

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
F: DecartesCoordinates => Double

public class PuttingConsumerInterface2Use implements Consumer<DecartesCoordinates> {

public static void main(String[] args) {


List<DecartesCoordinates> coordinates = new ArrayList<>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));

coordinates.forEach(elem->distance());
}

@Override
public void accept(DecartesCoordinates decartesCoordinates) {
System.out.println(decartesCoordinates.distance());
}
}

Department of Computer Science, the


University of Illinois at Chicago 25
And Improve Using Consumer
DecartesCoordinates Double

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
F: DecartesCoordinates => Double

public class PuttingConsumerInterface2Use implements Consumer<DecartesCoordinates> {

public static void main(String[] args) {


List<DecartesCoordinates> coordinates = new ArrayList<>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));

coordinates.forEach(distance);
}

@Override
public void accept(DecartesCoordinates decartesCoordinates) {
System.out.println(decartesCoordinates.distance());
}
}

Department of Computer Science, the


University of Illinois at Chicago 26
Completely Generic Processing
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class PuttingGenericConsumerInterface2Use<T> {


List<T> list = null;
Consumer<T> process = null;
public PuttingGenericConsumerInterface2Use(List<T> list, Consumer<T> process) {
this.list = list;
this.process = process;
}

public void Process(){


list.forEach(process);
}

public static void main(String[] args) {


List<DecartesCoordinates> coordinates = new ArrayList<DecartesCoordinates>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));
new PuttingGenericConsumerInterface2Use<DecartesCoordinates>(coordinates,
new Consumer<DecartesCoordinates>(){
public void accept(DecartesCoordinates decartesCoordinates) {
System.out.println(decartesCoordinates.distance());
}
}).Process();
}
}

Department of Computer Science, the


University of Illinois at Chicago 27
Let Us Improve the Syntax!
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class MakeItFunctional<T> {


public MakeItFunctional(List<T> list, Consumer<T> process) {
list.forEach(process);
}

public static void main(String[] args) {


List<DecartesCoordinates> coordinates =
new ArrayList<DecartesCoordinates>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));
new MakeItFunctional<DecartesCoordinates>(coordinates,
(DecartesCoordinates c)->System.out.println(c.distance()));
}
}

Department of Computer Science, the


University of Illinois at Chicago 28
Let Us Improve the Syntax!
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class MakeItFunctional<T> {


public MakeItFunctional(List<T> list, Consumer<T> process) {
list.forEach(process);
}

public static void main(String[] args) {


List<DecartesCoordinates> coordinates =
new ArrayList<DecartesCoordinates>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));
new MakeItFunctional<DecartesCoordinates>(coordinates,
(DecartesCoordinates c)->System.out.println(c.distance()));
}
}

Department of Computer Science, the


University of Illinois at Chicago 29
Finally,… What Was All That?!!

import java.util.ArrayList;
import java.util.List;

public class SimpleAndFunctionalListProcessing {

public static void main(String[] args) {


List<DecartesCoordinates> coordinates = new ArrayList<>();
coordinates.add(new DecartesCoordinates(1, 2));
coordinates.add(new DecartesCoordinates(2, 10));
coordinates.forEach(c->System.out.println(c.distance()));
}

Department of Computer Science, the


University of Illinois at Chicago 30
What Does This Program Output?

public class Hello {


Runnable r1 = ()->(System.out.println(this);};
Runnable r2 = ()->(System.out.println(toString());};
public String toString(){return “Howdy!”;}

public static void main(String args) {


new Hello().r1.run();
new Hello().r2.run();
}

Department of Computer Science, the


University of Illinois at Chicago 31
Lambda Examples

(int x, int y) -> x + y


() -> { System.gc(); return 0; }
Class Func {
Object I, j;
IntUnaryOperator iuo =
i->{int k = 3; return i + k;}
}
DoubleUnaryOperator sr = x->Math.sqrt(x)
Department of Computer Science, the
University of Illinois at Chicago 32
Implementing Factorial With Lambda

public class Factorial {


IntUnaryOperator fact;
public Factorial() {
fact = i -> i == 0 ? 1 :
i*fact.applyAsInt(i-1);
}
}
F: T -> T
Department of Computer Science, the
University of Illinois at Chicago 33
JLS 15.27: Lambda Expressions

n A lambda expression is like a method: it provides a list of


formal parameters and a body - an expression or block -
expressed in terms of those parameters.
n LambdaExpression:
LambdaParameters -> LambdaBody
n JLS 15.2: When some expressions appear in certain
contexts, they are considered poly expressions.
n It is a compile-time error if a lambda expression occurs
in a program in someplace other than an assignment
context (§5.2), an invocation context (§5.3), or a casting
context
Department of Computer Science, the
University of Illinois at Chicago 34
Evaluating Lambda Expressions

n Evaluation of a lambda expression produces an


instance of a functional interface.
n Lambda expression evaluation does not cause
the execution of the expression's body; instead,
this may occur at a later time when an
appropriate method of the functional interface is
invoked.
n Any local variable used but not declared in a
lambda body must be definitely assigned before
the lambda body, or a compile-time error occurs.
Department of Computer Science, the
University of Illinois at Chicago 35
15.27.4 Run-Time Evaluation of
Lambda Expressions
n At run time, evaluation of a lambda expression is similar
to evaluation of a class instance creation expression,
insofar as normal completion produces a reference to an
object.
n Evaluation of a lambda expression is distinct from
execution of the lambda body: (int p) -> p*2
n The value of a lambda expression is a reference to an
instance of a class with the following properties:
q The class implements the targeted functional interface type and, if the target type
is an intersection type, every other interface type mentioned in the intersection.
q The class overrides no other methods of the targeted functional interface type or
other interface types mentioned above, although it may override methods of the
Object class.
Department of Computer Science, the
University of Illinois at Chicago 36
Some Intuition And Definitions

n Consider the function f(x) =


println(x.distance())
q f(cp) is computed by substituting the value of cp for the
variable x in the body of the function &
reducing/evaluating it
n The function name is superfluous, we can write:
c->println(c.distance()), to say that the
function with the parameter c maps to the function
body
n Can a function take an argument as some function?
Department of Computer Science, the
University of Illinois at Chicago 37
Loaner Pattern
import java.io.*;
interface FileHandler<T>{
T handler(File file);
}
public class LoanerPattern {
public Boolean processFile(FileHandler<Boolean> handle) {
return handle.handler(new File("cs474grades.txt"));
}
public static void main(String[] args) {
new LoanerPattern().processFile(new FileHandler<Boolean>() {
@Override
public Boolean handler(File file) {
try {
return file.isDirectory();
}
finally {
file.delete();
}
}
});
}
}

Department of Computer Science, the


University of Illinois at Chicago 38
Formal Notation for Function Calculus

n λ<name>.<body> where <body>::=<expression>


n λc.println(c.distance())
n λc. λf f(c.distance())
n λc. λf f(c.distance())System.out.println
n λc. (λf f(c.distance())System.out.println)new DecartesCoordinate(...)

n λc. (f(c.distance())(f is replaced with


System.out.println)) new DecartesCoordinate(...)
n λc. (System.out.println(c.distance())) new DecartesCoordinate(...)
n (System.out.println(new DecartesCoordinate(…).distance()))

Department of Computer Science, the


University of Illinois at Chicago 39
Creation of the λ-calculus

Lambda calculus or λ-calculus was invented by


Alonzo Church in the 1930s (Alan Turing’s advisor)

Church’s aim was to use his lambda calculus as a


foundation for a formal theory of mathematics, in
order to establish which functions are ‘computable’
by means of an algorithm (and which are not).

Church’s goal was to replace the foundational


building blocks of math called sets with functions.

Department of Computer Science, the


University of Illinois at Chicago 40
λ expressions
n λ-calculus is a system for manipulating λ-expressions, which may be a
name to identify an abstraction point, a function to introduce an abstraction
or a function application to specialize an abstraction
q <expression>::=<name>|<function>|<application>
n λ-function is an abstraction over a λ-expression that has the form
<function>::= λ<name>.<body> where <body>::=<expression>
n <application> ::= (<function expression> <argument expression>)
where
q <function expression> ::= <expression>
q <argument expression> ::= <expression>
n A function application specializes an abstraction by providing a value for the
name.
n The function expression contains the abstraction to be specialized with the
argument expression.
n Name for abstraction is called the function’s bound variable and the
expression is called the function’s body
Department of Computer Science, the
University of Illinois at Chicago 41
Example Lambdas And Their ASTs

λf. λx.f(f x) or λfx.f(f x) λufx.f((u f x)

λf λu

λx λf

λx
left-associative
(u f)x
f fx

f
(f: Int->int, x: Int) => f(f(x))
(λf. (λx.(f(f x))) sqrt) 81 => x
(λx.(sqrt(sqrt x))) 81 =>
sqrt (sqrt 81) => 3 u f

Department of Computer Science, the


University of Illinois at Chicago 42
Semantics of λ-calculus

n How to evaluate an expression to a value


n The value of a function definition is not just
the function, but also the environment that
existed when the function was defined
q an environment is a map from variables to values
n The function with its environment is called a
closure
n Let us evaluate the following expression
λx.(λf.(λx.f 0) 2) λa.x) 1
Department of Computer Science, the
University of Illinois at Chicago 43
Evaluation of λx.(λf.(λx.f 0) 2) λa.x) 1

n first, x→1, then f→ λa.x,


and then x→2 λx 1

n Evaluate f 0
q f is the function λa.x, it returns the value λf λa
of x no matter what the argument is
x
q What is the value of x?
q The closure is f→ (λa.x, [x→1]) λx 2

f 0
Department of Computer Science, the
University of Illinois at Chicago 44
Identity λ functions

n An identity operation leaves its argument


unchanged
n λx.x is the identity function, its bound
variable is x and its body expression is the
name x
n In a function application, the bound
variable x is replaced by the argument
expression
n λx.x λx.x => λx.x => x
Department of Computer Science, the
University of Illinois at Chicago 45
λ functions
n λs.(s s); the bound variable is s and the body
expression is the function application named
s to the argument, s that has the same name
n (λx.x λs.(s s)) where λx.x is the function
expression and λs.(s s) is the arg expression
n Function expression is evaluated to x and is
replaced with λs.(s s) in the function
expression body
n (λvar.body)arg evaluates to body with arg
substituted for var
Department of Computer Science, the
University of Illinois at Chicago 46
Functions And Objects

Department of Computer Science, the


University of Illinois at Chicago 47
(Im)pure Functions

n Pure functions always evaluate the same


result value given the same argument values
n No side effects are produced by pure functions
n Impure functions are
q A function that uses a non-local variable
q A function that depends on some global state
q Random functions
q Functions that cause data to be sent to output ports
n But I/O functions can be viewed as pure
Department of Computer Science, the
University of Illinois at Chicago 48
Functions vs. Relations

n Any function can be defined as some kind of


binary relations
q f: A ® B is a binary relation from A to B s.t. no
two ordered pairs have the same first element
q If (a,b), (a,c) Î f then b = c
n Two functions are equal if they have the
same type and the same values for each
domain element

49
Partial and Total Functions

n A partial function from A to B is a function that is not


defined for some elements of A
q Division ÷ is a partial function of type R ´ R ® R
n A total function is a function that is defined on all its
domains
q Division ÷ is a total function of type R ´ (R – {0}) ® R
n How to transform a partial function into total function
q Shrink the domain
q Increase the size of the codomain A ® B È {somesymbol}
q Pick an error message to indicate an incorrect input

50
How to Construct Functions

n Composition
q (f ° g)(x) = f(g(x))
q cos: R+ ® R and log2 : R+ ® R, cos ° log2: R+ ® R
n The Map Function
q map(f, <x1,…xn>) = <f(x1),… f(xn)>
q map(+, <(1,2),(3,4),(5,6)>) = <3, 7, 11>
q map: (A ® B) ´ list(A) ® list(B)
n Importance of function composition concept

51
An Algebra of Functions
n Let S be a set, and let F be the set of all functions of
type S ® S. Let ° denote the operation of
compositions of functions. Then F is the set of an
algebra <F; °, id> where id is the identity function:
id °f = f °id = f for all functions f in F
n Algebras
q Groupoid: ° is a binary operation
q Semigroup: ° is an associative binary operation
q Monoid: ° is an associative binary operation with an identity
q Group: ° is an associative binary operation with an identity
and every element has an inverse

52
Recursion
n A function or procedure (a program that performs one or more
actions) is recursively defined if it is defined in terms of itself
n If A is an inductively defined set, then we can construct a function f
with domain A as follows
q For each basis element a in A, specify a value for f(a)
q Give rules that, for any inductively defined element a in A, will define f(a)
in terms of previously defined values of f
n Fibonacci numbers
q 0,1,1,2,3,5,8,13,21,34,55,…
q Let fib(n) be the nth Fibonacci number
q fib(0) = 0
q fib(1) = 1
q fib(n) = fib(n-1) + fib(n-2) for n >= 2
q fib(n+2) = fib(n+1) + fib(n) for n>0

53
Recursive Definition of Lists

n Define function f: N lists(N)


q f(n) = <n, n-1, n-2, …, 1, 0>
n Use cons operation to define f(n) in terms of
f(n-1)
q f(n) = <n, n-1, n-2, …, 1, 0> =
cons(n, <n-1, n-2, …, 1, 0>) = cons(n, f(n-1))
n We define f recursively
q f(0) = 0
q f(n) = cons(n, f(n-1))
q f(n) = if n == 0 then <0> else cons(n, f(n-1))

54
Program By Composing Functions

n Church’s model of computing is called the lambda


calculus
q based on the notion of parameterized expressions (with each
parameter introduced by an occurrence of the letter λ—hence the
notation’s name.
q Lambda calculus was the inspiration for functional programming
q one uses it to compute by substituting parameters into
expressions, just as one computes in a high level functional
program by passing arguments to functions
n The key idea: do everything by composing functions
q no mutable state
q no side effects

Department of Computer Science, the


University of Illinois at Chicago 55
Imperative Languages

n A variable is a changeable association


between a name and values
n The same name can be associated with
many different values
n Imperative languages consist of sequences
of commands that consist of assignments
which change the values of variables
n Imperative languages have a fixed command
execution order
Department of Computer Science, the
University of Illinois at Chicago 56
Functional Languages

Structured function calls


• Composed or nested by passing values

Names are introduced as the formal parameters of


functions
Recursion In Functional Programming
n Recursion (especially tail recursion) takes the
place of iteration
n In general, you can get the effect of a series
of assignments
x := 0 ...
x := expr1 ...
x := expr2 ...
from f3(f2(f1(0))), where each f expects the
value of x as an argument, f1 returns expr1,
and f2 returns expr2
Department of Computer Science, the
University of Illinois at Chicago 58
Looping Is Replaced
n Recursion even does a nifty job of replacing
looping
x := 0; i := 1; j := 100;
while i < j do
x := x + i*j; i := i + 1;
j := j - 1
end while
return x
becomes f(0,1,100), where
f(x,i,j) == if i < j then
f (x+i*j, i+1, j-1) else x
Department of Computer Science, the
University of Illinois at Chicago 59
Referential Transparency

n How can we easily compose functions?


n Each expression denotes a single value that
cannot be changed by evaluating the expression
or by allowing different parts of a program to
share the expression
n Imperative languages are referentially opaque
n Referentially transparent functions can be
memoized (transformed into equivalent functions
which cache results)

Department of Computer Science, the


University of Illinois at Chicago 60
Data Structures in FP

Explicit representation of nested data structs with explicit


changes to substructures

• No arrays, since their elements cannot be easily accessed at random without


assignments

Data structures are called purely functional if they guarantee


the (weak) equivalence of call-by-name, call-by-value and call-
by-need evaluation strategies, often by excluding destructive
modifications (updates) of entities in the program's running
environment.
• data Tree a = Null | Node Tree a Tree

Department of Computer Science, the


University of Illinois at Chicago 61
Reading
n Mandatory
q Functional Programming in Java: How functional
techniques improve your Java programs by
Pierre-Yves Saumont. Chapters 1,2
q G.J.Michaelson, An Introduction to Functional
Programming Through Lambda Calculus,
Addison-Wesley, ISBN 0-201-17812-5, 1988
Chapters 1-3
n Optional for undergrads, mandatory for grads
q https://docs.oracle.com/javase/specs/jls/se17/html/jls
-15.html#jls-15.27

Department of Computer Science, the


University of Illinois at Chicago

You might also like