Monday, January 12, 2015

Java 8: A functional programming language (?)

I'm preparing to teach some Java courses this semester.  I hadn't had a chance to look at Java 8 until this past week, and so far I am impressed.  This post is not really a tutorial; it's more of a small demonstration of the possibilities.

The key to Java 8's functional features is the new stream library.  A Stream in Java is a lazy sequence of values.  Each of the collection classes in the Collections framework has a .stream() method to get a stream view of its contents.  The Arrays class also contains a static method to get a stream view of an array.  The following example, which converts each command-line argument to an integer and prints the sum of the positive values only, demonstrates the framework nicely:

 import java.util.Arrays;  
 public class SimpleDemo1 {  
      public static void main(String[] args) {  
           System.out.println              
              (Arrays.stream(args).map(s -> Integer.parseInt(s))  
                                  .filter(x -> x > 0)  
                                  .reduce(0, (x, y) -> x + y));  
      }  
 }  

Here is an equivalent program in Haskell:

 module Main where  
 import System.Environment  
 main = do args <- getArgs  
           putStrLn $ show 
                    $ foldr (+) 0 
                    $ filter (\x -> x > 0) 
                    (map read args :: [Integer])

Here are some noteworthy similarities:
  • Both languages use -> to separate anonymous function arguments from the code. 
  • Java now includes (limited) type inference and an implicit return.
  • Both use lazy evaluation of the streams.
  • Both implementations have a similar amount of text:
    • Haskell: 30 words, 223 characters
    • Java: 31 words, 264 characters
And some noteworthy differences:
  • The fundamental paradigms of each language dictate the order in which the computations are written.  Note how the Haskell example has the higher-order functions placed in the reverse order of the Java example.
  • Lazy evaluation remains pervasive in Haskell, while being confined to this new little corner of Java.
  • The syntactic overhead of converting to streams in Java is not too bad, but it is not trivial either.  Still, for Java especially, this is an impressive reduction of boilerplate.
I will definitely be including this material in my Data Structures course this semester.  I plan to post periodically regarding how I approach the topic and how the students respond.

No comments:

Post a Comment