// XEmacs: This file contains Java source code, so use -*- JDE -*- mode.

// Compose: demonstrate procedural composition in Java

// John David Stone
// Department of Mathematics and Computer Science
// Grinnell College
// stone@cs.grinnell.edu

// created August 30, 2000
// last revised August 30, 2000

// A unary procedure is something that, when applied, takes an object as
// argument and returns an object as value.

interface UnaryProcedure {
    Object apply(Object argument);
}

// A binary procedure is something that, when applied, takes two objects as
// arguments and returns two objects as values.

interface BinaryProcedure {
    Object apply(Object left, Object right);
}

// A Compose object is a kind of binary procedure that, when applied, takes
// two unary procedures and returns a unary procedure that, when applied,
// combines the successive effects of the two unary procedures.

public class Compose implements BinaryProcedure {

    // The composite unary procedure is an instance of the inner class
    // Composite.

    class Composite implements UnaryProcedure {
        private UnaryProcedure outer;
        private UnaryProcedure inner;

        Composite(UnaryProcedure outerCandidate,
                  UnaryProcedure innerCandidate) {
            outer = outerCandidate;
            inner = innerCandidate;
        }

        public Object apply(Object argument) {
            return outer.apply(inner.apply(argument));
        }
    }

    public Object apply(Object left, Object right) {
        return new Composite((UnaryProcedure) left,
                             (UnaryProcedure) right);
    }
}

// Here are two examples of unary procedures: Successor, which computes
// the successor of a Long, and Square, which computes the square of a Long.

class Successor implements UnaryProcedure {
    public Object apply(Object argument) {
        return (Object) new Long(((Long) argument).longValue() + 1);
    }
}

class Square implements UnaryProcedure {
    public Object apply(Object argument) {
        long arg = ((Long) argument).longValue();
        return (Object) new Long(arg * arg);
    }
}

