On Sun, Feb 24, 2008 at 3:00 PM, Aristotle Pagaltzis <[EMAIL PROTECTED]> wrote:
>  Something like
>     path { $app_base_dir / $conf_dir / $foo_cfg . $cfg_ext }
>  where the operators in that scope are overloaded irrespective of
>  the types of the variables (be they plain scalar strings,
>  instances of a certain class, or whatever).

This is excellent.  I've long supported the one symbol-one meaning
idea.  There are some trade-offs, though, which I'll describe.  I'll
use Haskell as my object language, since it does this sort of operator
defining rather than operator overloading (only for infix binary
operators though, not full syntactic constructs).

Recently I implemented a Vector class in Haskell.  Vectors have
addition and multiplication, of a sort, so it makes sense to use the +
and * operators.   In Haskell overloading comes in type classes, where
the operators you overload need to have specific types and you also
have an implicit contract to obey some laws about them when you
overload.  In the context of Perl, types don't mean squat, but the
implicit laws are still important, so this argument still applies.
The Num class for these operators looks like this:

  class Num a where
    (+) :: a -> a -> a   -- take 2 arguments of type a and return 1 of type a
    (*) :: a -> a -> a

Which means that if I wanted to overload (+) to work on vectors, my
(+) would have to have type Vector -> Vector -> Vector.  That's fine,
it is that type.  And it's associative and commutative as the class

The trouble is with (*).  There are three types of "multiplication" on
vectors, with these types:

    Double -> Vector -> Vector
    Vector -> Double -> Vector
    Vector -> Vector -> Double  -- inner product

None of which is Vector -> Vector -> Vector as Num is expecting.

Now, not letting me overload (*) was a good idea on Num's part.
Normally if you have some v in Num, you can say (v*v) + v and it will
be legal.  But if v were a vector, then this wouldn't work, you'd end
up trying to add a scalar and a vector.  It's very simple, Vectors are
not Nums in the sense that the class requires

The way I solved this was to create my own class with special vector
operations, so the user of the module had to differentiate between
vector operations and scalar operations.

  class Vector v where
    (^+^) :: v -> v -> v
    (*^)  :: Double -> v -> v
    (^*^) :: v -> v -> Double

  x ^* y  = y *^ x
  x ^-^ y = x ^+^ (-1) *^ y

Which could be construed as annoying.

This problem could have been ameliorated in another way, namely to
have designed Num differently.  If Num separated the notions of
addition and multiplication, or even had been designed as a Vector
space in the first place, then I could have used + and * like I wanted
to.  But it wasn't, so I couldn't.  More below.

>  I hope it's obvious how such a thing would me implemented. Now,
>  if you used type-bound overloading, then the following two
>  expressions cannot yield the same result:
>     ( 2 / 3 ) * $x
>     2 * $x / 3
>  But if overloading was scope-bound, they would!

And here is why you need ad-hoc overloading in addition to scope-based
overloading.  There are times when I mean 2/3 < 4 to mean the symbolic
data structure representing that condition, and there are other times
when I just want to check whether 2/3 is less than 4!  Here's a
contrived example:

  my $expr = 1;
  my $count = 1;
  while ($count < 10) {
    print "$count> $expr\n";
    $expr = $expr + $expr;
    $count = $count + 1;

With the intention of printing something like:

  1> 1
  2> 1 + 1
  3> (1 + 1) + (1 + 1)

Which is, of course, broken.  I would have to do something crazy with scopes:

  while ($count < 10) {
    print "$count> $expr\n";
    $expr = do { use Math::Symbolic; $expr + $expr };
    $count = $count + 1;

Which could also be construed as annoying.

However, here $count + 1 and $expr + $expr are the same +.  They both
mean add.  Why should I have to play with scopes for this.  Contrast
this with Java, where 3 + 4 and "hello " + "world" are different +s
(unless you're used to thinking about monoids).  Getting those two to
coexist is the thing that should require playing with scopes (or
better yet, using diffrent symbols for the two of them).

I do think the best solution is a combination of overloading and
scoping.  That is, allow operator overloading, but not overloading the
name "+", rather overloading a specific +, such as &Math::infix:<+>.

This still allows poor usage as we've commonly seen with operator
overloading.  But it also allows well-behaved usage, which was
previously forbidden.


Reply via email to