HOJ - Higher-Order Java

We present a library that adds curried higher order functions and tuple types to Generic Java.

Many of these ideas are taken from the functional programming language Haskell.

Library contents

Curried higher-order functions

If we have a type for functions, we can define higher-order functions such as map, filter, foldr, foldl, zipWith, curry, uncurry, flip. We can provide partial application.

In HOJ, functions are curried, that is, a n-argument function is defined as a one-argument function that returns a (n-1)-argument function. Unary (one-argument) functions are represented by the class Fun<A,B>. Binary (two-argument) functions are represented by the class Fun2<A,B,C>, which extends Fun<A,Fun<B,C>>.

Functions can be applied using their apply() method. If apply() is called with fewer arguments that the arity of the function, a new function is returned. There are several method declared for functions. Examples include compose(), which composes to functions to a new function, flip() which reverses the order in which a binary function takes its arguments and map() which applies a function to all elements of iterator, returning a new iterator.

Basic functions such as Id (the identity function) and Const (a constant function) are supplied.

Tuples

We often need pairs (or triples, quads etc.), for things like symbol table entries and coordinates.

The library contains classes such as Pair, the 2-tuple, and Triple, the 3-tuple, (and Unit, the 0-tuple, and Cell, the 1-tuple). The tuple classes also contain static functions such as zip (turns a tuple of collections into a collection of tuples.)

All tuple classes are immutable, as it simplifies concurrent programming, and no compelling arguments for making them mutable has been found.

Examples

grep

This is a simple grep-like utility that prints all lines from System.in that matches a pattern given on the command line.

import java.util.regex.*;

import io.*;
import fun.*;
import static fun.Filter.*;
import static fun.Compose.*;
import static fun.TakeWhile.*;
import static fun.Not.*;
import static fun.Eq.*;


/**
 * Prints lines from System.in that that match a given regexp.
 */
public class Grep {
   private static class Match extends Fun2<Matcher,String,Boolean> {
      public Boolean apply(Matcher matcher, String s) {
         return Boolean.valueOf(matcher.reset(s).matches());
      }
   }

   public static void main(String[] args) {
      Matcher matcher = Pattern.compile(".*"+args[0]+".*").matcher("");
      new Println(System.out).map(
         filter(new Match().apply(matcher), 
                takeWhile(compose(not(), eq(null)), 
                          new ReadLine(System.in).repeat())));
   }
}

Radix.java from Pizza

Example 3.1 from "Pizza into Java: Translating theory into practise" by Martin Odersky and Philip Walder, rewritten using HOJ.

import fun.Fun;

class Radix {
   int n = 0;
   Fun<Character,Boolean> radix(final int r) {
      return new Fun<Character,Boolean> () {
         public Boolean apply(Character c) {
            n++;
            return new Boolean('0' <= c.charValue() 
                               && c.charValue() < '0'+r);
         }
      };
   }
   String test() {
      Fun<Character,Boolean> f = radix(8);
      return f.apply(new Character('0'))+" "+f.apply(new Character('9'))+" "+n;
   }
}

The original example (written in Pizza) is:

class Radix {
   int n = 0;
   (char)->boolean radix(int r) {
      return fun (char c)->boolean {
            n++; return '0' <= c && c < '0'+r;
      };
   }  
   String test () {
      (char)->boolean f = radix(8);
      return f('0')+" "+f('9')+" "+n;
   }
}

Download

The development version of HOJ is available in the HOJ darcs repo. Use darcs darcs to get it:

$ darcs get http://www.cs.chalmers.se/~bringert/darcs/hoj/

The current version (as of 2005-12-22, though it hasn't really changed in quite some time) is also available as a tarball: hoj-20051222.tar.gz.

Platform

The library is intended to be used with the version of Java programming language supported by the Java 1.5.0 release.

Documentation

I gave a short presentation on HOJ in the Topics in Computer Languages class.

Related work

JGA (Java Generic Algorithms), http://jga.sourceforge.net/, contains classes for unary and binary functions. The functions are not curried and there are few higher order functions defined.

Pizza into Java: Translating Theory into Practice, M. Odersky and P. Wadler, 1997. The Pizza language extends Java to add genericity, higher-order functions and algebraic types. The main difference between the higher-order functions in Pizza and those in HOJ is that Pizza requires syntax extensions to Java, whereas HOJ is a library. Also Pizza's funcions does not seem to be curried. The genericity in Generic Java is based on that in Pizza.

Author

Bjorn Bringert, bjorn@bringert.net.

References