Although most functional programming books present immutable variables first, we’re going to start with first-class functions. My hope is that as you read through this chapter, you will see ways in which you could start using some of these ideas at your job tomorrow.
First-class functions are functions treated as objects themselves, meaning we can pass a function as a parameter to another function, return a function from a function, or store a function in a variable. This is one of the most useful features in functional programming, and also one of the most difficult to learn to use effectively.
Welcome to your new company, XXY. You have been hired for your functional programming skills, which your boss would like to use in order to make the company’s code more “functional.” XXY currently uses Java but is interested in some newer languages such as Groovy or Scala. Although you have some ideas, you have been told that the company can’t afford to just “throw away all of its current code and start over.”
All right, it’s time to get down to business. You have been tasked with adding a new function to return a list of enabled customers’ addresses. Your boss tells you the code should be added to the Customer.java file, where XXY is already implementing the same type of functionality (see Example 2-1).
import
java.util.ArrayList
;
import
java.util.List
;
public
class
Customer
{
static
public
ArrayList
<
Customer
>
allCustomers
=
new
ArrayList
<
Customer
>();
public
Integer
id
=
0
;
public
String
name
=
""
;
public
String
address
=
""
;
public
String
state
=
""
;
public
String
primaryContact
=
""
;
public
String
domain
=
""
;
public
Boolean
enabled
=
true
;
public
Customer
()
{}
public
static
List
<
String
>
getEnabledCustomerNames
()
{
ArrayList
<
String
>
outList
=
new
ArrayList
<
String
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
outList
.
add
(
customer
.
name
);
}
}
return
outList
;
}
public
static
List
<
String
>
getEnabledCustomerStates
()
{
ArrayList
<
String
>
outList
=
new
ArrayList
<
String
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
outList
.
add
(
customer
.
state
);
}
}
return
outList
;
}
public
static
List
<
String
>
getEnabledCustomerPrimaryContacts
()
{
ArrayList
<
String
>
outList
=
new
ArrayList
<
String
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
outList
.
add
(
customer
.
primaryContact
);
}
}
return
outList
;
}
public
static
List
<
String
>
getEnabledCustomerDomains
()
{
ArrayList
<
String
>
outList
=
new
ArrayList
<
String
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
outList
.
add
(
customer
.
domain
);
}
}
return
outList
;
}
/* TODO: Add a main function */
}
There are four almost identical functions in the preceding example. Each function has:
for
loop
if
statement
return
statement
We have six lines of code duplicated per function. That’s 18 lines of duplicated code: one of those functions started this, which means we have 12 lines that have been copied and pasted.
The DRY (Don’t Repeat Yourself) principle has been around for many years; the concept is that we should not be duplicating lines of code. This makes the code harder to maintain. But why is that the case?
Think about what happens if you duplicate a function multiple times. Now imagine that you just found a bug in one of those functions; you’ll have to go through however many other functions to see if that same bug exists.
What would happen if you renamed enabled
, or if you decided to deprecate the enabled
field for something else? Now you have four functions that need to be rewritten. Could you imagine if you got a request for the alternate getDisabled*
functions? This explodes to eight copied-and-pasted functions.
You start getting a little light-headed thinking, “What have I gotten myself into?” You take a deep breath and realize that you can do this; you’re a functional programmer and eradicating copy and paste is what you do! Our first step is to begin thinking of functions as objects.
As we said before, first-class functions can be both passed and returned from another function. Let’s begin by thinking about what a function is. In its most general form, a function is merely a way to encapsulate a piece of work so that we can easily reference it again—that is, nothing more than a macro.
What are the components of a function? Functions are made up of a name that is used to identify the function, a parameter list containing objects to operate on, a body where we transform the parameters, and finally a return to specify the result.
Let’s break down the getEnabledCustomerNames
function from the Customer.java file (see Example 2-2). As we can see, the function name is getEnabledCustomerNames
, the parameter list is empty
, and the body contains code that iterates over the Customer.allCustomers
list, adding the customer.name
field to an output list only if the customer is enabled. Finally, our return is our output list, outList
.
public
static
List
<
String
>
getEnabledCustomerNames
()
{
ArrayList
<
String
>
outList
=
new
ArrayList
<
String
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
outList
.
add
(
customer
.
name
);
}
}
return
outList
;
}
Let’s write a new function that performs the same functionality from Example 2-2 (excluding the addition of the field to the outList
) and call it getEnabledCustomerField
. For the moment, we’ll just add a comment //Placeholder
where we were grabbing the customer.name
field and appending it to outList
.
The first thing to do is to create a new ArrayList
at the top of our function:
public
static
List
<
String
>
getEnabledCustomerField
()
{
ArrayList
<
String
>
outList
=
new
ArrayList
<
String
>();
We then create the for
loop and the if
statement, which checks for the customer being enabled:
for
(
Cutomer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
As I mentioned, we’re going to put in a placeholder where we were originally appending the field value to our list. We then close out the if
structure and for
loop, returning the new outList
:
//Placeholder
}
}
return
outList
;
}
Let’s put all of this together to create our new getEnabledCustomerField
method, as shown in Example 2-3.
public
static
List
<
String
>
getEnabledCustomerField
()
{
ArrayList
<
String
>
outList
=
new
ArrayList
<
String
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
//Placeholder
}
}
return
outList
;
}
Because we know all the possible fields that we’re looking for, let’s take a new parameter, the field name we’re looking for. We’ll then add an if
structure to append our list with the value of the field we are looking for, as shown in Example 2-4.
public
static
List
<
String
>
getEnabledCustomerField
(
String
field
)
{
ArrayList
<
String
>
outList
=
new
ArrayList
<
String
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
if
(
field
==
"name"
)
{
outList
.
add
(
customer
.
name
);
}
else
if
(
field
==
"state"
)
{
outList
.
add
(
customer
.
state
);
}
else
if
(
field
==
"primaryContact"
)
{
outList
.
add
(
customer
.
primaryContact
);
}
else
if
(
field
==
"domain"
)
{
outList
.
add
(
customer
.
domain
);
}
else
if
(
field
==
"address"
)
{
outList
.
add
(
customer
.
address
);
}
else
{
throw
new
IllegalArgumentException
(
"Unknown field"
);
}
}
}
return
outList
;
}
In Example 2-4, we’re throwing an IllegalArgumentException
to ensure type safety. This means that we are throwing an exception if the field is not one of our predefined fields.
Why is this such a bad idea? You are avoiding type safety since you are relying on string comparisons for field accessors. You are also now relying on someone spelling it correctly both in the if
structure as well as when he calls the method itself.
There are other ways to ensure type safety, such as using enumerations containing a list of valid values and matching the corresponding enumeration in our if/else
structure.
OK, we’ve just consolidated the looping functionality to exist in only one function. So what happens if we keep adding fields to extract? We’ll keep adding field checks to the if
/else
structure, which means we’ll eventually end up with an unmanageable if
structure. What if we could provide a simple function to extract the field we want from the object itself?
We’re going to be using Java interfaces to create an abstraction of a function that we could pass to another function. Many other languages, including the proposals in Java 8, offer functions as objects; as of this writing, however, Java 7 is the currently released and stable version. Thus, we are going to use interfaces to create functionality that we can pass to other functions.
You might be familiar with the Runnable
interface, with which you encapsulate some function that you want to execute on a thread. We have similar functionality here, except we need a function that can take an object (the object from which we want to extract a field) and return an object (the value of the field).
Let’s assume that we have a function, f
, that does some computation referencing the function a
and returns the value:
Now, let’s assume that we want to rewrite f
so that instead of calling a
we call b
. Well, to continue rewriting these functions as f
and f'
(and so on) would be duplication. Lambda calculus introduced the concept of passing a function to a function. So, instead of calling the function a
, what if we could pass it in? Let’s redefine f
:
Now, we can make a call into our function using either a
or b
fairly easily. Let’s see the call and substitute our values:
What should our function take as an argument? Let’s look where it will be called. It will be replacing the giant if
structure. The purpose of our function is to convert a Customer
record into a String
, which means our new function will take a Customer
object and return a String
. Let’s build our interface definition.
The first thing that we do is give our interface a name:
private
interface
ConversionFunction
{
Next we’ll define our method, which is the entry point into our function. As I said before, it will take a Customer
and return a String
:
public
String
call
(
Customer
customer
);
}
Example 2-5 shows the entirety of the ConversionFunction
definition.
private
interface
ConversionFunction
{
public
String
call
(
Customer
customer
);
}
We’ll want to make this interface public later, moving it into its own file and making it a little more generic. But for now, let’s focus on using this new ConversionFunction
interface by replacing our giant if
structure.
First, we replace the field parameter with a ConversionFunction
object. We can then replace the giant if
/else
structure with a call to func.call(customer)
. Remember, the call
method inside the ConversionFunction
interface will be performing the conversion for us. All we need to do is execute call
and add the result. Check out the code in Example 2-6.
public
static
List
<
String
>
getEnabledCustomerField
(
ConversionFunction
func
)
{
ArrayList
<
String
>
outList
=
new
ArrayList
<
String
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
outList
.
add
(
func
.
call
(
customer
));
}
}
return
outList
;
}
Now that we’re starting to think more functionally, let’s see what one of those ConversionFunctions
would look like. Just return the field that you want to extract. In the following example, the CustomerAddress
class allows us to take a Customer
and return the address
field:
static
private
class
CustomerAddress
implements
ConversionFunction
{
public
String
call
(
Customer
customer
)
{
return
customer
.
address
;
}
}
Let’s go ahead and make your boss happy now by implementing the getEnabledCustomerAddresses
function he wanted. We can create our getEnabledCustomerAddresses
function, which will call our new getEnabledCustomerField
method and pass it a conversion function. Now if the definition of enabled
ever changes, we only have to fix it in one place:
public
static
List
<
String
>
getEnabledCustomerAddresses
()
{
return
Customer
.
getEnabledCustomerField
(
new
CustomerAddress
());
}
Although we don’t need to do it yet, what would happen if we needed to get a list of all enabled customers? Well, our current interface really doesn’t help there, because our interface is strictly defined to take a Customer
object and return a String
. We should modify our interface to be more abstract by using generic typing. Let’s start by renaming our ConversionFunction
to Function1
, taking two type parameters (A1
and B
, which are the type of the parameter and the return, respectively). Our new function is shown in Example 2-7.
public
interface
Function1
<
A1
,
B
>
{
public
B
call
(
A1
in1
);
}
Why are we naming this interface Function1
? Well, we’re naming it Function
because it is going to be wrapping a function. The number 1 comes from the number of parameters that the function itself will take. Our generic typing seems a little odd because we have two parameters, but remember that the final parameter is the return type.
So, what if we needed a function that takes two arguments (shown in Example 2-8) or four arguments (shown in Example 2-9)?
public
interface
Function2
<
A1
,
A2
,
B
>
{
public
B
call
(
A1
,
in1
,
A2
in2
);
}
public
interface
Function4
<
A1
,
A2
,
A3
,
A4
,
B
>
{
public
B
call
(
A1
in1
,
A2
in2
,
A3
in3
,
A4
in4
);
}
Next, we’ll update the CustomerAddress
inheritance to be Function1<Customer,String>
.
static
private
class
CustomerAddress
implements
Function1
<
Customer
,
String
>
{
public
String
call
(
Customer
customer
)
{
return
customer
.
address
;
}
}
We then update getEnabledCustomerField
to take a Function1
. Our first parameter will always be a Customer
, but our second parameter will change, so we’ll leave that as B
. We then parameterize the getEnabledCustomerField
method to take a parameter B
and finally update our return type for getEnabledCustomerField
to return a List
of type B
(see Example 2-10).
public
static
<
B
>
List
<
B
>
getEnabledCustomerField
(
Function1
<
Customer
,
B
>
func
)
{
ArrayList
<
B
>
outList
=
new
ArrayList
<
B
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
outList
.
add
(
func
.
call
(
customer
));
}
}
return
outList
;
}
Now that you’ve done what your boss asked, it’s time to convert all the other getEnabledCustomer*
functions. We’ll just create a new class that implements our Function1
interface and then update the getEnabledCustomer*
method to call the Customer.getEnabledCustomerField()
method with a new instance of the appropriate class. Go ahead and refactor the rest of the file and then check out the code in Example 2-11 to see how it looks.
import
java.util.ArrayList
;
import
java.util.List
;
public
class
Customer
{
static
public
ArrayList
<
Customer
>
allCustomers
=
new
ArrayList
<
Customer
>();
public
Integer
id
=
0
;
public
String
name
=
""
;
public
String
address
=
""
;
public
String
state
=
""
;
public
String
primaryContact
=
""
;
public
String
domain
=
""
;
public
Boolean
enabled
=
true
;
public
Customer
()
{}
private
interface
Function1
<
A1
,
B
>
{
public
B
call
(
A1
in1
);
}
static
private
class
CustomerAddress
implements
Function1
<
Customer
,
String
>
{
public
String
call
(
Customer
customer
)
{
return
customer
.
address
;
}
}
static
private
class
CustomerName
implements
Function1
<
Customer
,
String
>
{
public
String
call
(
Customer
customer
)
{
return
customer
.
name
;
}
}
static
private
class
CustomerState
implements
Function1
<
Customer
,
String
>
{
public
String
call
(
Customer
customer
)
{
return
customer
.
state
;
}
}
static
private
class
CustomerPrimaryContact
implements
Function1
<
Customer
,
String
>
{
public
String
call
(
Customer
customer
)
{
return
customer
.
primaryContact
;
}
}
static
private
class
CustomerDomain
implements
Function1
<
Customer
,
String
>
{
public
String
call
(
Customer
customer
)
{
return
customer
.
domain
;
}
}
static
private
class
CustomerAsCustomer
implements
Function1
<
Customer
,
Customer
>
{
public
String
call
(
Customer
customer
)
{
return
customer
;
}
}
public
static
List
<
String
>
getEnabledCustomerAddresses
()
{
return
Customer
.
getEnabledCustomerField
(
new
CustomerAddress
());
}
public
static
List
<
String
>
getEnabledCustomerNames
()
{
return
Customer
.
getEnabledCustomerField
(
new
CustomerName
());
}
public
static
List
<
String
>
getEnabledCustomerStates
()
{
return
Customer
.
getEnabledCustomerField
(
new
CustomerState
());
}
public
static
List
<
String
>
getEnabledCustomerPrimaryContacts
()
{
return
Customer
.
getEnabledCustomerField
(
new
CustomerPrimaryContact
());
}
public
static
List
<
String
>
getEnabledCustomerDomains
()
{
return
Customer
.
getEnabledCustomerField
(
new
CustomerDomain
());
}
public
static
<
B
>
List
<
B
>
getEnabledCustomerField
(
Function1
<
Customer
,
B
>
func
)
{
ArrayList
<
B
>
outList
=
new
ArrayList
<
B
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
outList
.
add
(
func
.
call
(
customer
));
}
}
return
outList
;
}
}
Let’s answer our original question of “what would happen if we needed to get a list of all enabled customers?” We can create a new class that takes a customer and returns a customer, as shown in Example 2-12.
static
private
class
CustomerAsCustomer
implements
Function1
<
Customer
,
Customer
>
{
public
String
call
(
Customer
customer
)
{
return
customer
;
}
}
Now, we can call Customer.getEnabledCustomerField(new CustomerAsCustomer())
, which gives us a list of all our enabled cutomers. But what if we didn’t want to have to create all of these named classes? What if we didn’t actually need to define full classes? Well, that leads right into the next section on anonymous functions.
Anonymous functions are split into two types: lambda functions and closures. Closures are quite similar to lambdas with a very subtle difference, which we’ll discuss later. As you learned in the previous section, functions are made up of four parts: name, parameter list, body, and return. But what if we didn’t need the name of the function? This is the idea behind anonymous functions: being able to create functions that have a limited scope and need to exist only for a short time.
Let’s bring back our function from the beginning of this chapter:
Lambda calculus enables us to create a function without defining it formally. Our function f
is a formal definition, but what if we want to pass in a function as the parameter c
for something really simple, such as a square of the input? Let’s see that and substitute our variables:
The lambda expression is a function that takes an x
parameter and performs the x2
operation. So when we substitute, we can actually replace the entire c(x)
definition with the lambda function itself. Let’s simplify the function call a little bit, since we can now evaluate our lambda function:
Back at XXY, your boss is excited to see the company’s code becoming “functional.” However, he’s concerned with the number of extra classes being created and feels they are unnecessary. He’s asked you to clean up the code by reducing the number of inner classes. Luckily, we can do this by using lambda functions.
Lambda functions are unnamed functions that contain a parameter list, a body, and a return. In the following getEnabledCustomerAddresses
example, let’s try to use an anonymous function (in this case, an anonymous instance of Function1
) to get rid of the superfluous CustomerAddress
class:
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
customer
.
address
;
}
}
Let’s use this anonymous class by sending it to our getEnabledCustomerField
function (see Example 2-13). We can now remove the CustomerAddress
conversion class.
public
static
List
<
String
>
getEnabledCustomerAddresses
()
{
return
Customer
.
getEnabledCustomerField
(
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
customer
.
address
;
}
});
}
Go ahead and see if you can refactor the rest of the functions; when you’re done, check out Example 2-14 and see how they compare.
import
java.util.ArrayList
;
import
java.util.List
;
public
class
Customer
{
static
public
ArrayList
<
Customer
>
allCustomers
=
new
ArrayList
<
Customer
>();
public
Integer
id
=
0
;
public
String
name
=
""
;
public
String
address
=
""
;
public
String
state
=
""
;
public
String
primaryContact
=
""
;
public
String
domain
=
""
;
public
Boolean
enabled
=
true
;
public
Customer
()
{}
private
interface
Function1
<
A1
,
B
>
{
public
B
call
(
A1
in1
);
}
public
static
List
<
String
>
getEnabledCustomerAddresses
()
{
return
Customer
.
getEnabledCustomerField
(
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
customer
.
addresses
;
}
});
}
public
static
List
<
String
>
getEnabledCustomerNames
()
{
return
Customer
.
getEnabledCustomerField
(
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
customer
.
name
;
}
});
}
public
static
List
<
String
>
getEnabledCustomerStates
()
{
return
Customer
.
getEnabledCustomerField
(
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
customer
.
state
;
}
});
}
public
static
List
<
String
>
getEnabledCustomerPrimaryContacts
()
{
return
Customer
.
getEnabledCustomerField
(
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
customer
.
primaryContact
;
}
});
}
public
static
List
<
String
>
getEnabledCustomerDomains
()
{
return
Customer
.
getEnabledCustomerField
(
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
customer
.
domain
;
}
});
}
public
static
<
B
>
List
<
B
>
getEnabledCustomerField
(
Function1
<
Customer
,
B
>
func
)
{
ArrayList
<
B
>
outList
=
new
ArrayList
<
B
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
customer
.
enabled
)
{
outList
.
add
(
func
.
call
(
customer
));
}
}
return
outList
;
}
}
Your boss is excited by how great a job you’re doing, but he now needs a new piece of functionality. He needs to have his email prepended to the domain of each Customer
. As with most strange requests, you just kind of stare blankly at him for a moment and then agree to carry it out.
Your boss then shows you an example of a Customer
with the domain xxy.com, which is already defined in the Customer
object. “You should be able to just concatenate my email with the domain of each Customer
object and be done,” he says. “Something like [email protected].” You think for a few minutes and realize that it is a perfect time to use closures!
Closures are much like lambdas, except they reference variables outside the scope of the function. In the simplest explanation, the body references a variable that doesn’t exist in either the body or the parameter list.
Your boss’s request to prepend his email onto customer domains seems like a really simple function to write. We’re going to use our getEnabledCustomerField
, and in our anonymous function we’ll prepend “boss@” to the customer domains:
public
static
List
<
String
>
getEnabledCustomerBossesEmail
()
{
return
Customer
.
getEnabledCustomerField
(
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
"boss@"
+
customer
.
domain
;
}
});
}
But wait—what happens if the president of XXY comes to you and says, “I want my email prepended to the customer domains”? The first idea that comes to you is to copy and paste the function and update it with “president@”. This violates the DRY principle, however, so you should reconsider this approach. What if we were able to bring in a variable that was outside of our class definition? Well, this is a perfect use of a closure.
We know that we’re going to have a name of someone
passed into our function getEnabledCustomerSomeoneEmail
. This function should have a variable, someone
, passed to it. At this point, we can reference the someone
variable from inside our anonymous function and create the email address (see Example 2-15).
public
static
List
<
String
>
getEnabledCustomerSomeoneEmail
(
final
String
someone
)
{
return
Customer
.
getEnabledCustomerField
(
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
someone
+
"@"
+
customer
.
domain
;
}
});
}
Always remember to mark closed-over variables as final
. The Java compiler requires this; otherwise, it will throw a compile-time error of local variable someone is accessed from within inner class: needs to be declared final.
This is still a real closure, which we can tell from Example 2-16. Notice that we have our original Closure
variable coming in; we can see the variable printed out (providing us a variable reference) and the contents (which should be a blank string). Next, we set the internal string variable to bar
and then create/run our closure (it is a closure because the scope of the t
variable is “closed over” and brought into the scope of our runnable).
Upon execution, we print out "bar"
as is expected, but notice that the reference is the same! We then set the internal string to baz
and exit our closure. The next line in our function is to print out the internal string, which is now "baz"
and still has the same reference. Although a very simple example, this is a perfect illustration of how a closure truly works; we have an internal function that closes over a variable outside of its normal scope.
public
class
Closure
{
public
String
foo
=
""
;
public
static
Closure
process
(
final
Closure
t
)
{
System
.
out
.
println
(
t
.
toString
()
+
" = "
+
t
.
foo
);
t
.
foo
=
"bar"
;
new
Runnable
()
{
public
void
run
()
{
System
.
out
.
println
(
t
.
toString
()
+
" = "
+
t
.
foo
);
t
.
foo
=
"baz"
;
}
}.
run
();
System
.
out
.
println
(
t
.
toString
()
+
" = "
+
t
.
foo
);
return
t
;
}
public
static
void
main
(
String
[]
args
)
{
process
(
new
Closure
());
}
}
Using closures, you can build functions and pass them to other functions while referencing local variables. Think about our example of specifying any name to prepend to customer domains. If we were unable to close over the local variable of someone
, we would be forced to create new functions for every name we wanted to prepend. This means we would have quite a bit more code duplication.
The day you have been dreading has come: your boss has asked you to re-create the functions getEnabledCustomerAddresses
, getEnabledCustomerNames
, getEnabledCustomerStates
, getEnabledCustomerPrimaryContacts
, and getEnabledCustomerDomains
as getDisabled
style functions. The first way of doing this is to copy and paste the .getEnabledCustomerField
method and create a .getDisabledField
changing if (customer.enabled)
to if (!customer.enabled)
, as shown in Example 2-17.
public
static
<
B
>
List
<
B
>
getDisabledField
(
Function1
<
Customer
,
B
>
func
)
{
ArrayList
<
B
>
outList
=
new
ArrayList
<
B
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(!
customer
.
enabled
)
{
outList
.
add
(
func
.
call
(
customer
));
}
}
return
outList
;
}
It should be obvious that, again, we are violating the DRY principle. Let’s extract the test functionality in the if
statement so that we can pass it into the function. We will accept a function taking a Customer
and returning a Boolean
that will tell us whether it should be included. We then replace our if
with the evaluation of the test function call (see Example 2-18).
public
static
<
B
>
List
<
B
>
getField
(
Function1
<
Customer
,
Boolean
>
test
,
Function1
<
Customer
,
B
>
func
)
{
ArrayList
<
B
>
outList
=
new
ArrayList
<
B
>();
for
(
Customer
customer
:
Customer
.
allCustomers
)
{
if
(
test
.
call
(
customer
))
{
outList
.
add
(
func
.
call
(
customer
));
}
}
return
outList
;
}
Now, at first glance, we see that with this approach we’re going to be copying and pasting a ton of anonymous functions for each function. Instead, we’ll create two variables in which we will store the Enabled
and Disabled
Function1
implementations.
Inside of our Customer
class, we’ll create two function variables, EnabledCustomer
and DisabledCustomer
. This allows us to apply the DRY principle by not rewriting our Enabled
and Disabled
function implementations:
static
final
public
Function1
<
Customer
,
Boolean
>
EnabledCustomer
=
new
Function1
<
Customer
,
Boolean
>()
{
public
Boolean
call
(
Customer
customer
)
{
return
customer
.
enabled
==
true
;
}
};
static
final
public
Function1
<
Customer
,
Boolean
>
DisabledCustomer
=
new
Function1
<
Customer
,
Boolean
>()
{
public
Boolean
call
(
Customer
customer
)
{
return
customer
.
enabled
==
false
;
}
};
What does a call to this look like? Let’s look at the following getDisabledCustomerNames
function to see that we just pass the DisabledCustomers
object as the first parameter:
public
static
List
<
String
>
getDisabledCustomerNames
()
{
return
Customer
.
getField
(
Customer
.
DisabledCustomers
,
new
Function1
<
Customer
,
String
>()
{
public
String
call
(
Customer
customer
)
{
return
customer
.
name
;
}
}
);
}
As you can see, each of our getCustomer*
methods looks a little nasty. Normally in functional programming, we wouldn’t have all of these getCustomer*
methods; instead, we would call the Customer.getField
method where we needed the call. In the instance where we are making multiple calls over the code base—for example, if we called getDisabledCustomerNames
in a few different places—we would then create a method encapsulating that call (think DRY).
Let’s look at an example in Groovy to see how we could implement getDisabledCustomerNames
and getEnabledCustomerNames
in a more functional language. Example 2-19 shows these two pieces of functionality.
Notice that we are able to use the findAll
function, which allows you to filter a list based on another function, as we did with our for
with the inner if
structure inside getField
. We then use the collect
method to convert one object into another, as we did with our Function1
passed into the getField
.
// Get all enabled customer names
allCustomers
.
findAll
(
{
customer
->
customer
.
enabled
==
true
}
).
collect
(
{
customer
->
customer
.
name
}
)
// Get all disabled customer names
allCustomers
.
findAll
(
{
customer
->
customer
.
enabled
==
false
}
).
collect
(
{
customer
->
customer
.
name
}
)
There are a couple of things to note about our code in Example 2-19.
return
. Groovy uses the last statement in a function as the return of that function.
The big thing you’ll notice is that we’ve reiterated ourselves with the allCustomers
.findAll(. . .).collect(. . .)
call; and while we might say that this is duplicated code, it is a very minimal duplication in which we are actually being more expressive. If you remember back in getDisabledCustomerNames
, the amount of code required to get the names was much higher than in our Groovy code, and not as readable.
A function becomes “higher order” if it accepts or returns a function. Because functions are not objects in Java, it does not have the concept of a higher-order function. But, in our interface equivalency, we can see that the higher-order function is actually getField
from Example 2-18 because it accepts a “function.” In Example 2-19, we can see that the functions findAll
and collect
are both higher order because they themselves accept functions.
Why are higher-order functions so important? Think about the functions as objects: if we are able to pass functions as objects (without wrapping them in objects), we must have higher-order functions. Otherwise, what would utilize those functions as objects?
At this point, you should have an idea of how use first-class functions. In our examples, we made our new code functional and then went back and migrated our pre-existing code to the functional style. You should always remember that any time is a good time to make code more functional.
We took a little extra time to refactor our copy-and-paste code into a higher-order function that iterated over our list of customers. After that, we refactored the inner workings of our copy-and-paste code into simple, anonymous functions and even used a closure in case our boss ever wants to extend the functionality of the prepending email addresses.
The more we converted our copy-and-paste code using these functional concepts, the simpler our code became. It also became much easier for us to add new functionality because we no longer had to copy and paste things like our for
loops or other pieces that we extracted into our higher-order function getField
.
You don’t always need 10 functions to cover every possible use case in the future. Of course, if getEnabledCustomerNames
were to happen 5 or 10 times, it might make sense to create the function itself and make it a call to be done such that people aren’t duplicating that code.
Many of these abstractions, such as our Function1
, are already defined in libraries like Guava. For those of you who can’t switch to a language like Groovy, I would suggest looking into these types of libraries, which already have these abstractions available.
The next time you are reading a programming language book, be on the lookout for how you might be able to implement higher-order functions in that language. All languages can do some form of function passing, even C (using function pointers).