Upcoming SlideShare
×

# Functional OO programming (as part of the the PTT lecture)

885 views

Published on

Published in: Technology
0 Likes
Statistics
Notes
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

• Be the first to like this

Views
Total views
885
On SlideShare
0
From Embeds
0
Number of Embeds
323
Actions
Shares
0
0
0
Likes
0
Embeds 0
No embeds

No notes for slide

### Functional OO programming (as part of the the PTT lecture)

1. 1. Functional OO Programming Prof. Dr. Ralf Lämmel Universität Koblenz-Landau Software Languages Team
2. 2. Let’s say we know OOP. What’s functional programming?
3. 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. 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. 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. 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. 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. 8. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable) ﬁlter 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. 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 ﬁrst 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. 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. 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. 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.
13. 13. What’s functional OO programming (in Java 7)?
14. 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 ﬁlter in Java 7 (or C#)? Can we be lazy in Java 7 (or C#)? Can we do memoization in Java 7 (or C#)?
15. 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. 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. 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 ﬁelds of the hosting object. (By the language rules, the variables are all “ﬁnal”.)
18. 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. 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. 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. 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 deﬁnition of twice?
22. 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-ﬁnal variables in outer scope.
23. 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.
24. 24. Major use case for FOOP: Combinator libraries
25. 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-speciﬁc programming languages to be easily embedded into a general purpose language [...].“
26. 26. Traversal combinators http://101companies.org/wiki/ Contribution:javaSyb
27. 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:javaReﬂection How to be more typeful? What other traversals are conceivable?
28. 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:javaReﬂection How to be more typeful? What other traversals are conceivable?
29. 29. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable) The paradigm of using traversal combinators Identify problem-speciﬁc ingredients: Type-speciﬁc actions or functions. Represent these ingredients as closures. Extend those ingredients into generic ones. Apply a traversal scheme. Such schemes are closure combinators. Speciﬁcally,w e useSYB style.
30. 30. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable) Problem-speciﬁc 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. 31. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable) Problem-speciﬁc 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. 32. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable) Combine type-speciﬁc 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-speciﬁc case
33. 33. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable) Combine type-speciﬁc 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. 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. 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. 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. 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 reﬂection Apply the action to the last
38. 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. 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. 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
41. 41. Parser combinators http://101companies.org/wiki/ Contribution:javaParseLib
42. 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. 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. 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. 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 ﬁnally failing. (Backtracking needed.)
46. 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. 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. 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. 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. 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. 51. (C) 2010-2013 Prof. Dr. Ralf Lämmel, Universität Koblenz-Landau (where applicable) Nonterminals as static ﬁelds public static final Acceptor STRING = WS(sequence( CHAR('"'), star(sequence(not(CHAR('"')),any)), CHAR('"'))); STRING : '"' (~'"')* '"';
52. 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 ﬁelds”?
53. 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. 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, ...