When we think of a statement, we think of something like Integer x = 1;
or val x = 1
where we are setting a variable. Technically, that line evaluates to nothing, but what if we had already defined a variable, and we were setting it later—something like x = 1
? Some people already know that in C and Java, this statement actually returns 1
, as shown in Example 7-1.
public
class
Test
{
public
static
void
main
(
String
[]
args
)
{
Integer
x
=
0
;
System
.
out
.
println
(
"X is "
+
(
x
=
1
).
toString
());
}
}
Statements in functional programming introduce the idea that every line of code should have a return value. Imperative languages such as Java incorporate the concept of the ternary operator. This gives you an if/else
structure that evaluates to a value. Example 7-2 shows a simple usage of the ternary operator.
public
class
Test
{
public
static
void
main
(
String
[]
args
)
{
Integer
x
=
1
;
System
.
out
.
println
(
"X is: "
+
((
x
>
0
)
?
"positive"
:
"negative"
));
}
}
But if we’re able to make more use of statements, we can actually reduce the number of variables we have. If we reduce the number of variables that we have, we reduce the ability to mutate them and thus increase our ability to perform concurrent processes and become more functional!
Your boss is very happy with what you’ve been doing over at XXY. He’s actually impressed with functional programming and wants you to convert from a partially functional language to a fully functional one. This shouldn’t be difficult, because we’ve already become quite functional over the last few chapters.
We’re going to pick a language that still runs on the Java Virtual Machine (JVM) so that we’re not introducing a new technology such as the LISP runtime or the Erlang runtime. We could also pick a language such as Clojure or Erjang, but for the purpose of this book we’re using Scala, which is similar to the Java syntax and should not require a huge learning curve.
We’ll be rewriting each of our classes, so let’s begin with the easiest of the files: the Contact
class. You’ll remember the existing file, shown in Example 7-3.
public
class
Contact
{
public
final
Integer
contact_id
=
0
;
public
final
String
firstName
=
""
;
public
final
String
lastName
=
""
;
public
final
String
=
""
;
public
final
Boolean
enabled
=
true
;
public
Contact
(
Integer
contact_id
,
String
firstName
,
String
lastName
,
String
,
Boolean
enabled
)
{
this
.
contact_id
=
contact_id
;
this
.
firstName
=
firstName
;
this
.
lastName
=
lastName
;
this
.
=
;
this
.
enabled
=
enabled
;
}
public
static
List
<
Customer
>
setNameAndEmailForContactAndCustomer
(
Integer
customer_id
,
Integer
contact_id
,
String
name
,
String
)
{
Customer
.
updateContactForCustomerContact
(
customer_id
,
contact_id
,
{
contact
->
new
Contact
(
contact
.
contact_id
,
contact
.
firstName
,
name
,
,
contact
.
enabled
)
}
)
}
public
void
sendEmail
()
{
println
(
"Sending Email"
)
}
}
We’ll refactor this into its Scala equivalent, as shown in Example 7-4. In the Scala example, notice that we define our instance variables in a set of parentheses next to the class name. We also have an object and a class; static methods and members exist inside the object rather than the class definition. Types are also defined after the variable rather than before it.
object
Contact
{
def
setNameAndEmailForContactAndCustomer
(
customer_id
:
Integer
,
contact_id
:
Integer
,
name
:
String
,
:
String
)
:
List
[
Customer
]
=
{
Customer
.
updateContactForCustomerContact
(
customer_id
,
contact_id
,
{
contact
=>
new
Contact
(
contact
.
contact_id
,
contact
.
firstName
,
name
,
,
contact
.
enabled
)
}
)
}
}
class
Contact
(
val
contact_id
:
Integer
,
val
firstName
:
String
,
val
lastName
:
String
,
val
:
String
,
val
enabled
:
Boolean
)
{
def
sendEmail
()
=
{
println
(
"Sending Email"
)
}
}
Although there are lots of lines added for readability in this book, including empty lines and method definitions split onto multiple lines, the number of lines goes from 19 to 9. This results from how we define members in Java and how we set them via the constructor.
The next class we’re going to tackle is the Contract
class. This is a little more difficult because we were using a Java Calendar
object, which is not a very functional construct. Let’s take a look at the original file in Example 7-5.
import
java.util.List
;
import
java.util.Calendar
;
public
class
Contract
{
public
final
Calendar
begin_date
;
public
final
Calendar
end_date
;
public
final
Boolean
enabled
=
true
;
public
Contract
(
Calendar
begin_date
,
Calendar
end_date
,
Boolean
enabled
)
{
this
.
begin_date
=
begin_date
;
this
.
end_date
=
end_date
;
this
.
enabled
=
enabled
;
}
public
Contract
(
Calendar
begin_date
,
Boolean
enabled
)
{
this
.
begin_date
=
begin_date
;
this
.
end_date
=
this
.
begin_date
.
getInstance
();
this
.
end_date
.
setTimeInMillis
(
this
.
begin_date
.
getTimeInMillis
());
this
.
end_date
.
add
(
Calendar
.
YEAR
,
2
);
this
.
enabled
=
enabled
;
}
public
static
List
<
Customer
>
setContractForCustomerList
(
List
<
Integer
>
ids
,
Boolean
status
)
{
Customer
.
updateContractForCustomerList
(
ids
)
{
contract
->
new
Contract
(
contract
.
begin_date
,
contract
.
end_date
,
status
)
}
}
}
We’ll go ahead and convert the class over, as shown in Example 7-6. Let’s first look at the List[Integer]
, which is how Scala denotes generic typing. We’ll also see a very interesting syntax with def this(begin_date : Calendar, enabled : Boolean)
, which is how we define an alternate constructor. We can also see a line that just has a c
; this is actually valid because the line is treated as a statement. This line is then treated as the return value of that block of code.
What is most interesting about this syntax is the call to this
, in which we pass what appears to be a function where the end_date
variable should be passed. Why is the compiler not complaining that a Calendar
instance is expected, not a method that returns a Calendar
instance?
import
java.util.Calendar
object
Contract
{
def
setContractForCustomerList
(
ids
:
List
[
Integer
],
status
:
Boolean
)
:
List
[
Customer
]
=
{
Customer
.
updateContractForCustomerList
(
ids
,
{
contract
=>
new
Contract
(
contract
.
begin_date
,
contract
.
end_date
,
status
)
})
}
}
class
Contract
(
val
begin_date
:
Calendar
,
val
end_date
:
Calendar
,
val
enabled
:
Boolean
)
{
def
this
(
begin_date
:
Calendar
,
enabled
:
Boolean
)
=
this
(
begin_date
,
{
val
c
=
Calendar
.
getInstance
()
c
.
setTimeInMillis
(
begin_date
.
getTimeInMillis
)
c
.
add
(
Calendar
.
YEAR
,
2
)
c
},
enabled
)
}
The compiler infers that you are not passing a method, but instead wanting to evaluate the brackets {. . .}
. So when the alternate constructor is called, we will call into the actual constructor, and the brackets {. . .}
will be evaluated to come up with the end_date
Calendar
object. Alternate constructors are much like how Java allows you to overload constructors to take different arguments.
The code block shown in Example 7-7 is very simple; it creates a Calendar
object, setting the time in milliseconds based on our begin_date
object (reminiscent of a closure). It then adds two years to the time in order to create a time that is two years from the beginning of the contract. Finally, it returns our newly created c
object containing two years from begin_date
.
This statement makes it possible for us to step outside the normal functional paradigm, in which every line should be a statement that can then be directed into another function or used directly. You can think of this as a compound statement: you have multiple statements that must be evaluated in order to come up with an overall statement that is actually used.
{
val
c
=
Calendar
.
getInstance
()
c
.
setTimeInMillis
(
begin_date
.
getTimeInMillis
)
c
.
add
(
Calendar
.
YEAR
,
2
)
c
}
The interesting thing about this block of code is that it shows that, quite literally, everything is a statement. Think about this: the last line, c
, is a statement because it returns the variable c
. And the entire code block is a statement itself; when evaluated, it executes the lines of code in sequence and returns the new c
value we defined.
Finally, we’re going to convert our Customer
class, which shouldn’t be too hard; let’s look at the original Java file shown in Example 7-8.
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Calendar
;
public
class
Customer
{
static
public
List
<
Customer
>
allCustomers
=
new
ArrayList
<
Customer
>();
public
final
Integer
id
=
0
;
public
final
String
name
=
""
;
public
final
String
state
=
""
;
public
final
String
domain
=
""
;
public
final
Boolean
enabled
=
true
;
public
final
Contract
contract
=
null
;
public
final
List
<
Contact
>
contacts
=
new
ArrayList
<
Contact
>();
@Lazy
public
List
<
Contact
>
enabledContacts
=
contacts
.
findAll
{
contact
->
contact
.
enabled
}
public
Customer
(
Integer
id
,
String
name
,
String
state
,
String
domain
,
Boolean
enabled
,
Contract
contract
,
List
<
Contact
>
contacts
)
{
this
.
id
=
id
;
this
.
name
=
name
;
this
.
state
=
state
;
this
.
domain
=
domain
;
this
.
enabled
=
enabled
;
this
.
contract
=
contract
;
this
.
contacts
=
contacts
;
}
static
def
EnabledCustomer
=
{
customer
->
customer
.
enabled
==
true
}
static
def
DisabledCustomer
=
{
customer
->
customer
.
enabled
==
false
}
public
static
List
<
String
>
getDisabledCustomerNames
()
{
Customer
.
allCustomers
.
findAll
(
DisabledCustomer
).
collect
({
cutomer
->
cutomer
.
name
})
}
public
static
List
<
String
>
getEnabledCustomerStates
()
{
Customer
.
allCustomers
.
findAll
(
EnabledCustomer
).
collect
({
cutomer
->
cutomer
.
state
})
}
public
static
List
<
String
>
getEnabledCustomerDomains
()
{
Customer
.
allCustomers
.
findAll
(
EnabledCustomer
).
collect
({
cutomer
->
cutomer
.
domain
})
}
public
static
List
<
String
>
getEnabledCustomerSomeoneEmail
(
String
someone
)
{
Customer
.
allCustomers
.
findAll
(
EnabledCustomer
).
collect
({
cutomer
->
someone
+
"@"
+
cutomer
.
domain
})
}
public
static
ArrayList
<
Customer
>
getCustomerById
(
ArrayList
<
Customer
>
inList
,
final
Integer
id
)
{
inList
.
findAll
({
customer
->
customer
.
id
==
id
})
}
public
static
void
eachEnabledContact
(
Closure
cls
)
{
Customer
.
allCustomers
.
findAll
{
customer
->
customer
.
enabled
&&
customer
.
contract
.
enabled
}.
each
{
customer
->
customer
.
contacts
.
each
(
cls
)
}
}
public
static
List
<
Customer
>
updateCustomerByIdList
(
List
<
Customer
>
initialIds
,
List
<
Integer
>
ids
,
Closure
cls
)
{
if
(
ids
.
size
()
<=
0
)
{
initialIds
}
else
if
(
initialIds
.
size
()
<=
0
)
{
[]
}
else
{
def
idx
=
ids
.
indexOf
(
initialIds
[
0
].
id
)
def
cust
=
idx
>=
0
?
cls
(
initialIds
[
0
])
:
initialIds
[
0
]
[
cust
]
+
updateCustomerByIdList
(
initialIds
.
drop
(
1
),
idx
>=
0
?
ids
.
minus
(
initialIds
[
0
].
id
)
:
ids
,
cls
)
}
}
public
static
List
<
Customer
>
updateContactForCustomerContact
(
Integer
id
,
Integer
contact_id
,
Closure
cls
)
{
updateCustomerByIdList
(
Customer
.
allCustomers
,
[
id
],
{
customer
->
new
Customer
(
customer
.
id
,
customer
.
name
,
customer
.
state
,
customer
.
domain
,
customer
.
enabled
,
customer
.
contract
,
customer
.
contacts
.
collect
{
contact
->
if
(
contact
.
contact_id
==
contact_id
)
{
cls
(
contact
)
}
else
{
contact
}
}
)
})
}
public
static
List
<
Customer
>
updateContractForCustomerList
(
List
<
Integer
>
ids
,
Closure
cls
)
{
updateCustomerByIdList
(
Customer
.
allCustomers
,
ids
,
{
customer
->
new
Customer
(
customer
.
id
,
customer
.
name
,
customer
.
state
,
customer
.
domain
,
customer
.
enabled
,
cls
(
customer
.
contract
),
customer
.
contacts
)
})
}
public
static
def
countEnabledCustomersWithNoEnabledContacts
=
{
List
<
Customer
>
customers
,
Integer
sum
->
if
(
customers
.
isEmpty
())
{
return
sum
}
else
{
int
addition
=
(
customers
.
head
().
enabled
&&
(
customers
.
head
().
contacts
.
find
({
contact
->
contact
.
enabled
})
==
null
))
?
1
:
0
return
countEnabledCustomersWithNoEnabledContacts
.
trampoline
(
customers
.
tail
(),
addition
+
sum
)
}
}.
trampoline
()
}
When we convert the class and object over to Scala (see Example 7-9), there is one thing that doesn’t work: there is no ternary operator! Remember (conditional) ? true : false
? Well, as you can see in the Scala file, we actually replaced it with a true if
statement.
Scala does not include the concept of ternary because everything already is a statement. This means that our if
statement will evaluate to something. We can actually write if(conditional) { true } else { false }
and the if
statement will evaluate to either true
or false
.
object
Customer
{
val
allCustomers
=
List
[
Customer
]()
def
EnabledCustomer
(
customer
:
Customer
)
:
Boolean
=
customer
.
enabled
==
true
def
DisabledCustomer
(
customer
:
Customer
)
:
Boolean
=
customer
.
enabled
==
false
def
getDisabledCustomerNames
()
:
List
[
String
]
=
{
Customer
.
allCustomers
.
filter
(
DisabledCustomer
).
map
({
customer
=>
customer
.
name
})
}
def
getEnabledCustomerStates
()
:
List
[
String
]
=
{
Customer
.
allCustomers
.
filter
(
EnabledCustomer
).
map
({
customer
=>
customer
.
state
})
}
def
getEnabledCustomerDomains
()
:
List
[
String
]
=
{
Customer
.
allCustomers
.
filter
(
EnabledCustomer
).
map
({
customer
=>
customer
.
domain
})
}
def
getEnabledCustomerSomeoneEmail
(
someone
:
String
)
:
List
[
String
]
=
{
Customer
.
allCustomers
.
filter
(
EnabledCustomer
).
map
({
customer
=>
someone
+
"@"
+
customer
.
domain
})
}
def
getCustomerById
(
inList
:
List
[
Customer
],
customer_id
:
Integer
)
:
List
[
Customer
]
=
{
inList
.
filter
(
customer
=>
customer
.
customer_id
==
customer_id
)
}
def
eachEnabledContact
(
cls
:
Contact
=>
Unit
)
{
Customer
.
allCustomers
.
filter
({
customer
=>
customer
.
enabled
&&
customer
.
contract
.
enabled
}).
foreach
({
customer
=>
customer
.
contacts
.
foreach
(
cls
)
})
}
def
updateCustomerByIdList
(
initialIds
:
List
[
Customer
],
ids
:
List
[
Integer
],
cls
:
Customer
=>
Customer
)
:
List
[
Customer
]
=
{
if
(
ids
.
size
<=
0
)
{
initialIds
}
else
if
(
initialIds
.
size
<=
0
)
{
List
()
}
else
{
val
precust
=
initialIds
.
find
(
cust
=>
cust
.
customer_id
==
ids
(
0
))
val
cust
=
if
(
precust
.
isEmpty
)
{
List
()
}
else
{
List
(
cls
(
precust
.
get
))
}
cust
:::
updateCustomerByIdList
(
initialIds
.
filter
(
cust
=>
cust
.
customer_id
==
ids
(
0
)),
ids
.
drop
(
1
),
cls
)
}
}
def
updateContactForCustomerContact
(
customer_id
:
Integer
,
contact_id
:
Integer
,
cls
:
Contact
=>
Contact
)
:
List
[
Customer
]
=
{
updateCustomerByIdList
(
Customer
.
allCustomers
,
List
(
customer_id
),
{
customer
=>
new
Customer
(
customer
.
customer_id
,
customer
.
name
,
customer
.
state
,
customer
.
domain
,
customer
.
enabled
,
customer
.
contract
,
customer
.
contacts
.
map
{
contact
=>
if
(
contact
.
contact_id
==
contact_id
)
{
cls
(
contact
)
}
else
{
contact
}
}
)
})
}
def
updateContractForCustomerList
(
ids
:
List
[
Integer
],
cls
:
Contract
=>
Contract
)
:
List
[
Customer
]
=
{
updateCustomerByIdList
(
Customer
.
allCustomers
,
ids
,
{
customer
=>
new
Customer
(
customer
.
customer_id
,
customer
.
name
,
customer
.
state
,
customer
.
domain
,
customer
.
enabled
,
cls
(
customer
.
contract
),
customer
.
contacts
)
})
}
def
countEnabledCustomersWithNoEnabledContacts
(
customers
:
List
[
Customer
],
sum
:
Integer
)
:
Integer
=
{
if
(
customers
.
isEmpty
)
{
sum
}
else
{
val
addition
=
if
(
customers
.
head
.
enabled
&&
customers
.
head
.
contacts
.
exists
({
contact
=>
contact
.
enabled
}))
{
1
}
else
{
0
}
countEnabledCustomersWithNoEnabledContacts
(
customers
.
tail
,
addition
+
sum
)
}
}
}
class
Customer
(
val
customer_id
:
Integer
,
val
name
:
String
,
val
state
:
String
,
val
domain
:
String
,
val
enabled
:
Boolean
,
val
contract
:
Contract
,
val
contacts
:
List
[
Contact
])
{
}
Let’s look further into the code in Example 7-10, which shows how we can set a variable based on an if
statement.
val
addition
=
if
(
customers
.
head
.
enabled
&&
customers
.
head
.
contacts
.
exists
({
contact
=>
contact
.
enabled
}))
{
1
}
else
{
0
}
As we can see, addition
will actually get 1
or 0
depending on the if
evaluation. So, why is this much more interesting than a ternary? Because the if
functions like a normal if
statement, which means you can add any amount of code inside the true
or false
sections of the if
statement. The ternary operator really only allows very simple expressions, such as a value or a basic method call.
But what do I really mean by “everything is a statement”? Well, I actually mean that everything should evaluate to something. But what exactly does that mean? Many of us know the normal bean methodology in Java—that is, having a member variable with getters and setters. Obviously, the getter will return some value, but what about the setter? Check out Example 7-11.
public
class
Bar
{
public
Bar
setFoo
(
Foo
foo
)
{
this
.
foo
=
foo
;
return
this
;
}
public
Foo
getFoo
()
{
return
this
.
foo
;
}
}
This makes it possible for us to chain the function calls together and set a bunch of members in one line, as shown in Example 7-12. But why would we want to do this? Simply put, by doing this, we can redefine the setter methods and create immutable variables. Why? Because inside our setter, we can create a new instance of Bar
with the new value and return that! This means that implementing immutable variables becomes simpler.
return
bar
.
setFoo
(
newFoo
).
setBaz
(
newBaz
).
setQux
(
newQux
);
What about things like for
loops—are those statements as well? As a matter of fact, yes, they are, but not in the way you might imagine. for
loops generally come in two forms: one is just a normal loop, whereas the other is called a comprehension. The first type of loop is shown in Example 7-13.
When we run this code, we actually end up printing 0 to 9 on the screen. More important, the variable x
is actually being set to something; in this case, it’s being set to Unit
.
That might sound strange, but in Scala, Unit
is effectively a void
type (meaning it has no actual type). This means that our for
loop actually evaluated to nothing. So what are comprehensions? Let’s look at a for
comprehension in Example 7-14.
Now, we have an x
that is a list from 0 to 18 by twos. The comprehension allows us to generate a new list of something, or sometimes iterate over another list. Let’s look at Example 7-15, in which we’re actually iterating over another list.
val
x
=
for
(
i
<-
List
(
1
,
2
,
3
,
4
))
yield
{
i
*
2
}
So, what is the difference between this and using a map
function on the list? Check out Example 7-16. This functionality is identical to the for
comprehension in Example 7-15.
This begs the question, when do you use a map
function versus a for
comprehension? Generally, a map
is good if you already have a list and need to perform an operation over it. for
comprehensions are good if you are building a list or if need to do an operation n number of times.
We’ve taken the time to migrate from Java to Scala, marking our transition into a functional language that we’ll be able to use in the upcoming chapters. Statements allowed us to reduce some of our code base and, in some instances, are necessary for us to still use some of our Java “bean” paradigms. We’ve seen in examples such as the Calendar
object that when we need to use setters, we can create a block statement to set up our Calendar
.
Statements also show us that every method should have some form of return value, even setters. And if we have setters that are statements, we can more easily implement immutable variables. Statements also make our code concise by forcing us to think about why we are writing a specific line of code and what that line of code is supposed to represent when evaluated. By doing this, we can better understand why a line of code acts the way that it does.