Statements - Becoming Functional (2014)

Becoming Functional (2014)

Chapter 7. Statements

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.

Example 7-1. A simple assignment statement

publicclassTest {

publicstaticvoid 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.

Example 7-2. A simple ternary statement

publicclassTest {

publicstaticvoid 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!

Taking the Plunge

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.

Simple Statements

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.

Example 7-3. Contact.java file

publicclassContact {

publicfinal Integer contact_id = 0;

publicfinal String firstName = "";

publicfinal String lastName = "";

publicfinal String email = "";

publicfinal Boolean enabled = true;

public Contact(Integer contact_id,

String firstName,

String lastName,

String email,

Boolean enabled) {

this.contact_id = contact_id;

this.firstName = firstName;

this.lastName = lastName;

this.email = email;

this.enabled = enabled;

}

publicstatic List<Customer> setNameAndEmailForContactAndCustomer(

Integer customer_id,

Integer contact_id,

String name,

String email) {

Customer.updateContactForCustomerContact(

customer_id,

contact_id,

{ contact ->

new Contact(

contact.contact_id,

contact.firstName,

name,

email,

contact.enabled

)

}

)

}

publicvoid 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 classdefinition. Types are also defined after the variable rather than before it.

Example 7-4. Contact.scala file

objectContact {

def setNameAndEmailForContactAndCustomer(

customer_id :Integer,

contact_id :Integer,

name :String,

email :String) :List[Customer] = {

Customer.updateContactForCustomerContact(

customer_id,

contact_id,

{ contact =>

newContact(

contact.contact_id,

contact.firstName,

name,

email,

contact.enabled

)

}

)

}

}

classContact(val contact_id :Integer,

val firstName :String,

val lastName :String,

val email :String,

val enabled :Boolean) {

def sendEmail() = {

println("Sending Email")

}

}

NOTE

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.

Block Statements

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.

Example 7-5. Contract.java file

importjava.util.List;

importjava.util.Calendar;

publicclassContract {

publicfinal Calendar begin_date;

publicfinal Calendar end_date;

publicfinal 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;

}

publicstatic 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?

Example 7-6. Contract.scala file

importjava.util.Calendar

objectContract {

def setContractForCustomerList(ids :List[Integer],

status :Boolean) :List[Customer] = {

Customer.updateContractForCustomerList(ids, { contract =>

newContract(contract.begin_date, contract.end_date, status)

})

}

}

classContract(val begin_date :Calendar,

val end_date :Calendar,

val enabled :Boolean) {

defthis(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.

Example 7-7. Code block of end_date

{

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.

Everything Is a Statement

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.

Example 7-8. Customer.java file

importjava.util.ArrayList;

importjava.util.List;

importjava.util.Calendar;

publicclassCustomer {

staticpublic List<Customer> allCustomers = new ArrayList<Customer>();

publicfinal Integer id = 0;

publicfinal String name = "";

publicfinal String state = "";

publicfinal String domain = "";

publicfinal Boolean enabled = true;

publicfinal Contract contract = null;

publicfinal 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 }

publicstatic List<String> getDisabledCustomerNames() {

Customer.allCustomers.findAll(DisabledCustomer).collect({cutomer ->

cutomer.name

})

}

publicstatic List<String> getEnabledCustomerStates() {

Customer.allCustomers.findAll(EnabledCustomer).collect({cutomer ->

cutomer.state

})

}

publicstatic List<String> getEnabledCustomerDomains() {

Customer.allCustomers.findAll(EnabledCustomer).collect({cutomer ->

cutomer.domain

})

}

publicstatic List<String> getEnabledCustomerSomeoneEmail(String someone) {

Customer.allCustomers.findAll(EnabledCustomer).collect({cutomer ->

someone + "@" + cutomer.domain

})

}

publicstatic ArrayList<Customer> getCustomerById(

ArrayList<Customer> inList,

final Integer id) {

inList.findAll({customer -> customer.id == id })

}

publicstaticvoid eachEnabledContact(Closure cls) {

Customer.allCustomers.findAll { customer ->

customer.enabled && customer.contract.enabled

}.each { customer ->

customer.contacts.each(cls)

}

}

publicstatic List<Customer> updateCustomerByIdList(

List<Customer> initialIds,

List<Integer> ids,

Closure cls) {

if(ids.size() <= 0) {

initialIds

} elseif(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

)

}

}

publicstatic 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

}

}

)

})

}

publicstatic 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

)

})

}

publicstatic 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.

Example 7-9. Customer.scala file

objectCustomer {

val allCustomers =List[Customer]()

defEnabledCustomer(customer :Customer) :Boolean = customer.enabled == true

defDisabledCustomer(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

} elseif(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 =>

newCustomer(

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 =>

newCustomer(

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)

}

}

}

classCustomer(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.

Example 7-10. if statement result returned

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 ifstatement. 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.

Example 7-11. A setter for the Foo field on the Bar class, which returns the object itself

publicclassBar {

public Bar setFoo(Foo foo) { this.foo = foo; returnthis; }

public Foo getFoo() { returnthis.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.

Example 7-12. Method chaining on the Bar object

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 inExample 7-13.

Example 7-13. A basic for loop example in Scala

val x =for(i <- 0 until 10) {

println(i)

}

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.

Example 7-14. A basic for comprehension in Scala

val x =for(i <- 0 until 10) yield {

i*2

}

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.

Example 7-15. A for comprehension over another list in Scala

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.

Example 7-16. A map call on a list in Scala

val x =List(1,2,3,4).map({ i => i*2 })

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.

Conclusion

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.