Functional Java 1
First Class Data We have learned how to manipulate data with programs We can pass data to methods via arguments We can return data from methods via return types We can encapsulate data into objects / classes The object can be accessed by its reference variable The object can be acted on by its methods Because we handle data so easily, data is a first class citizen in the Java world 2
Functions have been Second Class We know how to define a function, but we don t know (yet) how to manipulate a function Can we pass a function as an argument? Can we return a function as a parameter? How do we encapsulate and reference a function? Closest we come is polymorphism One function invocation invokes different methods 3
Examples of Manipulating Functions Starting a GUI, we needed to add a function to the event loop to start things off the createandshowgui method We created an object that was an instance of an anonymous inner class The anonymous inner class implemented the Runnable interface We passed that object to the invokelater method as data The invokelater eventually invoked objref.run() That invoked the run method in our anonymous inner class That invoked the createandshowgui method Whew 4
Examples of Manipulating Functions When we registered an actionlistener callback We changed our class to implement the ActionListener interface We added an ActionPerformed method to our class We then registered by invoking addactionlistener The parameter to addactionlistener was not the callback function, but an object (data) The convention is, when an action is performed, find the data registered to that action, and invoke the object.actionperformed method Whew 5
Data Orientation When all you have is a hammer, everything looks like a nail When all you have is data, everything looks like an int 6
Another example of function We made Account comparable by adding a compareto method Sort uses compareto to determine how two objects in the same class are related (<, =, >) What about a class that does not implement Comparable? What if we could write some code OUTSIDE the class to tell sort how two objects are related? 7
Functional Needs We need a function that compares two items, and returns an integer If functions were first class citizens, we could define such a function and send it as an argument Or we could come up with an object whose class has such a function 8
The Comparator Interface Used to compare two objects Like ArrayList, supports a generic type Comparator<T> Requires one method int compare(t obj1,t obj2) Reference to the comparator object passed as a parameter to sort When sort has objects A and B, it invokes compobj.compare(a,b) to figure out whether A<B, A==B, or A>B Confused? Let s do an example. 9
java.awt.geom.ellipse2d.double An ellipse has fields: x, y, width, and height (x, y) width height Slides 6 10
Let s sort arrays of ellipses by area Area = π width height 4 11
Make a class that supports comparator import java.awt.geom.ellipse2d; import java.util.comparator; public class MyEllipseComp implements Comparator<Ellipse2D.Double> { @Override public int compare(ellipse2d.double arg0, Ellipse2D.Double arg1) { int retval = 0; double area0 = Math.PI*arg0.width*arg0.height/4; double area1 = Math.PI*arg1.width*arg1.height/4; if(area0 < area1) retval = -1; else if(area0 > area1) retval = 1; return retval; 12
Simplify remove common factors import java.awt.geom.ellipse2d; import java.util.comparator; public class MyEllipseComp implements Comparator<Ellipse2D.Double> { @Override public int compare(ellipse2d.double arg0, Ellipse2D.Double arg1) { int retval=0; double area0 = arg0.width*arg0.height; double area1 = arg1.width*arg1.height; if(area0 < area1) retval = -1; else if(area0 > area1) retval = 1; return retval; 13
Make an array of ellipses (circles) Ellipse2D.Double[ ] circles = { new Ellipse2D.Double(4, 7, 12, 12), new Ellipse2D.Double(5, 11, 16, 16), new Ellipse2D.Double(2, 65, 10, 10), new Ellipse2D.Double(7, 12, 1, 1), new Ellipse2D.Double(12, 6, 15, 15), new Ellipse2D.Double(34, 2, 14, 14) ; 14
Sort the array with a comparator Arrays.sort(circles, new MyEllipseComp()); for(ellipse2d e : circles) { System.out.print(e.getWidth() + " "); System.out.println(); Prints: 1.0 10.0 12.0 14.0 15.0 16.0 15
Anonymous Inner Class Why do we need an entire class just for one method? We only need it once to do the sort. Since Java 1.1, we can code an Anonymous Inner Class Anonymous has no name. We are only going to use it once. Inner contained inside another class Use the interface name as the type of the class! 16
Example Anonymous Inner Class Comparator<Ellipse2D.Double> comp = new Comparator<Ellipse2D.Double>() { @Override public int compare(ellipse2d.double arg0, Ellipse2D.Double arg1) { int retval=0; double area0 = arg0.width*arg0.height; double area1 = arg1.width*arg1.height; if(area0 < area1) retval = -1; else if(area0 > area1) retval = 1; return retval; Arrays.sort(circles, comp); 17
Or, skip the reference variable Arrays.sort(circles, new Comparator<Ellipse2D.Double>() { @Override public int compare(ellipse2d.double arg0, Ellipse2D.Double arg1) { int retval=0; double area0 = arg0.width*arg0.height; double area1 = arg1.width*arg1.height; if(area0 < area1) retval = -1; else if(area0 > area1) retval = 1; return retval; ); 18
Functions as a First Class Citizen If there was just some way of packaging the compare function And then passing that function as an argument to sort Then we wouldn t need an object We wouldn t need a Comparitor interface We wouldn t need an anonymous inner class or an explicit class We wouldn t need to pass data to the sort method But how can we package a function? 19
Lambda Expressions Invented by Alonzo Church in the 1930 s Method to express an anonymous function Supported in Java 1.8 Simplest form: x -> x*x Parameter name comes first Then -> to indicate this is a lambda expression Then an expression to evaluate the result Can have multiple parameters: (x,y)->x*y Can have multiple statements in braces { with return 20
Lambda Expression Comparator Arrays.sort(circles, (arg0, arg1) -> { int retval=0; double area0 = arg0.width*arg0.height; double area1 = arg1.width*arg1.height; if(area0 < area1) retval = -1; else if(area0 > area1) retval = 1; return retval; ); 21
Simplifying further Why not use the same trick as we used comparing integers? (arg0, arg1) -> arg0.width*arg0.height - arg1.width*arg1.height Comparators need to return integers The comparator class offers helper functions e.g. comparingdouble Argument a function to extract a double number from object When sort has objects A and B, it will invoke extract function on both, to get double values a and b Then, comparingdouble will return int <0, 0, >0, depending on the sign of (a b) 22
Simplify with comparingdouble Anonymous inner Comparator class: private Comparator<Ellipse2D.Double> comp1 = Comparator.comparingDouble(e-> e.width*e.height); Arrays.sort(circles, comp1); In fact the Comparator object may as well be anonymous: Arrays.sort(circles, Comparator.comparingDouble(e -> e.width*e.height)); 23
History of Lambda Expressions 1956 Information Processing Langauge Allen Newell, Cliff Shaw, and Herbert Siman at RAND/Carnegie IT List processing (dynamic memory, types, recursion, multi-tasking) 1958 LISP John McCarthy at MIT (IBM summer) 2nd major language (after FORTRAN) Mixes data and functions 1970 SCHEME LISP dialect using lambdas Guy Steele & Gerald Sussman at MIT 24
Typical functional construct : map map is a function which takes two arguments A function A vector Map applies the function to each element of the vector and computes multiple results The return value of map is a vector of results map *2 [1, 2, 3, 4, 5] [2, 4, 6, 8, 10] 25
Lambda in Other Languages: Scheme Scheme (1975) is a Lisp (1958) dialect > (define (square x) (* x x )) > (map square '(1 2 3 4)) (list 1 4 9 16) Anonymous lambda expression version > (map (lambda (x) (* x x)) '(1 2 3 4)) (list 1 4 9 16) Implementation of Scheme @ http://download.racket-lang.org/ 26
Lambda in other languages: Haskell Haskell (1990) Most popular functional language Put this line in 'Test.hs : square x = x * x Load it with (:l is the load command, l is ell) : :l Test Run: map square [1, 2, 3, 4] [1, 4, 9, 16] or using a lambda expression: map (\x -> x* x) [1, 2, 3, 4] [1, 4, 9, 16] 27
Lambda in Other Languages: Python(3) >>> a= [1,2,3,4] >>> sq = lambda x: x*x >>> list(map(sq,a)) [1, 4, 9, 16] >>> list(map(lambda x:x*x, a)) [1, 4, 9, 16] >>> def squ (n):... return n*n... >>> list(map(squ,a)) [1, 4, 9, 16] 28
Functions as Arguments: C int square ( int x ) { return x * x; int* map ( int (*f)(int), int len, int array[ ]) { int i = 0; int* ret = (int*)malloc(len*sizeof(int)); for(i = 0; i < len; i++) { ret[i] = (*f)(array[i]); return ret; void main () { int arg[ ] = {1, 2, 3, 4; int* tmp = map(square, 4, arg); printf("[%d, %d, %d, %d]\n", tmp[0], tmp[1], tmp[2], tmp[3]); 29
Defining map as static method in Java import java.util.function.function; // Java 1.8 Interface: apply method public class Mapper { public static double[ ] map(double[ ] array, Function<Double, Double> fn) { double[ ] temp = null; if(array!= null) { temp = new double[array.length]; for(int i = 0; i < array.length; i++) { temp[i] = fn.apply(array[i]); return temp; 30
Defining Function to be Passed In an anonymous inner class: static Function<Double, Double> square = new Function<Double, Double>() { @Override public Double apply(double t) { return t*t; ; As a stand-alone method public static double sqr(double d) { return d*d; 31
Passing Functions in to Functions public static void main(string[ ] args){ double[] data = {1,2,3,4; double[] sq1 = Mapper.map(data, square); double[] sq2 = map(data, Mapper::sqr); // Note :: double[] sq3 = map(data, Math::sqrt); // sqrt in Java lib double[] sq4 = map(data, d -> d*d); // lambda 32
Defining map as a dynamic method import java.util.function.function; // Java 1.8 Interface: apply method public class MapDyn { public double[ ] map(double[ ] array, Function<Double, Double> fn) { double[ ] temp = null; if(array!= null) { temp = new double[array.length]; for(int i = 0; i < array.length; i++) { temp[i] = fn.apply(array[i]); return temp; this is available, but not used 33
Defining Function to be Passed (dyn) In an anonymous inner class: Function<Double, Double> square = this is available, but not used new Function<Double, Double>() { @Override public Double apply(double t) { return t*t; ; As a stand-alone method public static double sqr(double d) { return d*d; 34
Passing Functions (dynamic) public static void main(string[ ] args){ need a reference double[] data = {1,2,3,4; variable to find MapDyn test = new MapDyn(); the map method double[] sq1 = test.map(data, test.square); double[] sq2 = test.map(data, MapDyn::sqr); // Note :: double[] sq3 = test.map(data, Math::sqrt); double[] sq4 = test.map(data, d -> d*d); // lambda 35