Click here to Skip to main content
Email Password   helpLost your password?

Introduction

The goal of the swensen.functional package is to provide Java with:

  1. a set of generic functors, and
  2. a wrapper type unifying Java arrays and Iterables with many hall-mark functional features.

The project was conceived by the author as a means to grow comfortable with Java, and inspired by the author's experience with C# and F#. The types in swensen.functional do not seek to extend Java as a language, they work with basic language features, making heavy use of generics and anonymous inner classes in particular.

Generic Functors (Func0, Func1, Func2, ... ; Action0, Action1, Action2, ...)

Comparator and Runnable are well-known examples of functors in Java. They are interfaces specifying a single method, and they are often created via anonymous inner classes. With generics, we can forego ad-hoc functors and refine ourselves to two varieties: FuncX (non-Void return type), and ActionX (Void return type), where X is the number arguments encapsulated by our functors (arbitrarily implemented through 5 in swensen.functional). For example, let's take Func2:

package swensen.functional;

/**
 * @author Stephen Swensen
 * A generic functor for non-Void methods with two parameters.
 * @param <T1>	the type of the first parameter of call
 * @param <T2>	the type of the second parameter of call
 * @param <R>	the return type of call
 */
public interface Func2 <T1,T2,R> {
	/**
	 * Invoke this functor synchronously.
	 * @param t1	the first parameter
	 * @param t2	the second parameter
	 * @return	the return value
	 */
	public R call(T1 t1, T2 t2);
}

Here we see that T1, T2, and R are type parameters allowing us to encapsulate any method with two (non-primitive) arguments T1, T2 and a (non-primitive) return type R. We might use it as follows:

Func2<String,String,Integer> func = new Func2<String,String,Integer>() {
	public Integer call(String t1, String t2) {
		t1 = (t1 == null) ? "" : t1.trim();
		t2 = (t2 == null) ? "" : t2.trim();
		return t1.length() - t2.length();
	}
};
		
Integer result = func.call("hello world   ", "  hello world"); //result == 0

swensen.functional also supplies a set of functors designed for compatibility between generic and legacy functors: CallableFunc, ComparatorFunc, and RunnableAction. Each of these abstract classes implements both their indicated legacy functors and their generic analogs, and may be instantiated directly by anonymous inner class or by the static method from overloaded to accept pre-existing instances of either supertype. One other functor, Predicate<T>, is an abstract class implementing Func1<T,Boolean> and exposing several methods for building compound Predicates.

Sequences (Seq)

Seq is a quasi immutable type implementing Iterable and featuring method chaining and lazy evaluation. Seqs are constructed via the static method of overloaded for Iterables and all primitive and generic vararg arrays. The wrapped data-structure is not actually copied into the resulting Seq and it is never modified: all operations against Seq's result in new Seq's (however, the data-structure could be externally modified, compromising the immutability of Seq). This is achieved by using anonymously constructed Iterators to produce Seq elements on demand (a technique used throughout the Seq class):

/**
 * Construct a sequence from a generic array.
 * Lazy Iterable wrapper around arr, no new memory is allocated.
 * Outside structural mutation to arr compromises the immutability of this Seq.
 * Note that since we override Seq.of for each and every primitive array type, we will
 * never get surprising behavior like that seen with Arrays.asList.
 * @param <E>	element type
 * @param arr	an array, may be null
 * @return	a new Seq constructed from arr 
 * (or Seq.EMPTY if arr is null or length 0)
 */
public static <E> Seq<E> of(final E...arr)
{
	if(arr == null || arr.length == 0)
		return Seq.getEmpty();
	
	return Seq.of(new Iterable<E>(){
		public Iterator<E> iterator() {
			return new Iterator<E>(){
				int cur = 0;
				
				public boolean hasNext() {
					return cur < arr.length;
				}

				public E next() {
					if(cur < arr.length)
					        return arr[cur++];
					else
					        throw new NoSuchElementException();
				}
				
				public void remove() {
					throw new UnsupportedOperationException();
				}
			};
		}
	});
}

Seq makes extensive use of the functors we introduced earlier. It includes classic functional methods like filter, map, and foldl along with many others useful for manipulating and generating Seq object streams. For example:

/* elements generated on demand, no memory allocated 
   up front except for Seq object itself. */
Seq<Integer> range1 = Seq.range(1, 10); 

/* range1 is neither mutated nor traversed. 
   could also do range1.append(34,36,38,29), leveraging varargs + autoboxing */
Seq<Integer> range2 = range1.append(Seq.of(new int[] {34, 36, 38, 39})); 

Func1<Integer,Integer> minus3 = new Func1<Integer,Integer>(){
	public Integer call(Integer t1) {
		return t1 - 3;
	}
};

/* could alternatively be a Predicate<Integer> */
Func1<Integer,Boolean> isPositiveOdd = new Func1<Integer,Boolean>(){
	public Boolean call(Integer t1) {
		return (t1 % 2) == 1;
	}
};

/* {1,2,3,4,5,6,7,8,9,10,34,36,38,39} -> 
   {-2,-1,0,1,2,3,4,5,6,7,31,33,35,36} -> {1,3,5,7,31,33,35} */
Seq<Integer> result = range2.map(minus3).filter(isPositiveOdd);

Action1<Object> println = new Action1<Object>() {
	public void call(Object obj){
		System.out.println(obj);
	}
};

/* apply the println Action to each element immediately. */
result.iter(println);

Func2<Integer,Integer,Integer> sum = new Func2<Integer,Integer,Integer>(){
	public Integer call(Integer t1,Integer t2) {
		return t1 + t2;
	}
};

/* 115 */
Integer resultSum = result.foldl(0, sum);

Final Remarks

Additional insight into the swensen.functional package may be gained by studying the source code and reviewing the comments. Any suggestions or bug reports would be much appreciated.

History

Version Date Description
1.00 November 11, 2009 First release
1.01 November 14, 2009

Added null argument checks to several methods
Changed Seq.any and Seq.all to accept Func1 instead of Predicate
Overloaded Seq.min/max/sort for Comparator, Func2, and ComparatorFunc
Fixed Seq.isEmpty()

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralErrors compiling
mbedward
16:40 16 Nov '09  
I get the following errors when trying to compile with both JDK 1.5 or 1.6 on Mac OSX...

/Users/michael/coding/functional/src/swensen/functional/Seq.java:1214: cannot find symbol
symbol   : method from(swensen.functional.Func2<capture#267 of ? super E,capture#511 of ? super E,capture#258 of ? extends java.lang.Integer>)
location: class swensen.functional.ComparatorFunc
                        return this.sort((Comparator<? super E>)ComparatorFunc.from(comparator));
                                                                                                         ^
/Users/michael/coding/functional/src/swensen/functional/Seq.java:1291: cannot find symbol
symbol   : method from(swensen.functional.Func2<capture#520 of ? super E,capture#981 of ? super E,capture#396 of ? extends java.lang.Integer>)
location: class swensen.functional.ComparatorFunc
                        return this.max((Comparator<? super E>)ComparatorFunc.from(comparator));
                                                                                                      ^
/Users/michael/coding/functional/src/swensen/functional/Seq.java:1366: cannot find symbol
symbol   : method from(swensen.functional.Func2<capture#97 of ? super E,capture#207 of ? super E,capture#640 of ? extends java.lang.Integer>)
location: class swensen.functional.ComparatorFunc
                        return this.min((Comparator<? super E>)ComparatorFunc.from(comparator));
                                                                                                      ^
3 errors
GeneralRe: Errors compiling [modified]
Stephen Swensen
17:58 16 Nov '09  
Hmm. Using Eclipse 3.4.2 / JDK 1.6 / Windows 7, I just downloaded the source code and imported it into a fresh workspace and was able to compile a little app using the offending Seq methods. I wonder if there is an issue with the text encoding since we are using different operating systems. When you look at the source code for ComparatorFunc.java do you see the supposedly missing method starting on line 25:

public static <T> ComparatorFunc<T> from(final Func2<? super T,? super T, ? extends Integer> func)
{
if(func == null)
throw new NullPointerException("func");

return new ComparatorFunc<T>() {
public Integer call(T t1, T t2) {
return func.call(t1, t2);
}
};
}

You might try rewriting each of those bad lines in Seq so that they look something like this:

return this.sort(new Comparator<E>() {
public int compare(E o1, E o2) {
return comparator.call(o1, o2);
}
});

The ComparatorFunc.from(Func2) method that the compiler is complaining about is not that crucial, so if you can get everything to compile using this solution, then you should be in good shape.

I wish I could be of better help, but until I get my hands on a Mac (maybe a week or two, hopefully less!) and reproduce the errors, I can only speculate and suggest workarounds. Please let me if you're able to get it working. Good luck!

modified on Monday, November 16, 2009 11:08 PM

GeneralRe: Errors compiling
mbedward
18:49 16 Nov '09  
Hi Stephen,

It's not a character encoding problem. I have it set to UTF-8.   It has something to do with the type parameter syntax and could be specific to Apple's JDK.  

Anyway, the good news is that modifying the three Seq methods as you suggested gets around the problem and I've just built the library successfully !

cheers
Michael
GeneralRe: Errors compiling
Stephen Swensen
19:01 16 Nov '09  
Excellent! Now I can sleep soundly.

ss
GeneralAn interesting and accessible article
mbedward
0:54 16 Nov '09  
Thanks for this article Stephen. I've wanted to dip my toe into functional programming for some time but somehow never get around to it. This package looks like it might make the toe-dipping easier by allowing one to gradually incorporate functional usage in everyday Java programming.

cheers
Michael
GeneralRe: An interesting and accessible article
Stephen Swensen
4:50 16 Nov '09  
Thanks Michael, glad to hear it. It's precisely my hope that this package serve as you've described, much as the IEnumerable extensions have done in C#.


Last Updated 15 Nov 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010