2017年11月1日 星期三

[ FP with Java ] Ch2 - Using functions in Java : Part1

Preface
This chapter covers
* Understanding functions in the real world
* Representing functions in Java
* Using lambdas
* Working with higher-order functions
* Using curried functions
* Programming with functional interfaces

To understand how functional programming works, we could use functional components provided by some functional library, or even the few that have been made available in the Java 8 library. But instead, we’ll look at how you can construct things rather than how to use these provided components. Once you’ve mastered the concepts, it will be up to you to choose between your own functions and the standard Java 8 ones, or to rely on one of the existing external libraries. In this chapter you’ll create a Function very similar to the Java 8 Function. It will be a bit simplified in how it handles type parameters (avoiding wildcardsin order to make the code easier to read, but it will have some powerful capacities that are absent from the Java 8 version. Apart from those differences, they’ll be interchangeable.

You might have trouble understanding some parts of the code presented in this chapter. That’s to be expected, because it’s very difficult to introduce functions without using other functional constructs such as ListOption, and others. Be patient. All the unexplained components will be discussed in the following chapters. I’ll now explain in greater detail what a function is, both in the real world and in a programming language. Functions aren’t only a mathematical or programming entity. Functions are part of everyday life. We’re constantly modeling the world in which we live, and this is true not only for programming. We construct representations of the world around us, and these representations are often based on objects that mutate their state as time changes. Seeing things this way is human nature. Going from state A to state B takes time, and it has a cost in terms of time, effort, or money.

Consider addition as an example. Most of us see it as a computation that takes time (and sometimes intellectual effort!). It has a starting statea transition (the computation), and a resulting state (the result of the addition). To add 345, 765, and 34,524, we certainly need to perform a computation. Some of us can do it in little time, and others will take longer. Some might never succeed, or will get an erroneous result. Some will make the computation in their head; others will need to write it down on paper. All will probably mutate some state to achieve this, whether it’s a sheet of paper or some part of their brain. But to add 2 and 3, we don’t need all this. Most of us have memorized the answer and can give the result immediately, without doing any computation.

This example shows that computation isn’t the essential element here. It’s just a means to calculate the result of a function. But this result existed before we made the computation. We just generally don’t know what this result is beforehand. Functional programming is just programming using functions. To be able to do this, we first need to know what a function is, both in the real world and in our programming language of choice.

What is a function?
A function is generally known as a mathematical object, although the concept is also ubiquitous in everyday life. Unfortunately, in everyday life, we often confuse functions and effects. And what is even more unfortunate is that we also make this mistake when working with many programming languages.

Functions in the real world
In the real world, a function is primarily a mathematic conceptIt’s a relation between a source set, called the function domain, to a target set, called the function codomain. The domain and the codomain need not be distinct. A function can have the same set of integer numbers for its domain and its codomain, for example.

What makes a relation between two sets a function
To be a function, a relation must fulfill one condition: all elements of the domain must have one and only one corresponding element in the codomain, as shown in figure 2.1.

Figure 2.1. All elements of a function’s domain must have one and only one corresponding element in the codomain.

This has some interesting implications:
* There cannot exist elements in the domain with no corresponding value in the codomain.
* There cannot exist two elements in the codomain corresponding to the same element of the domain.
* There may be elements in the codomain with no corresponding element in the source set.
* There may be elements in the codomain with more than one corresponding element in the source set.
* The set of elements of the codomain that have a corresponding element in the domain is called the image of the function.

You can, for example, define the function
where x is a positive integer. This function represents the relationship between each positive integer and its successor. You can give any name to this function. In particular, you can give it a name that will help you remember what it is, such as
  1. successor(x) = x + 1  
This may seem like a good idea, but you shouldn’t blindly trust a function name. You could alternatively have defined the function as follows:
  1. predecessor(x) = x + 1  
No error occurs here, because no mandatory relationship exists between a function name and the definition of the function. But, obviously, it would be a bad idea to use such a name.
Note that we’re talking about what a function is (its definition) and not what it does. A function does nothing. The successor function doesn’t add 1 to its argument. You can add 1 to an integer to calculate its successor, but you aren’t a function. The function
doesn’t add 1 to x. It is only equivalent to x + 1, which simply means that each time you encounter the expression successor(x), you can replace it with (x + 1). Note the parentheses that are used to isolate the expression. They aren’t needed when the expression is used alone, but they might be necessary on some occasions.

Inverse functions
A function may or may not have an inverse function. If f(x) is a function from A to B (A being the domain and B the codomain), the inverse function is noted as f-1(x) and has B as its domain and A as its codomain. If you represent the type of the function as A –> B, the inverse function (if it exists) has the type B –> A.
The inverse of a function is a function if it fulfills the same requirement as any function: having one and only one target value for each source value. As a result, the inverse of successor(x), a relation that you’ll call predecessor(x) (although you could just as well call it xyz), isn’t a function in N (the set of positive integers including 0) because 0 has no predecessor in N. Conversely, if successor(x) is considered with the set of signed integers (positive and negative, noted as Z), the inverse of successor is a function. Some other simple functions have no inverse. For example, the function
  1. f(x) = (2 * x)  
has no inverse if defined from N to N. It has an inverse if you define it as a function from N to the set of even integers.


Partial functions
A relation that isn’t defined for all elements of the domain but that fulfills the rest of the requirement (no element of the domain can have a relationship with more than one element of the codomain) is often called a partial function. The relation predecessor(x) is a partial function on N (the set of positive integers plus 0), but it’s a total function on N*, which is the set of positive integers without 0, and its codomain is N.

Partial functions are important in programming because many bugs are the result of using a partial function as if it were a total one. For example, the relation f(x) = 1/x is a partial function from N to Q (the rational numbers) because it isn’t defined for 0. It’s a total function from N* to Q, but it’s also a total function from N to (Q plus error). By adding an element to the codomain (the error condition), you can transform the partial function into a total one. But to do this, the function needs a way to return an error. Can you see an analogy with computer programs? You’ll see that turning partial functions into total ones is an important part of functional programming.

Function composition
Functions are building blocks that can be composed to build other functions. The composition of functions and g is noted as f ˚ g, which reads as f round g. If f(x) = x + 2 and g(x) = x * 2, then
  1. f ˚ g (x) = f(g(x)) = f(x * 2) = (x * 2) + 2  
Note that the two notations f ˚ g (x) and f(g(x)) are equivalent. But writing a composition as f(g(x)) implies using x as a placeholder for the argument. Using the f ˚ g notation, you can express a function composition without using this placeholder.

If you apply this function to 5, you’ll get the following:
  1. f ˚ g (5) = f(g(5)) = f(5 * 2) = 10 + 2 = 12  
It’s interesting to note that f ˚ g is generally different from g ˚ f, although they may sometimes be equivalent. For example:
  1. g ˚ f (5) = g(f(5)) = g(5 + 2) = 7 * 2 = 14  
Note that the functions are applied in the inverse of the writing order. If you write f ˚ g, you first apply g, and then f. Standard Java 8 functions define the compose() method and the andThen() method to represent both cases (which, by the way, isn’t necessary because f.andThen(g) is the same as g.compose(f), or g ˚ f).

Functions of several arguments
So far, we’ve talked only about functions of one argument. What about functions of several arguments? Simply said, there’s no such thing as a function of several arguments. Remember the definition? A function is a relation between a source set and a target set. It isn’t a relation between two or more source sets and a target set. A function can’t have several arguments. But the product of two sets is itself a set, so a function from such a product of sets into a set may appear to be a function of several arguments. Let’s consider the following function:
  1. f(x, y) = x + y  
This may be a relation between N x N and N, in which case, it’s a function. But it has only one argument, which is an element of N x N. N x N is the set of all possible pairs of integers. An element of this set is a pair of integers, and a pair is a special case of the more general tuple concept used to represent combinations of several elements. A pair is a tuple of two elements.
Tuples are noted between parentheses, so (3, 5) is a tuple and an element of N x N. The function f can be applied to this tuple:
  1. f((35)) = 3 + 5 = 8  
In such a case, you may, by convention, simplify writing by removing one set of parentheses:
  1. f(35) = 3 + 5 = 8  
Nevertheless, it’s still a function of one tuple, and not a function of two arguments.

Function currying
Functions of tuples can be thought of differently. The function f(3, 5) might be considered as a function from N to a set of functions of N. So the previous example could be rewritten as
where
In such a case, you can write
which means that the result of applying the function f to the argument x is a new function g. Applying this g function to y gives the following:
When applying g, x is no longer a variable. It doesn’t depend on the argument or on anything else. It’s a constant. If you apply this to (3, 5), you get the following:
  1. f(3)(5) = g(5) = 3 + 5 = 8  
The only new thing here is that the codomain of f is a set of functions instead of a set of numbers. The result of applying f to an integer is a function. The result of applying this function to an integer is an integer. f(x)(y) is the curried form of the function f(x, y). Applying this transformation to a function of a tuple (which you can call a function of several arguments if you prefer) is called currying, after the mathematician Haskell Curry (although he wasn’t the inventor of this transformation).

Partially applied functions
The curried form of the addition function may not seem natural, and you might wonder if it corresponds to something in the real world. After all, with the curried version, you’re considering both arguments separately. One of the arguments is considered first, and applying the function to it gives you a new function. Is this new function useful by itself, or is it simply a step in the global calculation?

In the case of an addition, it doesn’t seem useful. And by the way, you could start with either of the two arguments and it would make no difference. The intermediate function would be different, but not the end result. Now consider a new function of a pair of values:
  1. f(rate, price) = price / 100 * (100 + rate)  
That function seems to be equivalent to this:
  1. g(price, rate) = price / 100 * (100 + rate)  
Let’s now consider the curried versions of these two functions:
  1. f(rate)(price)  
  2. g(price)(rate)  
You know that f and g are functions. But what are f(rate) and g(price)? Yes, for sure, they’re the results of applying f to rate and g to price. But what are the types of these results? f(rate) is a function of a price to a price. If rate = 9, this function applies a tax of 9% to a price, giving a new price. You could call the resulting function apply9-percentTax(price), and it would probably be a useful tool because the tax rate doesn’t change often.

On the other hand, g(price) is a function of a rate to a price. If the price is $100, it gives a new function applying a price of $100 to a variable tax. What could you call this function? If you can’t think of a meaningful name, that usually means that it’s useless, though this depends on the problem you have to solve. Functions like f(rate) and g(price) are sometimes called partially applied functions, in reference to the forms f(rate, price) and g(price, rate). Partially applying functions can have huge consequences regarding argument evaluation. We’ll come back to this subject in a later section.

If you have trouble understanding the concept of currying, imagine you’re traveling in a foreign country, using a handheld calculator (or your smartphone) to convert from one currency to another. Would you prefer having to type the conversion rate each time you want to compute a price, or would you rather put the conversion rate in memory? Which solution would be less error prone?

Functions have no effects
Remember that pure functions only return a value and do nothing else. They don’t mutate any element of the outside world (with outside being relative to the function itself), they don’t mutate their arguments, and they don’t explode (or throw an exception, or anything else) if an error occurs. They can return an exception or anything else, such as an error message. But they must return it, not throw it, nor log it, nor print it.

Functions in Java
In chapter 1, you used what I called functions but were in fact methods. Methods are a way to represent (to a certain extentfunctions in traditional Java.

Functional methods
A method can be functional if it respects the requirements of a pure function:
* It must not mutate anything outside the function. No internal mutation may be visible from the outside.
* It must not mutate its argument.
* It must not throw errors or exceptions.
* It must always return a value.
* When called with the same argument, it must always return the same result.

Let’s look at an example.
- Listing 2.1. Functional methods
  1. public class FunctionalMethods {  
  2.   
  3.   public int percent1 = 5;  
  4.   private int percent2 = 9;  
  5.   public final int percent3 = 13;  
  6.   
  7.   public int add(int a, int b) {  
  8.     return a + b;  
  9.   }  
  10.   
  11. public setPercent2(int value) {  
  12.   percent2 = value;  
  13. }  
  14.   
  15.   public int mult(int a, Integer b) {  
  16.     a = 5;  
  17.     b = 2;  
  18.     return a * b;  
  19.   }  
  20.   
  21.   public int div(int a, int b) {  
  22.     return a / b;  
  23.   }  
  24.   
  25.   public int applyTax1(int a) {  
  26.     return a / 100 * (100 + percent1);  
  27.   }  
  28.   
  29.   public int applyTax2(int a) {  
  30.     return a / 100 * (100 + percent2);  
  31.   }  
  32.   
  33.   public int applyTax3(int a) {  
  34.     return a / 100 * (100 + percent3);  
  35.   }  
  36.   
  37.   public List append(int i, List list) {  
  38.     list.add(i);  
  39.     return list;  
  40.   }  
  41. }  
Can you say which of these methods represent pure functions? Think for a few minutes before reading the answer that follows. Think about all the conditions and all the processing done inside the methods. Remember that what counts is what’s visible from the outside. Don’t forget to consider exceptional conditions. Consider the first method:
  1. public int add(int a, int b) {  
  2.   return a + b;  
  3. }  
add is a function because it always returns a value that depends only on its arguments. It doesn’t mutate its arguments and doesn’t interact in any way with the outside world. This method may cause an error if the sum a + b overflows the maximum int value. But this won’t throw an exception. The result will be erroneous (a negative value), but this is another problem. The result must be the same each time the function is called with the same arguments. This doesn’t mean that the result must be exact!

Exactness
The term exact doesn’t mean anything by itself. It generally means that it fits what is expected, so to say whether the result of a function implementation is exact, you must know the intention of the implementer. Usually you’ll have nothing but the function name to determine the intention, which can be a source of misunderstanding. Consider the second method:
  1. public int mult(int a, Integer b) {  
  2.   a = 5;  
  3.   b = 2;  
  4.   return a * b;  
  5. }  
The mult method is a pure function for the same reason as add. This may surprise you, because it seems to be mutating its arguments. But arguments in Java methods are passed by value, which means that values reassigned to them aren’t visible from outside the method. This method will always return 10, which isn’t useful because it doesn’t depend on the arguments, but this doesn’t break the requirements. When the method is called several times with the same arguments, it will return the same value. By the way, this method is equivalent to a method with no argument. This is a special case of function: f(x) = 10. It’s a constant.

Now consider the div:
  1. public int div(int a, int b) {  
  2.   return a / b;  
  3. }  
The div method isn’t a pure function because it will throw an exception if the divisor is 0. To make it a function, you could test the second parameter and return a value if it’s null. It would have to be an int, so it would be difficult to find a meaningful value, but that’s another problem. Consider the fourth method:
  1. public int percent1 = 5;  
  2.   
  3. public int applyTax1(int a) {  
  4.   return a / 100 * (100 + percent1);  
  5. }  
The applyTax1 method seems not to be a pure function because its result depends on the value of percent1, which is public and can be modified between two function calls. As a consequence, two function calls with the same argument could return different values. percent1 may be considered an implicit parameter, but this parameter isn’t evaluated at the same time as the method argument. This isn’t a problem if you use the percent1 value only once inside the method, but if you read it twice, it could change between the two read operations. If you need to use the value twice, you must read it once and keep it in a local variable. This means the method applyTax1 is a pure function of the tuple (a, percent1), but it’s not a pure function of a. Compare that with the applyTax2 method:
  1. private int percent2 = 9;  
  2.   
  3. public int applyTax2(int a) {  
  4.   return a / 100 * (100 + percent2);  
  5. }  
The applyTax2 method is no different. You might see it as a function, because the percent2 property is private. But it’s mutable, and it’s mutated by the setPercent2 method. Because percent2 is accessed only once, applyTax2 can be considered a pure function of the tuple (a, percent2). But if considered as a function of a, it’s not a pure function. Now consider the sixth method:
  1. public final int percent3 = 13;  
  2.   
  3. public int applyTax3(int a) {  
  4.   return a / 100 * (100 + percent3);  
  5. }  
The method applyTax3 is somewhat special. Given the same argument, the method will always return the same value, because it depends only on its arguments and on the percent3 final property, which can’t be mutated. You might think that applyTax3 isn’t a pure function because the result doesn’t depend only on the method’s arguments (the result of a pure function must depend only on its arguments). But no contradiction exists here if you consider percent3 as a supplemental argument. In fact, the class itself may be considered a supplemental implicit argument, because all its properties are accessible from inside the method.

This is an important notion. All instance methods can be replaced with static methods by adding an argument of the type of the enclosing class. So the applyTax3 method may be rewritten as
  1. public static int applyTax3(FunctionalMethods x, int a) {  
  2.   return a / 100 * 100 + x.percent3;  
  3. }  
This method may be called from inside the class, passing a reference to this for the arguments, such as applyTax3(this, a). It can also be called from outside, because it’s public, provided a reference to a FunctionalMethods instance is available. Here, applyTax3 is a pure function of the tuple (this, a). And finally, our last method:
  1. public List append(int i, List list) {  
  2. list.add(i);  
  3. return list;  
  4. }  
The append method mutates its argument before returning it, and this mutation is visible from outside the method, so it isn’t a pure function.

Object notation vs. functional notation
You’ve seen that instance methods accessing class properties may be considered as having the enclosing class instance as an implicit parameter. Methods that don’t access the enclosing class instance may be safely made static. Methods accessing the enclosing instance may also be made static if their implicit parameter (the enclosing instance) is made explicit. Consider the Payment class from chapter 1:
  1. public class Payment {  
  2.   
  3.   public final CreditCard cc;  
  4.   public final int amount;  
  5.   
  6.   public Payment(CreditCard cc, int amount) {  
  7.     this.cc = cc;  
  8.     this.amount = amount;  
  9.   }  
  10.   
  11.   public Payment combine(Payment other) {  
  12.     if (cc.equals(other.cc)) {  
  13.       return new Payment(cc, amount + other.amount);  
  14.     } else {  
  15.       throw new IllegalStateException(  
  16.                           "Can't combine payments to different cards");  
  17.     }  
  18.   }  
  19. }  
The combine method accesses the enclosing class’s cc and amount fields. As a result, it can’t be made static. This method has the enclosing class as an implicit parameter. You could make this parameter explicit, which would allow you to make the method static:
  1. public class Payment {  
  2.   
  3.   public final CreditCard cc;  
  4.   public final int amount;  
  5.   
  6.   public Payment(CreditCard cc, int amount) {  
  7.     this.cc = cc;  
  8.     this.amount = amount;  
  9.   }  
  10.   
  11.   public static Payment combine(Payment payment1, Payment payment2) {  
  12.     if (payment1.cc.equals(payment2.cc)) {  
  13.       return new Payment(payment1.cc, payment1.amount + payment2.amount);  
  14.     } else {  
  15.       throw new IllegalStateException(  
  16.                                "Can't combine payments to different cards");  
  17.     }  
  18.   }  
  19. }  
A static method enables you to make sure no unwanted access exists to the enclosing scope. But it changes the way the method can be used. If used from inside the class, the static method can be called, passing it the this reference:
  1. Payment newPayment = combine(this, otherPayment);  
If the method is called from outside the class, you must use the class name:
  1. Payment newPayment = Payment.combine(payment1, payment2);  
This makes little difference, but it all changes when you need to compose method calls. If you need to combine several payments, an instance method written as follows:
  1. public Payment combine(Payment payment) {  
  2.     if (this.cc.equals(payment.cc)) {  
  3.       return new Payment(this.cc, this.amount + payment.amount);  
  4.     } else {  
  5.       throw new IllegalStateException(  
  6.                                "Can't combine payments to different cards");  
  7.     }  
  8.   }  
may be used with object notation:
  1. Payment newPayment = p0.combine(p1).combine(p2).combine(p3);  
That’s much easier to read than this:
  1. Payment newPayment = combine(combine(combine(p0, p1), p2), p3);  
Combining one more charge in the first case is also simpler.

Java functional interfaces and anonymous classes
Methods can be made functional, but they’re missing something that keeps them from being able to represent functions in functional programming: they can’t be manipulated besides being applied to arguments. You can’t pass a method as an argument to another method. The consequence is that you can’t compose methods without applying them. You can compose method applications, but not the methods themselves. A Java method belongs to the class where it’s defined, and it stays there.

You can compose methods by calling them from other methods, but this must be done while writing the program. If you want different compositions depending on particular conditions, you have to lay out these compositions at writing time. You can’t write a program in such a way that the program itself will change during execution. Or can you?

Yes, you can! Sometimes you register handlers at runtime to handle specific cases. You can add handlers to handler collections, or remove them, or change the order in which they’ll be used. How can you do this? By using classes containing the methods you want to manipulate. In a GUI, you often use listeners to handle specific events such as moving the mouse, resizing a window, or typing text. These listeners are generally created as anonymous classes implementing a specific interface. You can use the same principle to create functions. Let’s say you want to create a method to triple an integer value. First, you have to define an interface with a single method:
  1. public interface Function {  
  2.   int apply(int arg);  
  3. }  
You then implement this method to create your function:
  1. Function triple = new Function() {  
  2.   
  3.     @Override  
  4.     public int apply(int arg) {  
  5.         return arg * 3;  
  6.     }  
  7. };  
This function can then be applied to an argument:
  1. System.out.println(triple.apply(2));  
I must admit that this isn’t spectacular. A good old method would have been easier to use. If you want to create another function, you can process it exactly the same way:
  1. Function square = new Function() {  
  2.   
  3.     @Override  
  4.     public int apply(int arg) {  
  5.         return arg * arg;  
  6.     }  
  7. };  
So far, so good, but what’s the benefit of this?

Composing functions
If you think about functions as methods, composing them seems simple:
  1. System.out.println(square.apply(triple.apply(2)));  
But this isn’t function composition. In this example, you’re composing function applications. Function composition is a binary operation on functions, just as addition is a binary operation on numbers. So you can compose functions programmatically, using a method:
  1. Function compose(final Function f1, final Function f2) {  
  2.   return new Function() {  
  3.     @Override  
  4.     public int apply(int arg) {  
  5.       return f1.apply(f2.apply(arg));  
  6.     }  
  7.   };  
  8. }  
  9. System.out.println(compose(triple, square).apply(3));  
Now you can start seeing how powerful this concept is! But two big problems remain. The first is that our functions can only take integer (int) arguments and return integers. Let’s deal with this first.

Polymorphic functions
To make our function more reusable, you can change it into a polymorphic function by using parameterized types, which are implemented in Java using generics:
  1. public interface Function {  
  2.   U apply(T arg);  
  3. }  
Given this new interface, you can rewrite our functions as follows:
  1. Function triple = new Function() {  
  2.   @Override  
  3.   public Integer apply(Integer arg) {  
  4.     return arg * 3;  
  5.   }  
  6. };  
  7. Function square = new Function() {  
  8.   @Override  
  9.   public Integer apply(Integer arg) {  
  10.     return arg * arg;  
  11.   }  
  12. };  
As you see, we switched from int to Integer because int can’t be used as a type parameter in Java. Hopefully, auto-boxing and auto-unboxing will make the conversion transparent. A corresponding compose function:
  1. static Function compose(Function f1,  
  2.                                               Function f2) {  
  3.   return new Function() {  
  4.   
  5.     @Override  
  6.     public Integer apply(Integer arg) {  
  7.       return f1.apply(f2.apply(arg));  
  8.     }  
  9.   };  
  10. }  
Problem with function compositions
Function composition is a powerful concept, but when implemented in Java, it presents a big danger. Composing a couple of functions is harmless. But think about building a list of 10,000 functions and composing them into a single one. (This could be done through a fold, an operation you’ll learn about in chapter 3.) In imperative programming, each function is evaluated before the result is passed as the input of the next function. But in functional programming, composing functions means building the resulting function without evaluating anything. Composing functions is powerful because functions can be composed without being evaluated. But as a consequence, applying the composed function results in numerous embedded method calls that will eventually overflow the stack. This can be demonstrated with a simple example (using lambdas, which will be introduced in the next section):
  1. int fnum = 10_000; Function g = x -> x;  
  2. Function f = x -> x + 1;  
  3. for (int i = 0; i < fnum; i++) {  
  4.     g = Function.compose(f, g);   
  5. };  
  6.   
  7. System.out.println(g.apply(0));  
This program will overflow the stack when fnum is around 7,500. Hopefully you won’t usually compose several thousand functions, but you should be aware of this.

Simplifying the code by using lambdas
The second problem you have is that functions defined using anonymous classes are cumbersome to use in coding. If you’re using Java 5 to 7, you’re out of luck, because there’s no other way to go. Fortunately, Java 8 introduced lambdas. Lambdas don’t change the way the Function interface is defined, but they make implementing it much simpler:
  1. Function triple = x -> x * 3;  
  2. Function square = x -> x * x;  
Lambdas aren’t just a syntax simplification. Lambdas have some consequences in terms of code compilationOne of the main differences between lambdas and the traditional way of writing anonymous classes is that the types on the right side of the equals sign can be omitted. This is possible because Java 8 comes with new capabilities regarding type inference.

Prior to Java 7, type inference was possible only when chaining identifier dereferencing, such as this:
  1. System.out.println();  
Here, you don’t need to specify the type of out, and Java is able to find it. If you were to write this without chaining, you’d have to specify the type:
  1. PrintStream out = System.out;  
  2. out.println();  
Java 7 added a bit of type inference with the diamond syntax:
  1. List list = new ArrayList<>();  
Here, you don’t need to repeat the type parameter String for the ArrayList because Java is able to infer it by looking at the declaration. The same thing is possible with lambdas:
  1. Function triple = x -> x * 3;  
In this example, the type of x is inferred by Java. But this isn’t always possible. When Java complains that it isn’t able to infer the type, you have to write it explicitly. Then you must use parentheses:
  1. Function triple = (Integer x) -> x * 3;  
Specifying function types
Although Java 8 introduced lambdas to ease function implementation, it’s missing the same kind of tool to simplify writing function types. The type of a function from an Integer to an Integer is
and the function implementation is written like this:
  1. x -> expression  
It would be nice to be able to apply the same simplification to the type, which would allow you to write the whole thing as follows:
  1. Integer -> Integer square = x -> x * x;  
Unfortunately, this isn’t possible in Java 8, and it’s something you can’t add yourself. However, we can still rewrite out compose function with neat way:
  1. static Function Compose(Function f1, Function f2) {  
  2.     return arg -> f1.apply(f2.apply(arg));  
  3. }  

沒有留言:

張貼留言

[ Py DS ] Ch1 - IPython: Beyond Normal Python

Source From  Here   Keyboard Shortcuts in the IPython Shell   If you spend any amount of time on the computer, you’ve probably found a u...