Compiler Construction | Lecture 5 | Transformation by Term Rewriting
Ti1220 Lecture 7: Polymorphism
1. Lecture 6: Polymorphism
TI1220 2012-2013
Concepts of Programming Languages
Eelco Visser / TU Delft
2. Robin Milner is generally regarded as having
made three major contributions to computer
science. He developed LCF, one of the first tools for
automated theorem proving. The language he
developed for LCF, ML, was the first language with
polymorphic type inference and type-safe
exception handling. In a very different area, Milner
also developed a theoretical framework for
analyzing concurrent systems, the calculus of
communicating systems (CCS), and its successor, the
pi-calculus. At the time of his death, he was working
on bigraphs, a formalism for ubiquitous computing
subsuming CCS and the pi-calculus.[9]
http://en.wikipedia.org/wiki/Robin_Milner
5. Higher-order functions
• reducing code duplication, simplifying client code
Currying
• partial function applications
Writing new control structures
• growing the language
By-name parameters
• lazy evaluation
6. Search for files that end in ...
object FileMatcher {
private def filesHere = (new java.io.File(".")).listFiles
def filesEnding(query: String) =
for(file <- filesHere; if file.getName.endsWith(query))
yield file
}
7. Search for files that match a query ...
def filesEnding(query: String) =
for(file <- filesHere; if file.getName.endsWith(query))
yield file
def filesContaining(query: String) =
for(file <- filesHere;if file.getName.contains(query))
yield file
def filesRegex(query: String) =
for(file <- filesHere;if file.getName.matches(query))
yield file
def filesMatching(query: String, method) =
for(file <- filesHere;if file.getName.method(query))
yield file
Find the common pattern
8. Using a higher-order function
def filesMatching(query: String,
matcher: (String, String) => Boolean) = {
for (file <- filesHere; if matcher(file.getName, query))
yield file
}
def filesEnding(query: String) =
filesMatching(query, (x:String,y:String) => x.endsWith(y))
def filesContaining(query: String) =
filesMatching(query, _.contains(_))
def filesRegex(query: String) =
filesMatching(query, _.matches(_))
9. Using a higher-order function
def filesMatching(query: String,
matcher: (String, String) => Boolean) = {
for (file <- filesHere; if matcher(file.getName, query))
yield file
}
def filesEnding(query: String) =
filesMatching(query, _.endsWith(_))
def filesContaining(query: String) =
filesMatching(query, _.contains(_))
def filesRegex(query: String) =
filesMatching(query, _.matches(_))
18. def withPrintWriter(file: File)(op: PrintWriter => Unit) {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
val file = new File("date.txt")
withPrintWriter(file) { writer =>
writer.println(new java.util.Date)
}
Loan pattern using curried
higher-order functions
19. How to defer evaluation?
var assertionsEnabled = true
def myAssert(predicate: () => Boolean) =
if (assertionsEnabled && !predicate())
throw new AssertionError
myAssert(() => 5 > 3)
myAssert(5 > 3) // Won’t work, because missing () =>
20. Function arguments are
evaluated eagerly
def boolAssert(predicate: Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
scala> boolAssert(5 > 3)
scala> assertionsEnabled = false
assertionsEnabled: Boolean = false
scala> boolAssert(10 / 0 == 0)
java.lang.ArithmeticException: / by zero
at .<init>(<console>:8)
21. def byNameAssert(predicate: => Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
scala> byNameAssert(5 > 3)
scala> byNameAssert(10 / 0 == 0)
By-name parameters
are evaluated on demand
22. Re-occurring patterns
• transform every element of a list
• verify property of all elements of a list
• extract elements satisfying some criterion
• combining elements of a list using some
operator
Higher-order functions
• direct, reusable definitions of such patterns
23. Higher-order functions
• reducing code duplication, simplifying client code
Currying
• partial function applications
Writing new control structures
• growing the language
By-name parameters
• lazy evaluation
25. Lecture 4
type
Bool x, y;
x && y
x || y
!x
operations
data type: a collection of data values and a set
of predefined operations on those values
26. Lecture 4
type checking: the activity of ensuring that
the operands of an operation are of compatible
types.
A compatible type is one that either is legal
for the operator or is allowed under language
rules to be implicitly converted (coerced) to
a legal type
type error: the application of an operator
to an operand of an inappropriate type.
Type Compatible = Type Equal
limits code reuse
27. Monomorphic Functions
def map(xs: IntList, f: Int => Int): IntList = xs match {
case Nil() => Nil()
case Cons(y, ys) => Cons(f(y), map(ys, f))
}
def inc(xs: IntList) = map(xs, ((x:Int) => x + 1))
def square(xs: IntList) = map(xs, ((x:Int) => x * x))
Repeat pattern for each type
28. In computer science, polymorphism is a programming
language feature that allows values of different data types
to be handled using a uniform interface.
The concept of parametric polymorphism applies to both
data types and functions.
A function that can evaluate to or be applied to values of
different types is known as a polymorphic function.
A data type that can appear to be of a generalized type
(e.g., a list with elements of arbitrary type) is designated
polymorphic data type like the generalized type from which
such specializations are made.
31. In object-oriented programming, subtype
polymorphism or inclusion
polymorphism is a concept in type theory
wherein a name may denote instances of many
different classes as long as they are related by
some common super class. Inclusion
polymorphism is generally supported through
subtyping, i.e., objects of different types are
entirely substitutable for objects of another type
(their base type(s)) and thus can be handled via
a common interface.
http://en.wikipedia.org/wiki/Polymorphism_(computer_science)
32. Type T is a set of values with some operations.
A subtype of T is a subset of the values of
type T, and therefore may be used in a context
where a value of type T is expected.
C: char,int subtype-of long
float subtype-of double
33. If S subtype-of T, then
Every value of S is also a value of T
A value in S can be used where a value of
type T is expected
Operations of T can be applied to values in S
(S inherits the operations)
34. Liskov Substitution Principle
Substitutability is a principle in object-oriented
programming. It states that, in a computer program, if
S is a subtype of T, then objects of type T may be
replaced with objects of type S (i.e., objects of type S
may be substituted for objects of type T) without
altering any of the desirable properties of that
program (correctness, task performed, etc.).
http://en.wikipedia.org/wiki/Liskov_substitution_principle
35. Context:
- e has type T1
- var x: T2 = e
- def f(x: T2) and f(e)
Is T1 compatible with T2?
T1 is a subtype of T2: safe,
by substitutability
T1 is not a subtype of T2:
requires run-time type check
36. TypePoint = Point(Double x Double)
+ Circle(Doube x Double x Double)
+ Rectangle(Double x Double x Double x Double)
class Point {
protected double x, y;
public void draw() { /* ... */ }
}
Subtype and Inheritance in Java
class Circle extends Point {
private double r;
public void draw() { /* ... */ }
}
class Rectangle extends Point {
private double w, h;
public void draw() { /* ... */ }
}
Point p;
p = new Point(3.0, 4.0);
p = new Circle(3.0, 4.0, 5.0);
37. class BaseTest {
public static void main(String[] args) {
Base base1 = new Base(45);
class Base { Base base2 = new Child(567, 245);
Integer x; base1.print();
base2.print();
public Base(Integer v) { }
x = v; }
}
public void print() {
System.out.println("Base: " + x);
}
}
class Child extends Base {
Integer y;
public Child(Integer v1, Integer v2) {
super(v1);
y = v2;
}
public void print() {
System.out.println("Child: (" + x + "," + y + ")");
}
}
Dynamic Dispatch in Java
39. objects in JavaScript are dynamically
composed without class blueprints
dynamic structural subtyping
40. In computer programming with object-oriented
programming languages, duck typing is a style of
dynamic typing in which an object's methods and properties
determine the valid semantics, rather than its inheritance
from a particular class or implementation of a specific
interface.
When I see a bird that walks like a duck and swims like
a duck and quacks like a duck, I call that bird a duck.
James Whitcomb Riley
http://en.wikipedia.org/wiki/Duck_typing
41. var Duck = function(){
this.quack = function(){alert('Quaaaaaack!');};
this.feathers = function(){alert('The duck has white and gray feathers.');};
return this;
};
var Person = function(){
this.quack = function(){alert('The person imitates a duck.');};
this.feathers =
function(){alert('The person takes a feather from the ground and shows it.');};
this.name = function(){alert('John Smith');};
return this;
};
var in_the_forest = function(duck){
duck.quack();
duck.feathers();
};
var game = function(){
var donald = new Duck();
var john = new Person();
in_the_forest(donald);
in_the_forest(john); Duck typing in Java Script
};
game();
http://en.wikipedia.org/wiki/Duck_typing#In_JavaScript
44. Object.create()
var o1 = Object.create({x:1, y:2});
// o1 inherits properties x and y.
var o2 = Object.create(null);
// o2 inherits no props or methods.
var o3 = Object.create(Object.prototype);
// o3 is like {} or new Object().
45. var p = {x:1};
// Define a prototype object.
var o = Object.create(p);
// Create an object with that prototype.
p.isPrototypeOf(o)
// => true: o inherits from p
Object.prototype.isPrototypeOf(p)
// => true: p inherits from Object.prototype
The prototype Attribute
46. Inheritance
var o = {}
// o inherits object methods from Object.prototype
o.x = 1;
// and has an own property x.
var p = Object.create(o);
// p inherits properties from o and Object.prototype
p.y = 2;
// and has an own property y.
var q = Object.create(p);
// q inherits properties from p, o, and Object.prototype
q.z = 3;
// and has an own property z.
var s = q.toString();
// toString is inherited from Object.prototype
q.x + q.y
// => 3: x and y are inherited from o and p
47. var unitcircle = { r:1 };
// An object to inherit from
var c = Object.create(unitcircle);
// c inherits the property r
Inheritance
c.x = 1; c.y = 1;
// c defines two properties of its own
c.r = 2;
// c overrides its inherited property
unitcircle.r;
// => 1: the prototype object is not affected
48. function range(from, to) {
var r = Object.create(range.methods);
r.from = from;
r.to = to;
return r; Classes and Prototypes
}
range.methods = {
includes : function(x) {
return this.from <= x && x <= this.to;
},
foreach : function(f) {
for ( var x = Math.ceil(this.from); x <= this.to; x++) f(x);
},
toString : function() {
return "(" + this.from + "..." + this.to + ")";
}
};
var r = range(1, 3); // Create a range object
r.includes(2); // => true: 2 is in the range
r.foreach(console.log); // Prints 1 2 3
console.log(r); // Prints (1...3)
49. Classes and Constructors
function Range(from, to) {
this.from = from;
this.to = to;
}
Range.prototype = {
includes : function(x) {
return this.from <= x && x <= this.to;
},
foreach : function(f) {
for ( var x = Math.ceil(this.from); x <= this.to; x++) f(x);
},
toString : function() {
return "(" + this.from + "..." + this.to + ")";
}
};
var r = new Range(1, 3); // Create a range object
r.includes(2); // => true: 2 is in the range
r.foreach(console.log); // Prints 1 2 3
console.log(r); // Prints (1...3)
50. r instanceof Range
// returns true if r inherits
// from Range.prototype
Constructors and Class Identity
51. var F = function() {}; Constructor Property
// This is a function object.
var p = F.prototype;
// This is the prototype object associated with it.
var c = p.constructor;
// This is the function associated with the prototype.
c === F
// => true: F.prototype.constructor==F for any function
var o = new F();
// Create an object o of class F
o.constructor === F
// => true: the constructor property specifies the class
52. function Range(from, to) {
this.from = from; Extend Prototype
this.to = to;
}
Range.prototype.includes = function(x) {
return this.from <= x && x <= this.to;
};
Range.prototype.foreach = function(f) {
for ( var x = Math.ceil(this.from); x <= this.to; x++)
f(x);
};
Range.prototype.toString = function() {
return "(" + this.from + "..." + this.to + ")";
};
54. If all code is written without mention of any
specific type and thus can be used
transparently with any number of new types,
it is called parametric polymorphism.
http://en.wikipedia.org/wiki/Polymorphism_(computer_science)
55. A mono-morphic (single-shaped)
procedure can operate only on arguments of
a fixed type.
A poly-morphic (many-shaped) procedure
can operate uniformly on arguments of a
whole family of types.
Parametric polymorphism is a type
system in which we can write polymorphic
procedures.
56. Polymorphic function: parameterized with types
A => B : type of functions from type A to type B
def map[A,B](xs: List[A], f: A => B): List[B] = xs match {
case List() => List()
case y :: ys => f(y) :: map(ys, f)
}
val l = map(List(1, 2, 3), ((x: Int) => x + 1))
58. If the function denotes different and potentially
heterogeneous implementations depending on a limited
range of individually specified types and combination, it is
called ad-hoc polymorphism. Ad-hoc polymorphism
is supported in many languages using function and
method overloading.
http://en.wikipedia.org/wiki/Polymorphism_(computer_science)
59. Method Overloading
def *(that: Rational): Rational =
new Rational(numer * that.numer, denom * that.denom)
def *(i: Int): Rational =
new Rational(numer * i, denom)
scala> val c = new Rational(3,7)
c: Rational = 3/7
In a method call, the
scala> c * 2
compiler picks the version
res1: Rational = 6/7
of an overloaded method
that correctly matches the
types of the arguments.
61. Graded Assignment 1
Algebraic datatypes in C
Dynamic dispatch in C
Important dates
Deadline: April 2, 2013 23:59
Extension: April 5, 2013 23:59
Submitting after extension date is not possible
Maximum penalty for submitting after deadline: 6 points
Minimum grade needed: 4
Grade: 70% unit tests, 30% check lists
Grade for GAs: average of four assignments
62. Syntax and Semantics Quarter 3
Names, Bindings, and Scopes
Storage
Data Types Basics of
Functional Programming Scala
First-class Functions JavaScript
Polymorphism C
Type Parameterization
Parsing and Interpretation
Data Abstraction / Modular Programming
Functional Programming Redux
Concurrency
Concurrent Programming
Quarter 4 Domain-Specific Languages
63. Material for exam
Slides from lectures
Tutorial exercises
Sebesta: Chapters 1-3, 5-8
Programming in Scala: Chapters 1, 4-9, 15-16
K&R C: Chapters 1-6
JavaScript Good Parts: Chapters 1-4
64. Content of exam
20% multiple choice questions
about concepts
40% Scala programming
(functional programming)
20% C programming
(structures and pointers)
20% JavaScript programming
(objects and prototypes)