> -----Original Message-----
> From: Larry Wall [mailto:[EMAIL PROTECTED]
> On Thu, Mar 11, 2004 at 06:49:44AM -0800, Gregor N. Purdy wrote:
> : So, will "mutatingness" be a context we'll be able to inquire on
> : in the implementation of a called routine?
>
> Probably not, but it's vaguely possible you could somehow get a
> reference to what is being assigned to, if available, and check to see
> if $dest =:= $src (where =:= tests to see if two refs point to the
> same object).  But in general I think most "want" testing is just a
> way of making code run slow, because it forces tests to be done at run
> time that should be done at compile time or dispatch time.  It's better
> for the optimizer if you can give it enough type hints and signature
> hints to decide things earlier than the body of the sub or method.
>
> : Or, could we provide a specialized distinct implementation
> : for mutating that would get called if .=X() is used?
>
> That is much more likely.  In general if you don't define both an <op>
> and an <op>= then Perl can autogenerate or emulate the missing
> one for you.
>
> Now in the specific case of . and .= we don't exactly have a normal
> binary operator, because the right side is not an expression.

  $tis.=««sad pity pity sad sad pity true»;

  $s .= ($useMbcs ? wlength : length);

(Side note: although that expression isn't valid, since the wlength
and length methods aren't qualified, it *should* be, since a human could
infer it rather easily. Can we make that DWIM? (One way would be
for the parser to convert that into if-else form if it appeared
ambiguous.))

> So we may have to provide a way of marking a normal method as a
> mutator. Possibly we end up with
>
>     method =sort (Array @ary) returns Array {...}  # inplace
>     method sort (Array @ary) returns Array {...}   # cloning
>
> That works nicely with the .= vs . distinction, visually speaking.

Why not just put a property on the calling context, and allow either:

    # Run-time handling
    method sort(Array @a) { if ($CALLER.mutating) {...} ...}
or
    # Properties should be after names
    method sort:mutating(Array @a) {...}
or
    # But this is consistent with operators
    method mutating:sort(Array @a) {...}

>
> On the other hand, you might want to do the same with multi subs:
>
>     multi sub =sort (Array @ary) returns Array {...}  # inplace
>     multi sub sort (Array @ary) returns Array {...}   # cloning
>
> and then it gets a little more problematic syntactically because
> multis are called like subroutines:
>
>     =sort(@array);
>
> We would have to allow an initial = at the beginning of a term.  So far
> I've resisted doing that because I don't want
>
>     @obj.meth=foo();
>
> to become ambiguous, in case I decide to make the parentheses optional
> on method calls with arguments.  If I did decide that, and we have
> terms beginning with =, it would not be clear whether the above meant
>
>     @obj.meth(=foo());
>
> or
>
>     @obj.meth=(foo());

Or @obj.meth = foo();

(As much as I despise those who don't use spaces around the assignment
operator, I'm willing to defend their "right" to the practice...)

> The = prefix notation also doesn't work very well for talking about the
> name of a routine:
>
>     &=sort
>
> That looks an awful lot like a junctive assignment operator...

But it would be obvious from context that it was/n't:

   $foo = &=sort;
   bar(&=sort);
   $baz &=sort;

> From a C++-ish perspective, the right thing to do is to differentiate
> not by the name but by the declared mutability of the invocant:
>
>     multi sub sort (Array @ary is rw) returns Array {...}  # inplace
>     multi sub sort (Array @ary)       returns Array {...}  # cloning
>
> Or I suppose a case could be made for something that specifically
> declares you're returning one of the arguments:
>
>     multi sub sort (Array @ary is rw) returns @ary {...}  # inplace
>
> After all, it's possible to write a method that mutates its invocant
> but *doesn't* return it like a well-behaved mutator should.  You don't
> always call a mutator in a void context--sometimes you want
> to be able to stack mutators:
>
>     @array.=sort.=uniq;
>
> So you have to be able to return the mutant as well as mutate it in place.

In the case of mutators, won't the return always be the first argument?

So couldn't we just say:

    multi sub sort(Array @ary is rw is mutated) returns Array {...}
    multi sub sort(Array @ary)                  returns Array {...}

(and can't we infer the "returns Array" when "is mutated" is provided?)

> On the other hand, I'm deeply suspicious of a return signature that
> mentions a specific variable.  What if the body says to return something
> else?  Is that just ignored?  Do we check it to see if it's the same
> item?

No. You might well say:

   $string.=length;

And convert from one subtype to another. I think the mutation indicator
is a hint to the optimizer, and a crutch for the implementor, in cases
where it's possible to squeeze more performance out of skipping the
assignment phase. (In particular, where an inefficient assignment operator
exists.)

Question: Can all this noise be eliminated by paying more attention to
the construction of the assignment operator?

That is, do we have an example where $a .= meth is going to perform poorly,
and that performance is NOT because of the $a = $a.meth assignment? (And
that cannot be fixed by declaring the invocant 'is rw'.)

> So my guess is that it's probably better to have something more specific
> for the mutator "template".  I think, actually, that I've convinced myself
> that a mutator should be marked in its name, and that it should generally
> be defined as a standard method rather than a multi sub:
>
>     method =sort (Array @ary is rw) {...}  # inplace
>
> This would automatically arrange to return the invocant.
> It would be illegal to use C<return> in such a routine.

So what? C<goto END;> ?

This isn't making it onto the "good ideas list"...

> And I guess, since it's an ordinary method, we can leave out the invocant:
>
>     method =sort () {...}  # inplace
>
> with the assumption that the default invocant on a mutator would
> automatically be assumed "rw".
>
> If you do happen to want to define a multi sub mutator, then the
> syntax for calling it could be
>
>     &«=sort»(@array)
>

What about defining a multimethod mutator:

  multi =foo(Array @a, Object $b) {...}
  multi =foo(Array @a, Scalar $s) {...}

I want to be able to say

  @a.=foo($x);

and have it work, right?

> However, we really don't have to special case the = prefix syntax if
> we make it something like:
>
>     method    postfix:.=sort () {...}  # inplace
>     multi sub postfix:.=sort () {...}  # inplace
>
> That's getting way up there on the ugliness factor.  Might be worth
> a new operator category:
>
>     method    mutate:sort () {...}  # inplace
>     multi sub mutate:sort () {...}  # inplace
>
> or
>
>     method    inplace:sort () {...}  # inplace
>     multi sub inplace:sort () {...}  # inplace
>
> or
>
>     method    rw:sort () {...}  # inplace
>     multi sub rw:sort () {...}  # inplace
>
> or
>
>     method    self:sort () {...}  # inplace
>     multi sub self:sort () {...}  # inplace
>
> On the final hand, if people fall in love with both self:sort and
> =sort, we
> could have =sort be a shorthand for self:sort where it's unambiguous.
>
> On the (n+1)st hand, that says we could write it either as
>
>     @array.=sort.=uniq
>
> or
>
>     @array.self:sort.self:uniq
>
> Perhaps that's okay under TMTOWTDI.  I actually find the shorter one
> more readable.  But then calling it as a sub would always just be
>
>     self:sort(@array);
>
> And then,
>
>     .self:sort
>
> might or might not be preferred over
>
>     .=sort

I'd still like to put a space between = and sort.

  @a .= sort

may *imply* using inplace:sort instead of just sort, but I like that
space for ergonomic reasons.


> : If we are performing some operation on
> : large data, and we know the end result is going to clobber the
> : current object, we could avoid making an extra copy.
>
> Yes, computer performance is desirable. but I think the biggest goal
> of the mutating operators is mental performance.  The fact is that
>
>     $a += 1;
>
> is much easier to understand than
>
>     $a = $a + 1;

Even you like the space. Vive le space!

=Austin

Reply via email to