commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Phil Steitz" <p...@steitz.com>
Subject Re: [math][functor] More Design Concerns
Date Fri, 27 Jun 2003 17:35:32 GMT

> 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.

I agree.  The question is when to bring this kind of machinery in (see 
comments below).

> 
> 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).

Can you expand a little on exactly what you mean here, ideally with some 
specific examples?  Don't limit yourself to what is already implemented 
in commons-math.

> 
> 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. 

Can you provide some examples on the ease of use point here?  You don't 
have to limit them to what exists now in commons-math.  As a 
mathematician, I might personally be very happy (sometimes) working with 
function spaces definable by the operations that they support (e.g L-P 
spaces, groups, rings, fields, etc) but I don't think that 
non-mathematicians -- or even mathematicians who are lazy programmers -- 
would like to work in that world.  A concrete example is the "Operable 
objects" defined in Jade and their use in Jade's matrix class 
(http://www.dautelle.com/jade/api/com/dautelle/math/Matrix.html).  As a 
user, I would much prefer to work directly with a real matrix (see 
additional notes below on the special position of reals).  Part of what 
I am resisting here is the lure of "mathematical domain modelling" -- 
even from a functional standpoint -- as a primary focus.  I would prefer 
to keep the focus on the practical applications and bring in the 
abstractions only as we need them.  I think that it is likely that I am 
missing your main point, so please (gently) enlighten me.

  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.

I agree.

> 
> 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.

This is certainly part of my problem.  I could get over both of this, 
however, if I saw big value in having UnivariateRealFunction extend 
Functor.  Here is more precisely what I am struggling with in the 
specific case of real-valued functions. When I said that "a real-valued 
(differentiable) function is an object in its own right" what I really 
meant was that the fact that its characterization as a (partially) 
composable mapping does not capture very much -- the most important 
functional characteristics of a real -> real function make essential use 
of the fact that its domain and codomain are the real numbers. Things 
like convolution, integration, differentiation, rootfinding, etc. make 
no sense at the Functor level. To me, having UnivariateRealFunction 
extend Functor would be like having Complex extend Group. Sure, you 
could do it and it makes sense logically, but why introduce the 
additional layer unless there is a strong practical reason (or 
anticipated reason) to do so?  Also, just as there are additional 
natural layers between Complex and Group (Field, Ring) there are also 
probably additional natural layers between Functor and 
UnivariateRealFuntion (see more below). I know this is mixing OO and 
functional ideas, but hopefully you get the point.

Note that this does NOT rule out the applicability of the functor 
approach for other things down the road.

> 
> 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.

Yes. but maybe not a serious concern for some use cases.  This is an 
interesting idea. I would like to think/discuss some more about the 
potential uses of this.

> 
> 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.
> 

Also a very interesting idea.  My intuition is that what will turn out 
to be useful are not "primitive" versions, but "algebraic" or 
"mathematical" versions (i.e., versions that use primitives or other 
mathematical objects as domain, codomain to represent functors with 
additional algebraic properties).

> 
> 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.

Please point out any additional opportunities/inconsistencies that you 
can see now or that you see as more gets added to the package. Thanks in 
advance.

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

For now, I agree.  I am intrigued, however, by the idea of "mathematical 
functors", mostly for use in more strictly mathematical applications 
than what commons-math is at least initially aiming at.  To me, the real 
distinction is between reals, complex numbers (not yet in the package), 
matrices, etc and "domain elements", not primitives vs objects.

> 
> * 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).

I agree.
> 
> * 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.

I agree.  Thanks for the feedback.  Pls help make sure that we don't do 
anything that will make this harder down the road.

Phil

> 
> - 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
> 




---------------------------------------------------------------------
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