While browsing the updated synopses, I noticed a problem with how
scalar container methods are called. Currently, both value methods and
container methods are shown in the synopses as being called with
C<$foo.bar()>.
For several reasons, that doesn't work for me. The method conflict
between container methods and value methods should be obvious. What
should ((1|2)|(3&4)).values return?
The answer is simple enough: for scalar container method calls, use
$foo.\bar(), which would be syntactic sugar for (\$foo).bar, and would
be the Perl6 equivalent to the Perl5 idiom tied($foo)->bar()
That way, we get:
((1|2)|(3&4)).values ~~ (1|3,2|4) # (1,2)|(3,4) I presume
((1|2)|(3&4)).\values ~~ (1|2, 3&4)
@foo.\elems would work the same as @foo.elems, since @foo in scalar
context *is* the container object in the first place.
Comments?
Ashley Winters
Well, there is an answer, and it's one I've never been extremely happy
with. That is, Junctions delegate everything to their junctands
*except* a small set of methods. .values is one of those. So:
((1|2)|(3&4)).values returns (1|2), (3&4)
And you can call values on each of those to get the others out.
In my proposal "The Object Sigil", I attempted to get around this
problem for both Junctions and iterators, using a syntactic convention.
I later realized that you could do this with just one method.
Presumably this method would be called the same thing for everything,
let's say .val.
Then:
((1|2)|(3&4)).values returns
((1|2).values | (3&4).values) which returns
(1.values | 2.values) | (3.values & 4.values) which is an error
So instead you'd use:
((1|2)|(3&4)).val.values
You can see why I wanted syntactic support.
But this convention provides much more accuracy than memorizing a list
of methods that don't automatically thread, or memorizing a list of
iterator methods that act on the iterator and not its current value.
That is:
for @foo -> $i {
$i.next; # call .next on the current value in @foo
$i.val.next; # call .next on the iterator
# let's say @foo is a list of iterators!
$i.next; # call .next on the value pointed to by the iterator in @foo
$i.val.next; # call .next on the iterator $i
$i.val.deref.val.next; # call .next on the iterator in @foo
}
Yeah, the last one is a mouthfull, but let's see you do that *at all*
with the other semantics.
Anyway, there's something to think about. Moving on...
> The answer is simple enough: for scalar container method calls, use
> $foo.\bar(), which would be syntactic sugar for (\$foo).bar, and would
> be the Perl6 equivalent to the Perl5 idiom tied($foo)->bar()
Er, hmm. See, that doesn't solve your junction problem. Junction is a
value, and it's different from the scalar container it goes in.
Differentiating method calls on arrays and hashes and on their values is
easy:
@array.sort;
@array[0].sort;
It's scalars which are the problem. /A1?2/ said that you actually do
use:
tied($foo).bar;
So, well, there you go.
I don't think $foo.\bar() is really going to work. It doesn't fit
together logically. Adding a \ after the dot seems to be saying
something about the method, when you're actually saying something about
the invocant. Plus, it means that you can do:
$foo.bar.\baz();
And what does that mean? That thing doesn't even have a container yet.
It'd be nice to have some nicer way than tied($foo).bar, though. I
think we should see how often it comes up in the real world (or the
theoretical world) before we jump to adding a new operator on it.
Luke
Except that you don't actually have to memorize a list. Methods thread
on their invocant only if their invocant isn't a Junction. Its
mnemonic value is no better or worse than any other MMD distinction.
: That is:
:
: for @foo -> $i {
: $i.next; # call .next on the current value in @foo
: $i.val.next; # call .next on the iterator
:
: # let's say @foo is a list of iterators!
: $i.next; # call .next on the value pointed to by the iterator in @foo
: $i.val.next; # call .next on the iterator $i
: $i.val.deref.val.next; # call .next on the iterator in @foo
: }
:
: Yeah, the last one is a mouthfull, but let's see you do that *at all*
: with the other semantics.
We can't have semantics where an object keeps track of what container
it's in. What if it's in more than one container?
The only reason a tied()-like operator works is that it's not really
a run-time operator at all.
: Anyway, there's something to think about. Moving on...
:
: > The answer is simple enough: for scalar container method calls, use
: > $foo.\bar(), which would be syntactic sugar for (\$foo).bar, and would
: > be the Perl6 equivalent to the Perl5 idiom tied($foo)->bar()
:
: Er, hmm. See, that doesn't solve your junction problem. Junction is a
: value, and it's different from the scalar container it goes in.
:
: Differentiating method calls on arrays and hashes and on their values is
: easy:
:
: @array.sort;
: @array[0].sort;
:
: It's scalars which are the problem. /A1?2/ said that you actually do
: use:
:
: tied($foo).bar;
:
: So, well, there you go.
Except that S12 currently has that as var() instead of tied().
: It'd be nice to have some nicer way than tied($foo).bar, though. I
: think we should see how often it comes up in the real world (or the
: theoretical world) before we jump to adding a new operator on it.
Agreed.
Larry
Is this behavior exclusive to methods? Or does something like this:
3.14159 + "1"|2;
try to MMD-dispatch to:
multi sub *infix:<+> (Num $foo, Str|Int $bar)
instead of (or before) threading?
Ashley Winters
Well, except that you do have to memorize a list. But, yeah, you
basically do have to memorize a list of all MMD functions that take
junctions... so, I guess, sortof....
> : That is:
> :
> : for @foo -> $i {
> : $i.next; # call .next on the current value in @foo
> : $i.val.next; # call .next on the iterator
> :
> : # let's say @foo is a list of iterators!
> : $i.next; # call .next on the value pointed to by the iterator in @foo
> : $i.val.next; # call .next on the iterator $i
> : $i.val.deref.val.next; # call .next on the iterator in @foo
> : }
> :
> : Yeah, the last one is a mouthfull, but let's see you do that *at all*
> : with the other semantics.
>
> We can't have semantics where an object keeps track of what container
> it's in. What if it's in more than one container?
That's not what I'm talking about. I'm just saying that .val is the one
method that doesn't delegate when you're an iterator:
$i ==> the iterator
$i.val ==> the iterator without delegation ability
$i.val.deref ==> what the iterator points to without the .val method
It's just a way of making things concrete in delegates so that they
delegate in all but one simple case (for when you need to talk to the
delegate itself).
What I'm really worried about is:
my $meth = get_method(); # say it returns "values"
my $rets = (1|2).::($meth);
And instead of getting a junction in $rets like you would for *any other
method*, you get a list. It's a spooky, inconsistent,
hard-to-work-around special case.
And come to think of it, .val doesn't really solve that either.
You can see why I wanted syntactic support :-)
I don't really care how ugly it is though, I just want it. If we can
make it into a method or routine or something, that's fine, as long as
we can get it consistent. No, not consistent, *predictable*.
Note that with delegate-style iterator semantics, it is *not possible*
to manipulate an iterator through another iterator. There's some famous
Perl quote about things being possible...
> Except that S12 currently has that as var() instead of tied().
Cool.
That would be an "instead of", I suspect.
Larry