2. Let’s say we know OOP.
What’s functional programming?
3. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Functional programming focuses on
functions as abstractions (as in math).
-- Two functions only requiring zero, succ, pred
add n m = if (n==0) then m else (add (n-1) m) + 1
mult n m = if (n==0) then 0 else add (mult (n-1) m) m
Functions are perfectly well-defined mathematical entities.
Making us think of the above functions as objects isn’t that helpful.
Haskell
4. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Encoding functions as instance methods
To be void or to return?
Why to tie a function to the class for the 1st argument?
public class “int” {
public int add(int m) {
return (this==0) ? m : (this-1).add(m)+1;
}
public int mult(int m) {
return (this==0) ? 0 : (this-1).mult(m). add(m);
}
}
5. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
public class TrivialExample {
public static int add(int n, int m) {
return (n==0) ? m : add(n-1,m)+1;
}
public static int mult(int n, int m) {
return (n==0) ? 0 : add(mult(n-1,m),m);
}
}
What’s the purpose of classes here?
There is some boilerplate code.
Encoding functions as static methods
6. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
evaluate :: Expr -> Int
evaluate (Lit i) = i
evaluate (Add l r) = evaluate l + evaluate r
Remember the expression problem!
Virtual functions scatter the code over classes.
Haskell
Functional programming supports
case discrimination.
7. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Instance-of-based pattern matching
public static int Evaluate(Expr e) {
if (e instanceof Lit) {
Lit l = (Lit)e;
return l.info;
}
if (e instanceof Add) {
Add a = (Add)e;
return Evaluate(a.left) + Evaluate(a.right);
}
throw new IllegalArgumentException();
}
Very little type checking!
Proper functional OO programming instances to the rescue.
8. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
filter is a higher-order function!
-- QuickSort in Haskell
quicksort [] = []
quicksort [e] = [e]
quicksort unsorted = quicksort lt ++ eq ++ quicksort gt
where
pivot = head unsorted
lt = filter (<pivot) unsorted
eq = filter (==pivot) unsorted
gt = filter (>pivot) unsorted
Haskell
Functional programming encourages
composition of functionality
via higher-order functions.
9. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
We iterate the increment function forever.
However, we only take the first 5 elements of the stream.
> take 5 (iterate (1+) 0)
[0,1,2,3,4]
Haskell
Functional programming supports
(in some instances) lazy evaluation.
10. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Hence:
• Optimizations can be more aggressive.
• Parallelization is feasible more often.
• Transactions in memory are more manageable.
• Proofs of program correctness rely on referential transparency.
Functional programming encourages
pure functions.
11. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
(define (memoize f)
(let ((table (make-table)))
(lambda (x)
(let ((previously-computed-result (lookup x table)))
(if (not (null? previously-computed-result))
previously-computed-result
(let ((result (f x)))
(insert! x result table)
result))))))
A memoization function as of Brian Goetz: Java theory and practice: The
closures debate, 2007, IBM developerWorks.
The combinator takes a function and returns a function.
It interleaves imperative actions with normal function application.
Scheme
Functional programming supports
composition of closures.
12. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
The notion of closure
Wikipedia as of 4 June 2013: In computer science, a closure
(also lexical closure or function closure) is a function or
reference to a function together with a referencing
environment—a table storing a reference to each of the non-
local variables (also called free variables) of that function. [...]
The concept of closures was developed in the 1960s and
was first fully implemented in 1975 [...] as a language feature
in the Scheme programming language to support lexically
scoped first-class functions.
14. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Functional OO Programming
in Java 7
Relevant Java concepts
Nested classes
Anonymous classes
Function objects (functors)
Riddles
Can we do filter in Java 7 (or C#)?
Can we be lazy in Java 7 (or C#)?
Can we do memoization in Java 7 (or C#)?
15. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Nested classes in Java
class OuterClass {
! ...
! class NestedClass { ... }
! ...
}
• Nested classes can be class members.
• Nested classes can be declared locally in a method.
• Non-static nested classes have access to outer scope.
• Why use nested classes?
– Group classes that are only used in one place.
– Nested scopes – simulate closures.
16. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Anonymous classes
cutButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.cutEmployeeClicked();
}
});
• These are nested classes w/o a name.
• They are derived from an interface or base class.
• Members are declared as part of construction.
• Free names occur inside the constructor.
A closure
17. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Closures in Java
In Java (7), we may equate the notion of an
anonymous class with (a restricted form of) the
general notion of closure. For such a closure to
be “nontrivial” (in the sense that the
anonymous class cannot be trivially eliminated),
the anonymous class should reference
“variables” outside the immediate scope of the
anonymous class. For instance, there could be
references to the arguments of the enclosing
method, or to temporary variables, or to fields
of the hosting object. (By the language rules, the
variables are all “final”.)
18. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Listeners in a GUI
DEMO
cutButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.cutEmployeeClicked();
}
});
101implementation:swing
19. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Function objects
Wikipedia as of 4 June 2013: “A function object [...]
is a computer programming construct allowing an
object to be invoked or called as if it were an
ordinary function, usually with the same syntax (a
function parameter that can also be a function).”
20. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Sample of a functor interface
/**
* Functions with 1 argument
*/
public interface UnaryFunction<X,Y> {
public Y apply(X x);
}
21. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Sample of a functor implementation
twice(
new UnaryFunction<Integer,Integer>(){
public Integer apply(Integer x) {
return ++x;
}
},
40
)
Exercise: suppose this expression is
supposed to compute 42 because twice
applies the argument twice. What’s the
definition of twice?
22. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Issues with Java <= 7 functors
Only function objects – no functions.
Verbose creation of closures.
No access to non-final variables in outer scope.
23. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Further reading
• John Hughes: Why Functional Programming Matters, 1989,
Computer Journal, available online.
• Alexandre Pereira Calsavara: Achieve better Java code with
inner and anonymous classes, 2003, TechRepublic, available
online.
• Abhijit Belapurkar: Functional programming in the Java
language, 2004, IBM developerWorks, available online.
25. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
The notion of
combinator library
Wikipedia as of 4 June 2013: “A combinator library is a
software library which implements combinators for a
functional programming language; "the key idea is this: a
combinator library offers functions (the combinators) that
combine functions together to make bigger functions". [...]
These kinds of libraries are particularly useful for
allowing domain-specific programming languages to be
easily embedded into a general purpose language [...].“
27. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Motivation of
traversal combinators
public static void cut(Company c) {
Walker w = new Walker(c);
for (Object o : w)
if (o instanceof Employee) {
Employee e = (Employee)o;
e.setSalary(e.getSalary() / 2);
}
}
http://101companies.org/wiki/Contribution:javaReflection
How to be more
typeful?
What other traversals
are conceivable?
28. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Motivation of
traversal combinators
public static Double total(Company c) {
Walker w = new Walker(c);
double result = 0;
for (Object o : w)
if (o instanceof Employee) {
Employee e = (Employee)o;
result += e.getSalary();
}
return result;
}
http://101companies.org/wiki/Contribution:javaReflection
How to be more
typeful?
What other traversals
are conceivable?
29. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
The paradigm of using
traversal combinators
Identify problem-specific ingredients:
Type-specific actions or functions.
Represent these ingredients as closures.
Extend those ingredients into generic ones.
Apply a traversal scheme.
Such schemes are closure combinators.
Specifically,w
e
useSYB
style.
30. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Problem-specific transformation
functionality for “Cut”
public static Action<Employee> updateSalary() {
return new Action<Employee>() {
public void apply(Employee x) {
x.setSalary(x.getSalary() / 2);
}
};
}
Update the salary when facing
an employee along traversal
http://101companies.org/wiki/Contribution:javaSyb
31. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Problem-specific query
functionality for “Total”
public static Function<Employee,Double> getSalary() {
return new Function<Employee, Double>() {
public Double apply(Employee x) {
return x.getSalary();
}
};
}
Extract the salary when facing an
employee along traversal
http://101companies.org/wiki/Contribution:javaSyb
32. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Combine type-specific transformation
with identity function
public static <X> Action<Object> orIdentity(final Action<X> a) {
return new Action<Object>() {
public void apply(Object x) {
try { a.apply((X)x); }
catch (ClassCastException _) { }
}
};
}
Rely on cast exception to
check for applicability of
type-specific case
33. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Combine type-specific query with
constant function
public static <X,Y> Function<Object,Y> orDefault(
final Function<X,Y> f,
final Y otherwise) {
return or(f, new Function<Object,Y>() {
public Y apply(Object x) { return otherwise; }
});
}
or uses cast in the same
way as orIdentity.
34. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Apply traversal scheme for
transformation
public static void cut(Company c) {
everywhere(orIdentity(updateSalary())).apply(c);
}
Try out every node
35. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Apply traversal scheme for query
public static Double total(Company c) {
return everything(
orDefault(getSalary(), 0.0),
add,
0.0).apply(c);
}
Try out
every node
Add intermediate
results
Start from
“0.0”
36. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
The traversal scheme
everywhere
public static Action<Object> everywhere(
final Action<Object> a) {
return new Action<Object>() {
public void apply(Object x) {
all(everywhere(a)).apply(x);
a.apply(x);
}
};
}
Compose an action which applies argument
action a to all the subobjects reachable from
the object passed to the composed action.
37. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
The traversal scheme
everywhere
public static Action<Object> everywhere(
final Action<Object> a) {
return new Action<Object>() {
public void apply(Object x) {
all(everywhere(a)).apply(x);
a.apply(x);
}
};
}
Recurse into all
immediate subobjects
using reflection
Apply the action
to the last
38. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
The traversal scheme
everything
public static <Y> Function<Object,Y> everything(
final Function<Object,Y> f,
final BinaryOperator<Y> op,
final Y initial) {
return new Function<Object,Y>() {
public Y apply(Object x) {
return op.apply(f.apply(x),
all( everything(f, op, initial),
op,
initial).apply(x));
}
};
}
Compose a function which applies argument
function f to all the subobjects reachable while
composing intermediate results with the binary
operator op while starting from initial.
39. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
The traversal scheme
everything
public static <Y> Function<Object,Y> everything(
final Function<Object,Y> f,
final BinaryOperator<Y> op,
final Y initial) {
return new Function<Object,Y>() {
public Y apply(Object x) {
return op.apply(f.apply(x),
all( everything(f, op, initial),
op,
initial).apply(x));
}
};
}
40. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Traversal of companies
DEMO
http://101companies.org/wiki/
Contribution:javaSyb
42. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
void department() {
match(DEPARTMENT);
match(STRING);
match(OPEN);
match(MANAGER);
employee();
while (test(EMPLOYEE)) {
match(EMPLOYEE);
employee();
}
while (test(DEPARTMENT))
department();
match(CLOSE);
}
department :
'department' STRING '{'
('manager' employee)
('employee' employee)*
dept*
'}';
Grammar
production
Corresponding
procedure
Motivation: how to avoid encoding
of recursive descent parsing?
43. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
The paradigm of using
parser combinators
Model functor classes for acceptance & parsing.
Model closure combinators for EBNF constructs.
Sequence, Choice, Star, Plus, ...
Model nonterminals as closure-returning methods.
Encode RHS of productions in method body.
44. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
The types of
acceptors and parsers
public abstract class Acceptor {
public abstract boolean accept(Input i);
}
public abstract class Parser<T> {
public abstract T parse(Input i);
}
Abstract
functor
classes
45. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
An acceptor combinator for “*”
public class Star extends Acceptor {
private Acceptor a;
public Star(Acceptor a) { this.a = a; }
public boolean accept(Input i) {
while (true) {
int mark = i.getPos();
if (!a.accept(i)) {
i.setPos(mark);
return true;
}
}
}
}
Accept input from i for as many times as possible.
Make sure not to consume input that was seen
when i was finally failing. (Backtracking needed.)
46. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
An acceptor combinator for “*”
public class Star extends Acceptor {
private Acceptor a;
public Star(Acceptor a) { this.a = a; }
public boolean accept(Input i) {
while (true) {
int mark = i.getPos();
if (!a.accept(i)) {
i.setPos(mark);
return true;
}
}
}
}
Concrete
functor
class
47. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
public static Acceptor star(final Acceptor a) {
return new Acceptor() {
public boolean accept(Input i) {
while (true) {
int mark = i.getPos();
if (!a.accept(i)) {
i.setPos(mark);
return true;
}
}
}
};
}
An alternative:
a static function
that returns a
closure
An acceptor combinator for “*”
48. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
public class Star<T> extends Parser<List<T>> {
private Parser<T> p;
public Star(Parser<T> p) { this.p = p; }
public List<T> parse(Input i) {
List<T> r = new LinkedList<T>();
while (true) {
int mark = i.getPos();
T t = p.parse(i);
if (t!=null)
r.add(t);
else {
i.setPos(mark);
return r;
}
}
}
}
An parser combinator for “*”
Given a parser p which returns a T, construct a
parser which returns a list of Ts.
49. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
public class Star<T> extends Parser<List<T>> {
private Parser<T> p;
public Star(Parser<T> p) { this.p = p; }
public List<T> parse(Input i) {
List<T> r = new LinkedList<T>();
while (true) {
int mark = i.getPos();
T t = p.parse(i);
if (t!=null)
r.add(t);
else {
i.setPos(mark);
return r;
}
}
}
}
Concrete
functor
class
An parser combinator for “*”
50. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
EBNF of 101companies
company :
'company' STRING '{' department* '}' EOF;
department :
'department' STRING '{'
('manager' employee)
('employee' employee)*
department*
'}';
employee :
STRING '{'
'address' STRING
'salary' FLOAT
'}';
STRING : '"' (~'"')* '"';
FLOAT : ('0'..'9')+ ('.' ('0'..'9')+)?;
Let’s parse
companies
again.
51. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Nonterminals as static fields
public static final Acceptor STRING =
WS(sequence(
CHAR('"'),
star(sequence(not(CHAR('"')),any)),
CHAR('"')));
STRING : '"' (~'"')* '"';
52. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Nonterminals as methods
public static Acceptor department() {
return new Acceptor() {
public boolean accept(Input i) {
return (
sequence(
SPECIAL("department"),
STRING,
SPECIAL("{"),
employee("manager"),
star(employee("employee")),
star(department()),
SPECIAL("}")
)).accept(i);
}
};
}
department :
'department' STRING '{'
('manager' employee)
('employee' employee)*
department*
'}';
What’s the limit of
“nonterminals as
static fields”?
53. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Parsing comapanies
DEMO
http://101companies.org/wiki/
Contribution:javaParseLib
54. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable)
Summary
Functional OOP really combines OOP and FP:
OOP: Encapsulation and generalization
FP: Function application, composition, combination
Major use case for FOOP: combinator libraries.
There are languages more suitable for functional OOP:
Java 8, F#, C#, Ruby, Python, ...