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

Example 7-2. A simple ternary statement
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!

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
public class Contact {

        public final Integer contact_id = 0;
        public final String firstName = "";
        public final String lastName = "";
        public final String email = "";
        public final 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;
        }

        public static 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
                                )
                        }
                )
        }

        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.

Example 7-4. Contact.scala file
object Contact {

        def setNameAndEmailForContactAndCustomer(
      customer_id : Integer,
            contact_id : Integer,
            name : String,
            email : String) : List[Customer] = {
    Customer.updateContactForCustomerContact(
        customer_id,
        contact_id,
        { contact =>
                new Contact(
          contact.contact_id,
          contact.firstName,
          name,
          email,
          contact.enabled
        )
      }
    )
  }
}

class Contact(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
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?

Example 7-6. Contract.scala file
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.

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

Example 7-9. Customer.scala file
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.

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

Example 7-11. A setter for the Foo field on the Bar class, which returns the object itself
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.

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 in Example 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset