Saturday 6 July 2013

Pattern Matching (Java - non-generic)

The purpose of this page is to document a pattern matching class (without generics) as an accompaniment to the blog Using TotallyLazy the functional library for Java. To view a more complex version of the class (which does use generics) please go to Pattern Matching (Java - using generics).

I use pattern matching a lot in functional languages and find it very natural and readable (to the initiated). Therefore, when I come back to imperative languages such as Java, I find wading through the many nested branching 'if' statements quite horrible and antiquated. For this reason I decided to create a pattern matching class in Java.

Here is an example of how to use the pattern matching class. At the end the 'matchResult' string will equal "47f" because the input parameters match the "with(_, 'f')" method. The underscore character '_' is used as a wildcard.

import com.googlecode.totallylazy.Function1;
import static net.intrepidis.library.functional.PatternMatch._;
import static net.intrepidis.library.functional.PatternMatchMany.matchMany;
class Example {
    void example() {
        String matchResult =
            matchMany(7, 'f').returns(String.class)
            .with(1, 'a').then(returnWith("1"))
            .with(2, 'f').then(returnWith("2"))
            .with(1, 'f').then(returnWith("3"))
            .with(_, 'f').then(returnWith("4"))
            .with(1, _).then(returnWith("5"))
            .with(_, _).then(returnWith("6"))
            .result();
    }

    private static Function1<Object[], String> returnWith(final String position) {
        return new Function1<Object[], String>() {
            public String call(Object[] ps) {
                return position + ps[0] + ps[1];
            }
        };
    }
}

Below is the full listing of the PatternMatchMany class:


package net.intrepidis.library.functional;

import com.googlecode.totallylazy.Function1;

import static net.intrepidis.library.functional.PatternMatch.areEqual;
import static com.googlecode.totallylazy.Sequences.sequence;

public class PatternMatchMany<TR> {
    private boolean matched = false;
    private TR result;
    private final Object[] parameters;

    private PatternMatchMany(Object[] parameters) {
        this.parameters = parameters;
    }

    public static PatternMatchMany<Object> matchMany(Object... parameters) {
        return new PatternMatchMany<Object>(parameters);
    }

    @SuppressWarnings("UnusedParameters")
    public <UR> PatternMatchMany<UR> returns(Class<UR> returnType) {
        return new PatternMatchMany<UR>(parameters);
    }

    public TR result() {
        return result;
    }

    public With with(Object... candidates) {
        return new With(candidates);
    }

    public class With {
        private boolean act = false;

        private With(Object[] candidates) {
            if (matched) {
                // A match has already been found, so don't match again.
                return;
            }

            // Are any unmatched?
            boolean areAnyDifferent =
                    sequence(candidates) // These possible matches...
                            .zip(sequence(parameters)) // pair up with these inputs...
                            .map(areEqual) // make 'true' if they're equal...
                            .contains(false); // return true on the first non-match.

            if (!areAnyDifferent) {
                // All parameters have matched.
                act = matched = true;
            }
        }

        public PatternMatchMany<TR> then(TR returnValue) {
            if (act) {
                result = returnValue;
            }
            return PatternMatchMany.this;
        }

        public PatternMatchMany<TR> then(Function1<Object[], TR> actor) throws Exception {
            if (act) {
                result = actor.call(parameters);
            }
            return PatternMatchMany.this;
        }
    }
}

This code is completely free to use by anybody. It is held under the Do What You Want To Public License: http://tinyurl.com/DWYWTPL

No comments:

Post a Comment