The Jambda 0.1 Documentation

Index

The Jambda documentation

Jambda is an attempt to provide the Java(TM) world with tools and concepts from functional programming (FP).

The goals are several:

This document is an attempt to introduce Java programmers into the FP world, and at the same time explain some (or most) of the features in Jambda.

Using functions

DemoFunctions is the building blocks of functional programming. They are used in

The simplest function

The simplest function is Fn0, which maps to a specific type, in this case String, from nothing:

Fn0<String> anonymousGreeting = new Fn0<String>() {
    public String apply() {
        return "Hello!";
    }
};
String greetingsPhrase = anonymousGreeting.apply();

The result is:

Hello!

This is an anonymous inner class, but in reality you will probably extract and group these domain functions by their purpose, e.g. as constants. Also, constants and static method can be imported with a static import, hence the code won't need being referenced by class name.

The mapping function

Fn1 maps data from one type to another, in this case Integer to String

String greetingsPhrase = personalGreeting.apply(new Integer(1));

The greetingsPhrase variable is:

Hello, nr 1!

The implementation of personalGreeting() is

public static Fn1<Integer, String> personalGreeting =
    new Fn1<Integer, String>() {
        public String apply(Integer nr) {
            return "Hello, nr " + nr + "!";
        }
    };

Using typed arguments

Although you could create functions like the one above, it makes much more sense to type the arguments. The resulting types can then in turn be used by other functions, and the readability of the code base increases.

In this case we want to transform a String (the user name) into a User, then the User to a Greeting, and for documentation purposes here we also want to turn the Greeting into a String. Each such mapping gets its own reusable mapping function:

String greetingsPhrase = greetingToString.apply(
        typedPersonalGreeting.apply(userCreator.apply("Daniel")));

The greetingsPhrase variable is (as before):

Hello, Daniel!

The implementations used are:

public static Fn1<String, User> userCreator =
    new Fn1<String, User>() {
        public User apply(String name) {
            return new User(name);
        }
    };
public static Fn1<User, Greeting> typedPersonalGreeting = 
    new Fn1<User, Greeting>() {
        public Greeting apply(User user) {
            return new Greeting("Hello, " + user.getName() + "!");
        }
    };
public static final Fn1<Greeting, String> greetingToString = 
    new Fn1<Greeting, String>() {
        public String apply(Greeting greeting) {
            return greeting.getGreeting();
        }
    };

The instantiable classes Greeting and User are just simple java beans, or structs as a functional programmer would put it.

Using composition to build complex functions

Instead of assembling the functions at the same time as they are executed, composition can be used.

In the last example, we went through String-User-Greeting-String. We can use the compose method and create a function to use later on:

Fn1<String, String> greetPerson = 
    userCreator.compose(typedPersonalGreeting.compose(greetingToString));

String greetingsPhrase = greetPerson.apply("Daniel");

This composition can of course be stored as any other domain function in some module (class) in your code base.

The greetingsPhrase variable is (as before):

Hello, Daniel!

Composition is commutative

The interesting thing about composition is that it is commutative, i.e. the function above could also be created using

Fn1<String, String> greetPerson = 
    userCreator.compose(typedPersonalGreeting).compose(greetingToString);
String greetingsPhrase = greetPerson.apply("Johan");

(note the placement of the parenthesis)

The greetingsPhrase variable is:

Hello, Johan!

Currying

Currying in functional programming is just what it sounds like; you take a function an you spice it with one or more of the values it requires, and it produces a new function.

The simplest example in Jambda is this:

Fn0<User> spicedUp = userCreator.curry("Daniel");

The userCreator is a function that takes a String and produces a User with that name. By currying it with the name, a new function is created, and that function will create and return a User when being called:

User user = spicedUp.apply();
assertEquals("Daniel", user.getName());

Currying functions with several parameters

For functions that take more parameters you have to curry from the left or from the right.

In this case we use the plus function that takes two integers and adds them to produce another integer.

Fn2<Integer, Integer, Integer> plus = Numeric.plus(IntegerType.integerType);

Here we do it both left- and right curry style:

Fn1<Integer, Integer> rightCurried = plus.rightCurry(2);
Fn1<Integer, Integer> leftCurried = plus.apply(3);

After this we got a function that waits for the second argument to complete the addition.

Integer rightCurryResult = rightCurried.apply(3);
Integer leftCurryResult = leftCurried.apply(2);
assertEquals(new Integer(5), rightCurryResult);
assertEquals(new Integer(5), leftCurryResult);

More about currying in jambda

All functions of N arguments (N=[2,6]) have an apply (leftCurry) that takes the left-most M (M=[1,N-1]) arguments and returns a function of N-M arguments.

Functions.Fn6<Byte, Short, Integer, Long, Float, Double, String> fn6 = 
    new Fn6<Byte, Short, Integer, Long, Float, Double, String>() {
        public String apply(Byte b, Short s, Integer i,Long l, Float f, Double d) {
            return b+s+i+l+f+d+"";
        }
  
    };

This is an example where, for N is 6 and M is 1, we curry down to the result:

Fn5<Short, Integer, Long, Float, Double, String> fn5 = fn6.apply((byte)1);
Fn4<Integer,Long,Float,Double,String> fn4 = fn5.apply((short)2);
Fn3<Long,Float,Double,String> fn3 = fn4.apply(4);
Fn2<Float, Double, String> fn2 = fn3.apply(8L);
Fn1<Double,String> fn1 = fn2.apply(16F);
String result = fn1.apply(32D);

assertEquals("63.0", result);

There is also a rightCurry method that does the same from the right.

Fn5<Byte, Short, Integer, Long, Float, String> fn5_right = fn6.rightCurry(64D);
Fn4<Byte, Short, Integer, Long, String> fn4_right = fn5_right.rightCurry(32F);
Fn3<Byte, Short, Integer, String> fn3_right = fn4_right.rightCurry(16L);
Fn2<Byte, Short, String> fn2_right = fn3_right.rightCurry(8);
Fn1<Byte, String> fn1_right = fn2_right.rightCurry((short)4);
Fn0<String> fn0_right = fn1_right.curry((byte)2);

String resultOfRightCurry = fn0_right.apply();
assertEquals("126.0", resultOfRightCurry);

Several arguments can be passed, both from the left and from the right:

Fn1<Long, String> fn1_leftAndRight = fn6.apply((byte)1, (short)2, 4).rightCurry(100F, 1000D);
String resultOfLeftAndRightCurry = fn1_leftAndRight.apply(10000L);
assertEquals("11107.0", resultOfLeftAndRightCurry);

Using options

Options are used to make the decision on data existence. Functions are passed to an Option to handle the cases some data exist, i.e. some, and no data exists, i.e. none

The decision on what execution path to go is made when the Option is created, i.e. which is at the earliest possible moment. This is a way of building in the null-pattern in FP.

The some option

In this case we create an option with data by calling the Option factory method some with the data to be provided, in this case a User.

Option<User> user = Option.some(new User("Daniel"));
Greeting greeting = user.map(typedPersonalGreeting, typedAnonymousGreeting);
assertEquals("Hello, Daniel!", greeting.getGreeting());

The map method takes two functions:

  1. The function that will map from the data type to the output type (the first argument)
  2. The function that will provide the output for the case when no data existed (the second argument)
In this case, there is data available, and the first function will be called.

The none option

Here we create an option without data by calling the factory method none. Since it represents no data available, no data need to be provided.

Option<User> option = Option.none();
Greeting greeting = option.map(typedPersonalGreeting, typedAnonymousGreeting);
assertEquals("Hello!", greeting.getGreeting());

In this case, the second function will be used to create the Greeting

Maps and lists

Using unions

A Union is used when there are two possible execution paths with different in-data. There is a left Union and a right Union representing the two paths.

The difference from the Option is that the Option only has data to act upon in the some-case, whereas in the none-case it only has a return value.

Left and right

Unions can be used to handle e.g exceptions transparently. Here we create a Union for the normal case:

Union<User, Exception> userRetrievalWentWell = Union.left(new User("Daniel"));
Greeting normalGreeting = userRetrievalWentWell.map(typedPersonalGreeting, exceptionGreeting);
assertEquals("Hello, Daniel!", normalGreeting.getGreeting());

In the case we couldn't retrieve the user we have to create another greeting. This is done by passing exactly the same functions, but the Union knows which function to apply depending on whether it was the left or the right Union that was created.

String message = "Couldn't retrieve user";
Union<User, Exception> oopsExceptionWasThrown = Union.right(new Exception(message));
Greeting unsuccessfulGreeting = oopsExceptionWasThrown.map(typedPersonalGreeting, exceptionGreeting);
assertEquals("There was an Exception: " + message, unsuccessfulGreeting.getGreeting());

The function that handles the exception looks like this:

public static Fn1<Exception, Greeting> exceptionGreeting =
    new Fn1<Exception, Greeting>() {
        public Greeting apply(Exception exception) {
            return new Greeting("There was an Exception: "
                    + exception.getMessage());
        }
    };

This is of course not the only application of Unions, but it shows a powerful application.

Normally the creation of the Union, i.e. calling the left or right factory method, is done at the place where this information appears. In the case of the User/Exception, it is probably made in the method where the exception first can be caught.

Using sequence

Mapping a collection

Often when programming you have a collection of data (of the same type), and you want to create a new collection of the same size, but with new data based on the data in the original collection.

In this case we have a sequence of names, and we want to create a list of User objects:

Iterable<String> userNames = Sequence.createSequence("Daniel", "Johan", "Joakim");

Iterable<User> users = Sequence.map(userNames, userCreator);
Iterator<User> iterator = users.iterator();
assertEquals("Daniel", iterator.next().getName());
assertEquals("Johan", iterator.next().getName());
assertEquals("Joakim", iterator.next().getName());

In this case we are returned an Iterable since the function provided to the map function created a User object for each String object in the provided List.

Mapping a collection to multple results per element

The first argument of the mapFlat method takes a key sequence and will use the elements of that sequence as in-data to the second argument, a function that creates new sequences for every key. mapFlat will aggregate all those new sequences into one consecutive sequence.

In this case the key sequence is the numbers 1 and 2, and the function just creates a new sequence of names with the numbers attached at the end.

Iterable<Integer> keySequence = Sequence.createSequence(1, 2);

Iterable<String> users = 
    Sequence.mapFlat(
            keySequence, 
            new Fn1<Integer, Iterable<String>>() {
                public Iterable<String> apply(Integer key) {
                    return Sequence.createSequence("Daniel" + key, 
                                                    "Johan" + key, 
                                                    "Joakim" + key);
                }
            });
Iterator<String> iterator = users.iterator();
assertEquals("Daniel1", iterator.next());
assertEquals("Johan1", iterator.next());
assertEquals("Joakim1", iterator.next());
assertEquals("Daniel2", iterator.next());
assertEquals("Johan2", iterator.next());
assertEquals("Joakim2", iterator.next());
assertFalse(iterator.hasNext());

The fold functions

The map function uses one of the fold functions: foldLeft or foldRight. The fold functions can be used to either map a collection to another, like we saw in the map example, or it can be used to aggregate or accumulate data, which we will show now.

Let us start with a sequence containing some names:

Iterable<String> userNames = Sequence.createSequence("Daniel", "Johan", "Joakim");

Now we will use an aggregating function to create a string:

public static Fn2<String, String, String> aggregateStrings = 
    new Fn2<String, String, String>() {
        public String apply(String string, String accumulation) {
            return "(" + accumulation + string + ")";
        }
    };

This function simply appends the string parameter to the accumulation parameter and puts parenthesis around.

Then we use the foldLeft and the foldRight respectively to apply the function to the list of names

String foldLeft = Sequence.foldLeft(userNames, aggregateStrings, "");
String foldRight = Sequence.foldRight(userNames, aggregateStrings, "");

The results are

foldLeft: (((Daniel)Johan)Joakim)
foldRight: (((Joakim)Johan)Daniel)

The result depends on how the fold was performed, and on how the function makes the aggregation.

Filtering a collection

Other times you have a collection of data and you want to filter out some of the elements.

We have the same sequence of names, but we only want the names that start with J:

Iterable<String> userNames = Sequence.createSequence("Daniel", "Johan", "Joakim");

Iterable<String> users = Sequence.filter(userNames, acceptStringsStartingWithJ);
Iterator<String> iterator = users.iterator();
assertEquals("Johan", iterator.next());
assertEquals("Joakim", iterator.next());
assertFalse(iterator.hasNext());

The filtered iterator does not contain the name Daniel

The function used to filter looks like this:

public static Fn1<String, Boolean> acceptStringsStartingWithJ =
    new Fn1<String, Boolean>() {
        public Boolean apply(String string) {
            return string.indexOf("J")==0;
        }
    };

Creating new sequences

Sequence can be used as a sequence factory to create new sequences. Here, we create a never-ending sequence of integers:

Fn1<Integer, Integer> incrementor = new Fn1<Integer, Integer>() {
    public Integer apply(Integer previous) {
        return previous+1;
    }
};

Iterable<Integer> range = Sequence.range(incrementor, 0);
Iterator<Integer> rangeIterator = range.iterator();
assertEquals(new Integer(0), rangeIterator.next());
assertEquals(new Integer(1), rangeIterator.next());
assertEquals(new Integer(2), rangeIterator.next());

Note that range(...) takes a generic incrementor that calculates the next value in the sequence, hence your range can contain objects of any type. The seed is the first value for the range and must be of the same generic type as the incrementor.

If you want a limited range, provide a limiter:

Fn1<Integer, Boolean> limiter = new Fn1<Integer, Boolean>() {
    public Boolean apply(Integer number) {
        int limit = 2;
        return number<limit;
    }
};
Iterable<Integer> limitedRange = Sequence.range(incrementor, limiter, 0);
Iterator<Integer> limitedRangeIterator = limitedRange.iterator();

assertEquals(new Integer(0), limitedRangeIterator.next());
assertEquals(new Integer(1), limitedRangeIterator.next());
assertFalse(limitedRangeIterator.hasNext());

Limit a collection

takeWhile can limit a collection to end when a condition is satisfied.

Iterable<Integer> naturalNumbers = Sequence.range(plus(integerType).apply(1), 0);

Iterable<Integer> numbersUpTo5 = Sequence.takeWhile(naturalNumbers, smallerThan(integerType).rightCurry(6));
Iterator<Integer> iterator = numbersUpTo5.iterator();
assertEquals(new Integer(0), iterator.next());
assertEquals(new Integer(1), iterator.next());
assertEquals(new Integer(2), iterator.next());
assertEquals(new Integer(3), iterator.next());
assertEquals(new Integer(4), iterator.next());
assertEquals(new Integer(5), iterator.next());
assertFalse(iterator.hasNext());

Making a generic tree

A typesafe generic tree

This is how to make a generic tree. The only thing is that the type of the visitor must be decided when creating the nodes, and you need to provide the node constructor a function that is called on visits.

Optionally, as below, you can also provide functions to be called before and after traversal of children if you need tree structure information.

Lets create the functions:

Fn2<Integer, Visitor, Visitor> onVisitInteger = new Fn2<Integer, Visitor, Visitor>() {
    public Visitor apply(Integer integer, Visitor visitor) {
        return visitor.visitInteger(integer);
    }
};
Fn2<String, Visitor, Visitor> onVisitString = new Fn2<String, Visitor, Visitor>() {
    public Visitor apply(String string, Visitor visitor) {
        return visitor.visitString(string);
    }
};
Fn1<Visitor, Visitor> beforeChildren = new Fn1<Visitor, Visitor>() {
    public Visitor apply(Visitor visitor) {
        return visitor.beforeChildren();
    }
};
Fn1<Visitor, Visitor> afterChildren = new Fn1<Visitor, Visitor>() {
    public Visitor apply(Visitor visitor) {
        return visitor.afterChildren();
    }
};

These functions should be kept in conjunction with the specific visitor.

Now lets build the tree.

Node<Integer,Visitor> root = Node.create(1, onVisitInteger, beforeChildren, afterChildren);
        
root.add(
        root.create("Daniel", onVisitString)
            .add("Johan", onVisitString))
    .add(2, onVisitInteger);

Finally, we create a visitor and traverse the created tree:

final StringBuffer result = new StringBuffer();
root.traverse(new Visitor(){
    int level = 1;
    public Visitor visitInteger(Integer i) {
        append(i);
        return this;
    }
    // This is not kosher...
    private void append(Object object) {
        result.append("Level " + level + ": " + object);
    }
    public Visitor visitString(String s) {
        append(s);
        return this;
    }
    public Visitor beforeChildren() {
        level++;
        return this;
    }
    public Visitor afterChildren() {
        level--;
        return this;
    }
});
assertEquals(
        "Level 1: 1" +
        "Level 2: Daniel" +
        "Level 3: Johan" +
        "Level 2: 2", result.toString());

Parallel execution

Execute function with the same return type

Jambda can help you execute functions in parallel. In this case we create a function that we later curry with a number for the output.

Fn1<Integer,String> fn = new Fn1<Integer, String>() {
    public String apply(Integer nr) {
        try {
            long slept = (long) (Math.random()*200);
            Thread.sleep(slept);
            return "Nr " + nr + " slept for " + slept + "ms";
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
};
Iterable<String> results = 
    Parallel.parallel(Sequence.createSequence(
            fn.curry(1),
            fn.curry(2),
            fn.curry(3),
            fn.curry(4),
            fn.curry(5),
            fn.curry(6)), 3);

The result is


Nr 1 slept for 134ms
Nr 2 slept for 152ms
Nr 3 slept for 74ms
Nr 4 slept for 146ms
Nr 5 slept for 26ms
Nr 6 slept for 78ms

Different return values

If you need to have different return types, use the functions taking a Tuple of functions.

Fn1<Integer,String> delay1 = new Fn1<Integer,String>() {
    public String apply(Integer sleep) {
        return "Slept " + delay().apply(sleep.longValue()) + "ms";
    }
};
Fn1<String,Long> delay2 = new Fn1<String,Long>() {
    public Long apply(String sleep) {
        return new Long(delay().apply(Long.parseLong(sleep)));
    }
};
ExecutorService executor = Executors.newFixedThreadPool(2);

long start = System.currentTimeMillis();

Tuple2<Fn0<String>, Fn0<Long>> functions = Tuples.duo(delay1.curry(100),delay2.curry("101"));
Tuple2<String, Long> result = Parallel.parallel(functions, executor);

long end = System.currentTimeMillis();

assertEquals(result.getFirst(), "Slept 100ms");
assertEquals(result.getSecond(), new Long(101));
        
assertTrue("Should take at least 100ms", end-start>=100);
assertTrue("Should take less than 200ms due to threading", end-start<200);

Practical usages

Creating a template engine

Passing the rendering function to the renderer

Often when implementing templating you wish to render things differently depending on the context.

Functions can be a way to provide that variation.

In this case we want to render in HTML a list of links as a main menu.

Iterable<Link> links = createLinks();

String result = Sequence.foldLeft(
        Sequence.map(links, Rendering.linkToString()), 
        Rendering.stackAccumulator(), 
        "");

createLinks() creates a bunch of links for us. The result is:

<a href="http://www.agical.com">Agical</a>
<br/>
<a href="http://www.agilesweden.se">Agile Sweden</a>
<br/>
<a href="http://www.agilealliance.org">Agile alliance</a>
<br/>

The link transformer looks like this:

public static Fn1<Link, String> linkToString() {
    return new Fn1<Link, String>() {
        public String apply(Link arg) {
            return "<a href=\"" + arg.getUrl() + "\">" + arg.getText() + "</a>";
        }
    };
}

The stack accumulator looks like this:

public static Fn2<String, String, String> stackAccumulator() {
    return new Fn2<String, String, String>() {
        public String apply(String string, String accumulator) {
            return accumulator + string + "\n<br/>\n";
        }
    };
}

For an article

In the same way, we can present articles:

Iterable<Article> articles = createArticles();
String result = Sequence.foldLeft(
        Sequence.map(articles, Rendering.articleToString()), 
        Rendering.stackAccumulator(), 
        "");

The result is:

<h1>FP takes the lead</h1>
<p><i><b>Functional programming ...</b></i></p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p>

<br/>
<h1>Java and functional programming</h1>
<p><i><b>Java isn't a bad option...</b></i></p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p>

<br/>
<h1>Agical does it again</h1>
<p><i><b>Jambda takes the FP paradigm to Java...</b></i></p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p>

<br/>

Adding it all to a page

Euler

Project Euler in jambda.

Euler problem1

The problem: If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.

A solution in F#:

List.fold_left (+) 0 (List.filter (fun n -> (n%3 = 0) || (n%5 = 0)) [0 .. 999])        

In jambda it could be solved like this:

Integer sum = foldLeft( 
        filter(takeWhile(range(plus(integerType).apply(1), 0), smallerThan(integerType).rightCurry(1000)), 
                new Fn1<Integer, Boolean>() {
                    public Boolean apply(Integer x) {
                        return x % 5 == 0 || x % 3 == 0;
                    }
                }),
        plus(integerType),
        0);

assertEquals(new Integer(233168), sum);

Euler problem2

The problem: Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

Find the sum of all the even-valued terms in the sequence which do not exceed four million.

A sollution in Haskell:

module P2 where
    problem2:: Int -> Int
    problem2 y =    sum [ x | x <- takeWhile (<= y) fibonacci, mod x 2 ==0]
        where
        fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)

In jambda it could be solved like this:

Iterable<Integer> fibs = range(plus(integerType), 0, 1);

Integer sum = 
    sum(integerType, filter(takeWhile(fibs, greaterThan(integerType).apply(4000000)), 
               modulo(integerType).rightCurry(2).compose(equalTo(integerType).apply(0))));

assertEquals(new Integer(4613732), sum);
© 2008-2009 Johan Kullbom, Daniel Brolund, Joakim Ohlrogge