FAQ
I'm in the mood to question my sanity, so I'm seeking feedback for some
test mangling:

In t/spec/S03-operators/assign.t there are some tests that cause me a
headache. I'm trying to re-write them to not use the now-gone want()
function, but I'd have to understand them first ;-)

A good example is this one:

sub W () { substr(eval('want'), 0, 1) }
...

# line 560:
{
my @a;
my @z = (@a[0] = W, W);
#?rakudo 2 todo 'want function'
is(@a, 'L', 'lhs treats @a[0] as list');
is(@z[0], 'L', 'lhs treats @a[0] as list');
ok(!defined(@z[1]), 'lhs treats @a[0] as list');
}


This tests that
1) both calls to want() are in list context
2) @a[0] gets only the return value of the first call to W()
3) @z[0] gets the same thing
4) There's no item for @z[1]

Somehow I think this test (and many similar tests) are dead wrong.
Here's why:

Either it's parsed as '@a[0] = (W, W)' (list assignment), then @a should
get both elements, and so should @z.
Or it's parsed as '(@a[0] = W), W' (item assignment), then the first
call should be in item context, and the second one in list context, and
@z should still get both items.
Right? Or am I completely missing something important here?

My current plan is to write such tests as

sub l { 1, 2 } # return a short list
{
my @a;
my @z = (@a[0] = l(), l());
is @a[0].elems, 4, '@a[0] = treats LHS as list'
is @z.elems, 4, '... and passes all elements on the right';
}

Does this look sane, and match your understanding of assignment?

Cheers,
Moritz

Search Discussions

  • Larry Wall at Jul 28, 2009 at 8:09 pm
    On Tue, Jul 28, 2009 at 09:24:40PM +0200, Moritz Lenz wrote:
    : sub W () { substr(eval('want'), 0, 1) }
    : ...
    :
    : # line 560:
    : {
    : my @a;
    : my @z = (@a[0] = W, W);
    : #?rakudo 2 todo 'want function'
    : is(@a, 'L', 'lhs treats @a[0] as list');
    : is(@z[0], 'L', 'lhs treats @a[0] as list');
    : ok(!defined(@z[1]), 'lhs treats @a[0] as list');
    : }
    :
    :
    : This tests that
    : 1) both calls to want() are in list context
    : 2) @a[0] gets only the return value of the first call to W()
    : 3) @z[0] gets the same thing
    : 4) There's no item for @z[1]
    :
    : Somehow I think this test (and many similar tests) are dead wrong.
    : Here's why:
    :
    : Either it's parsed as '@a[0] = (W, W)' (list assignment), then @a should
    : get both elements, and so should @z.

    Not according to S03, at least by one reading. @a[0] as a scalar
    container only wants one item, so it only takes the first item off
    the list, and the list assignment produces a warning on the second
    because it's discarded. Since an assignment returns its left side,
    only one element is available to @z from @a[0].

    : Or it's parsed as '(@a[0] = W), W' (item assignment), then the first
    : call should be in item context, and the second one in list context, and
    : @z should still get both items.

    It's not parsed like that, because only $x, $$xref, $($xref) and such
    are allowed targets for item assignment. Basically, it has to start
    with a $ sigil and not do anything fancy like subscripting. However,
    if you say

    @z = $x = $a, $b;

    it parses as @z = ($x = $a), $b and $b ends up in @z[1] but not
    in $x, which only gets $a.

    : Right? Or am I completely missing something important here?

    To get all the elements into $x you'd have to say

    @z = $x = ($a, $b);

    and @z[0] would be ($a,$b) with some type or other, whatever was
    returned by the parens, quite possibly Parcel (that is, List of Thunk
    or some such).

    : My current plan is to write such tests as
    :
    : sub l { 1, 2 } # return a short list
    : {
    : my @a;
    : my @z = (@a[0] = l(), l());
    : is @a[0].elems, 4, '@a[0] = treats LHS as list'
    : is @z.elems, 4, '... and passes all elements on the right';
    : }
    :
    : Does this look sane, and match your understanding of assignment?

    Yes, but no. @z.elems and @a[0].elems should both be 1, I suspect.
    I wish we had a way of trapping and testing warnings too so we could
    see that 3 elements were discarded by the inner list assignment..

    Larry
  • Jon Lang at Jul 28, 2009 at 8:22 pm

    Larry Wall wrote:
    Moritz Lenz wrote:
    : Either it's parsed as '@a[0] = (W, W)' (list assignment), then @a should
    : get both elements, and so should @z.

    Not according to S03, at least by one reading.  @a[0] as a scalar
    container only wants one item, so it only takes the first item off
    the list, and the list assignment produces a warning on the second
    because it's discarded.  Since an assignment returns its left side,
    only one element is available to @z from @a[0].
    So, do we still have p5's 'want_array' (or whatever it was called)?
    That is, the predicate that tells the you whether the function is
    being called in item or list context? I know that the generalized
    'want' function proved to be unworkable; but I'd expect p6 to at least
    be able to do everything that p5 can do; and that includes functions
    that are aware of whether they're being used as singular or plural.

    --
    Jonathan "Dataweaver" Lang
  • Mark J. Reed at Jul 28, 2009 at 8:45 pm
    My understanding is that the P6 way to do that is to return a Capture
    containing the desired return values (which can lazily do things only
    when accessed) in the appropriate slots.
    On 7/28/09, Jon Lang wrote:
    Larry Wall wrote:
    Moritz Lenz wrote:
    : Either it's parsed as '@a[0] = (W, W)' (list assignment), then @a should
    : get both elements, and so should @z.

    Not according to S03, at least by one reading.  @a[0] as a scalar
    container only wants one item, so it only takes the first item off
    the list, and the list assignment produces a warning on the second
    because it's discarded.  Since an assignment returns its left side,
    only one element is available to @z from @a[0].
    So, do we still have p5's 'want_array' (or whatever it was called)?
    That is, the predicate that tells the you whether the function is
    being called in item or list context? I know that the generalized
    'want' function proved to be unworkable; but I'd expect p6 to at least
    be able to do everything that p5 can do; and that includes functions
    that are aware of whether they're being used as singular or plural.

    --
    Jonathan "Dataweaver" Lang
    --
    Sent from my mobile device

    Mark J. Reed <markjreed@gmail.com>
  • Damian Conway at Jul 28, 2009 at 9:12 pm

    Mark J. Reed wrote:

    My understanding is that the P6 way to do that is to return a Capture
    containing the desired return values (which can lazily do things only
    when accessed) in the appropriate slots.
    Return a Capture or a more heavily overloaded object, depending on how
    fine a degree of context discrimination you need.

    Further to that idea, I'm actually planning to attempt to port
    Contextual::Return to Perl 6 in the next few weeks.

    Damian
  • Jon Lang at Jul 28, 2009 at 10:13 pm

    Damian Conway wrote:
    Mark J. Reed wrote:
    My understanding is that the P6 way to do that is to return a Capture
    containing the desired return values (which can lazily do things only
    when accessed) in the appropriate slots.
    Return a Capture or a more heavily overloaded object, depending on how
    fine a degree of context discrimination you need.

    Further to that idea, I'm actually planning to attempt to port
    Contextual::Return to Perl 6 in the next few weeks.
    Right. I don't think that a Capture has sufficiently fine context
    discrimination to handle even those cases that Perl 5 was able to
    handle. For instance, consider a function that's intended to return a
    list of items in list context, or a count of the items in item
    context: since imposing item context on a Capture object extracts the
    first positional parameter, a Capture object wouldn't be able to be
    loaded properly to do this. So something along the lines of
    Contextual::Return is probably a good idea.

    Hmm... it might be nice to be able to use something akin to a "given
    ... when" construct:

    sub foo() {
    return context {
    when Item { 5 }
    when List { a, b, c, d, e }
    }
    }

    The idea here is that the 'context' keyword means that the block is to
    be called as soon as the desired context is known, with said context
    being passed to the block as its topic ('$_').

    --
    Jonathan "Dataweaver" Lang
  • Moritz Lenz at Jul 28, 2009 at 8:58 pm

    Jon Lang wrote:
    Larry Wall wrote:
    Moritz Lenz wrote:
    : Either it's parsed as '@a[0] = (W, W)' (list assignment), then @a should
    : get both elements, and so should @z.

    Not according to S03, at least by one reading. @a[0] as a scalar
    container only wants one item, so it only takes the first item off
    the list, and the list assignment produces a warning on the second
    because it's discarded. Since an assignment returns its left side,
    only one element is available to @z from @a[0].
    So, do we still have p5's 'want_array' (or whatever it was called)? No.
    That is, the predicate that tells the you whether the function is
    being called in item or list context? I know that the generalized
    'want' function proved to be unworkable; but I'd expect p6 to at least
    be able to do everything that p5 can do; and that includes functions
    that are aware of whether they're being used as singular or plural.
    The problem is that it's impossible in Perl 6, because we have multi
    dispatch.

    Consider this simple case:

    multi a(@a*) { } # provides list context onto the arguments
    multi a(Str $x) { } # provides Str context or at least some sort of
    # item context on the argument
    sub b() { }
    a(b()); # you can't know which context b() is called in

    since the multi dispatch to a() depends on the return value of b(), we
    only know b()'s context after we know its return value - which is too late.

    So the "active" context propagation that Perl 5 does is not possible in
    Perl 6.

    Cheers,
    Moritz
  • Larry Wall at Jul 28, 2009 at 10:09 pm

    On Tue, Jul 28, 2009 at 01:22:28PM -0700, Jon Lang wrote: : Larry Wall wrote:
    : > Moritz Lenz wrote:
    : > : Either it's parsed as '@a[0] = (W, W)' (list assignment), then @a should
    : > : get both elements, and so should @z.
    : >
    : > Not according to S03, at least by one reading.  @a[0] as a scalar
    : > container only wants one item, so it only takes the first item off
    : > the list, and the list assignment produces a warning on the second
    : > because it's discarded.  Since an assignment returns its left side,
    : > only one element is available to @z from @a[0].
    :
    : So, do we still have p5's 'want_array' (or whatever it was called)?
    : That is, the predicate that tells the you whether the function is
    : being called in item or list context? I know that the generalized
    : 'want' function proved to be unworkable; but I'd expect p6 to at least
    : be able to do everything that p5 can do; and that includes functions
    : that are aware of whether they're being used as singular or plural.

    Perl 6 is not about doing everything that Perl 5 can do. It's about
    breaking everything that needs breaking. This is one of those things.

    We *may* have something like want someday, but if so, it can only
    work by time travel (read: return a type profile with lazy values,
    and let the eventual binding of the type profile call back for the
    actual values). But I doubt will require such for 6.0.0 in any case.

    But Perl 6 is based on knowing the types to do MMD, and we have to know
    at least those types, even if we don't know the values. That makes
    it very difficult for a function that wants to return *different*
    types based on the eventual dispatch, since the eventual dispatch
    depends on those types in a circular sort of way. Sanity dictates
    that we make time flow forward rather than backward for most of what
    Perl 6 programmers will want to do most of the time.

    Larry
  • Moritz Lenz at Jul 28, 2009 at 8:42 pm
    Thanks for the quick reply.

    Larry Wall wrote:
    On Tue, Jul 28, 2009 at 09:24:40PM +0200, Moritz Lenz wrote:
    : sub W () { substr(eval('want'), 0, 1) }
    : ...
    :
    : # line 560:
    : {
    : my @a;
    : my @z = (@a[0] = W, W);
    : #?rakudo 2 todo 'want function'
    : is(@a, 'L', 'lhs treats @a[0] as list');
    : is(@z[0], 'L', 'lhs treats @a[0] as list');
    : ok(!defined(@z[1]), 'lhs treats @a[0] as list');
    : }
    :
    :
    : This tests that
    : 1) both calls to want() are in list context
    : 2) @a[0] gets only the return value of the first call to W()
    : 3) @z[0] gets the same thing
    : 4) There's no item for @z[1]
    :
    : Somehow I think this test (and many similar tests) are dead wrong.
    : Here's why:
    :
    : Either it's parsed as '@a[0] = (W, W)' (list assignment), then @a should
    : get both elements, and so should @z.

    Not according to S03, at least by one reading. @a[0] as a scalar
    container only wants one item, so it only takes the first item off
    the list, and the list assignment produces a warning on the second
    because it's discarded. Since an assignment returns its left side,
    only one element is available to @z from @a[0].
    So it's parsed like a list assignment, but list-assigning to a scalar
    container doesn't promote the whole RHS to a Capture (or whatever it
    ends up being) as it would do in the case of item assignment, but takes
    one item off. OK.

    I guess when I initialize @a[0] = [] it's the same, because then @a[0]
    is still a scalar, right?
    Only when I wrote @a[0] := [] I'd get all items in @a[0].
    : Or it's parsed as '(@a[0] = W), W' (item assignment), then the first
    : call should be in item context, and the second one in list context, and
    : @z should still get both items.

    It's not parsed like that, because only $x, $$xref, $($xref) and such
    are allowed targets for item assignment. Basically, it has to start
    with a $ sigil and not do anything fancy like subscripting. However,
    if you say

    @z = $x = $a, $b;

    it parses as @z = ($x = $a), $b and $b ends up in @z[1] but not
    in $x, which only gets $a.

    : Right? Or am I completely missing something important here?

    To get all the elements into $x you'd have to say

    @z = $x = ($a, $b);

    and @z[0] would be ($a,$b) with some type or other, whatever was
    returned by the parens, quite possibly Parcel (that is, List of Thunk
    or some such).
    Ok, that makes sense to me.
    : My current plan is to write such tests as
    :
    : sub l { 1, 2 } # return a short list
    : {
    : my @a;
    : my @z = (@a[0] = l(), l());
    : is @a[0].elems, 4, '@a[0] = treats LHS as list'
    : is @z.elems, 4, '... and passes all elements on the right';
    : }
    :
    : Does this look sane, and match your understanding of assignment?

    Yes, but no. @z.elems and @a[0].elems should both be 1, I suspect.
    I wish we had a way of trapping and testing warnings too so we could
    see that 3 elements were discarded by the inner list assignment..

    Larry
    Cheers,
    Moritz
  • Larry Wall at Jul 28, 2009 at 11:09 pm
    On Tue, Jul 28, 2009 at 10:42:01PM +0200, Moritz Lenz wrote:
    : I guess when I initialize @a[0] = [] it's the same, because then @a[0]
    : is still a scalar, right?

    No, as in Perl 5 [] still produces a scalar object that hides the arrayness
    from list context, so it's like:

    $b = [];
    @a[0] = $b;

    It doesn't unwrap the scalar object to see what's in it.

    : Only when I wrote @a[0] := [] I'd get all items in @a[0].

    That binds the Array in place of the current @a[0] container, just as

    $b = [];
    @a[0] := $b;

    would.

    Just as $obj and @$obj default differently in list context, so do
    [] and (). To put it another way, Array and Hash objects don't
    naturally interpolate into list context without some help from @ or %
    syntax (or equivalent method call). If I say:

    @a = 42, [1,2,3], { foo => 1, bar => 2, baz => 3 };

    then @a will always end up with exactly 3 elements, just as if I'd
    said:


    $a = 42;
    $b = [1,2,3];
    $c = { foo => 1, bar => 2, baz => 3 };
    @a = $a, $b, $c;

    Larry

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupperl6-language @
categoriesperl
postedJul 28, '09 at 7:24p
activeJul 28, '09 at 11:09p
posts10
users5
websiteperl6.org

People

Translate

site design / logo © 2021 Grokbase