commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Rodney Waldhoff <rwaldh...@apache.org>
Subject Re: [math][functor] More Design Concerns
Date Fri, 27 Jun 2003 15:27:16 GMT
Mark Diggory wrote:

> I'm hoping you might be able to provide
> some viewpoints as to the benefits of
> [the] Functor pattern, and any ideas you
> may have about its application to math?

The base idea behind functor is to create large and complex behaviors by
composing small and simple (and generally stateless) ones.

Part of this is the same as the mathematical notion of the composition of
functions--build a composite function c out of simpler functions. For
example:

  c(x) :=  f(g(x))

or,

  c(x,y) := h(f(x),g(y))

or,

  c(x,y) := f(x) when g(x) is true
            f(y) otherwise
etc.

Another part of this is the composition of arbitrary functions into
"templated" algorithms, similar to the Gang-of-Four's "Template Method"
pattern, except generally using composition instead of inheritance/method
overloading.  For example, a function like "inject" or "fold":

  Object inject(Iterator iter, Object seed, BinaryFunction f) {
    while(iter.hasNext()) {
      seed = func.evaluate(seed,iter.next());
    }
    return seed;
  }

can be used for a very large number of things.  For instance, ((x<y)?y:x))
makes inject into a "max" function, (x+y) makes inject into a "sum"
function, ((x*count)+y)/++count) makes inject into an "average" function,
etc.  When combined with the first notion of composition, this becomes a
very powerful technique.

I think this strategy is quite applicable to mathematics and commons-math
in particular.

Mark also wrote (I think this was Mark anyway):

> b.) Which design strategy is of more interest
> to the project? Small compact classes used to
> complete one specific task, where the programming
> is primarily OOD in nature? or Feature rich
> interfaces that encapsulate full ranges of
> functionality, where programming is primarily
> "procedural" in nature?

IMO, and as I lurker on commons-math at best, my opinion probably doesn't
and shouldn't carry a lot of weight, the appropriate design strategy is a
functional one, which shares some of the attributes what you describe as
OO (but functional is decidedly not OO).

Phil Steitz wrote:

> IMHO, the most important considerations
> are 1) how easy the library will be to
> navigate and use 2) how maintainable it
> will be and 3) how well it will perform
> (including resource requirements).

I agree with that, and think a functional approach would definitely
support the second point (maintainability) and IMO the first (ease of use)
as well.  The third point (performance) probably isn't helped by a
functional approach, although it may not be hurt by it either.  I'm of the
"don't do it, yet" philosophy on optimization though.

Phil also wrote:

> My opinion here is that a univariate real
> function is an object in its own right.
> I suppose that it could extend Functor,
> but I do not see the point in this and
> I would personally not like having to
> lose the typing and force use and casting
> to Double to implement
> Object evaluate(Object obj);

A few observations here:

1) A common approach to this sort of problem would be something like:

abstract class <Type>UnaryFunction implements UnaryFunction {
   final Object evaluate(Object obj) {
      return (<Type>)evaluate((<Type>)obj);
   }

   abstract <Type> evaluate<Type>(<Type> type);
}

class MyFuction extends <Type>UnaryFunction;

etc., but I'll argue that it's the distinction between primitives and
their object wrappers that are the concern here.

2) It may not be necessary to commit to extending or using commons-functor
from the beginning.  Any functional approach is readily adapted to
commons-functor.  E.g., given:

 interface UnivariateFunction {
   Double evaluate(Double x);
 }

it's not difficult to create a (commons-functor) UnaryFunction adapter:

 class Univariate2UnaryFunction implements UnaryFunction {
    Univariate2UnaryFunction(UnivariateFunction f) {
      this.f = f;
    }

    Object evaluate(Object obj) {
      return f.evaluate((Double)obj);
    }

    UnivariateFunction f = null;
 }

which would join the two libraries, but again it's the distinction between
primitives and their object wrappers that are the concern here.

3) I can imagine primitive versions of the commons-functor interfaces,
say:

interface DoubleFunction { double evaluate(double); }

with either parallel implementations of the functor composers, adapters,
and utilities, or with adapters between the two (or both).  (I'm the guy
behind most of collections.primitives.* after all.)  But I don't imagine
those being part of a version 1 release.


In short, and for what it's worth, my suggestion is this:

* Where appropriate (and I think it's probably appropriate often),
commons-math should follow a functional design, like the one we see in
UnivariateFunction.

* This functional approach will probably be based on primitives rather
than objects, and hence fully independent of anything in commons-functor.

* When object based functors are created, I'd consider implementing the
corresponding commons-functor interface, or at least leaving room for
doing so in the future (for instance, using the same method signatures
with the same semantics, but not necessarily extending or implementing the
functor type for now).

* Let's come back to this discussion after a v1 release of both math and
functor, and see if we want to start bridging the two APIs more directly.
I think doing so could be very useful, and with just a little bit of
forethought, should be fairly easy as well.

- Rod <http://radio.weblogs.com/0122027/>


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message