> On Jan 19, 2021, at 12:18 PM, Brian Duggan <bdug...@matatu.org> wrote:
>
> Hi Folks,
>
> I ran into this situation today, which seems counterintuitive:
>
> my @one = 1,2,3;
> my @two = 4,5,6;
> my @both = @one,@two;
> my @first = @both[0];
> say @one.raku;
> say @first.raku;
>
> output:
>
> [1, 2, 3]
> [[1, 2, 3],]
>
> I was expecting @first and @one to be the same.
I agree that @first not equalling @one is a bit counterintuitive, since the
formation of @both as a two-element array, and the extraction of its first
element, seems to be exact opposite operations.
In fact, let’s make it even more exactly opposite:
my @one = 1,2,3;
my @both;
@both[0] = @one; # Stuff it into first element
my @first = @both[0]; # Yank it out of first element
say @one.raku;
say @first.raku;
This produces the same output as your original code, even though the two
commented lines appear to cancel (like in algebra) to produce an identical
result.
But wait! This version makes this issue clearer (at least to my Raku-accustomed
eyes).
The first commented line is a version of this:
$scalar_var = @some_array;
, in which $scalar_var _captures_ @some_array during the assignment. (Similar
to “references" in Perl: `$z = \@y;`)
To exactly invert, we need to also counteract the capturing in the second
commented line.
(continued below)
> I discovered that I could instead write either of these --
>
> my (@first) = @both[0];
> my @first := @both[0];
>
> or I could change the @both assignment to be
>
> my @both := @one, @two;
To discuss those three alternatives, let’s extend your original code:
my @one = 1,2,3;
my @two = 4,5,6;
my @both = @one,@two;
my @first = @both[0];
say @one.raku;
say @first.raku;
my (@a) = @both[0];
my @b := @both[0];
my @both_bound := @one,@two;
my @c := @both_bound[0];
say .raku for @a, @b, @c; # They all look right, but only because
nothing wrote to them.
@a[1] = 7; say @one; # [1 2 3] , independent as expected
@b[1] = 8; say @one; # [1 8 3] , bound/aliased!
@c[1] = 9; say @one; # [1 9 3] , bound/aliased!
The first form, `my (@array) = …`, I read as quite clever (as in “I didn’t
think of it myself”). it does what I think you intended.
Our community is still in the idiom-forming phase, but this would not be a
preferred form for me.
I think that is (mostly?) due to it being a LHS-manipulation, so it cannot
easily work as part of a larger expression without some temp array on the
left-hand side.
[
I admit that part of my dislike is because it looks too much like this
from Perl:
my ($only_want_the_first_element) = @some_array;
, and having a @-sigiled variable inside that expression looks like a a
redundant use of parens.
Please give zero weight to that biased part of my assessment.
]
The second and third forms both use “binding”, which would be fine if “live
links” between @one and @first was your need and intention, but would be a
source of bugs otherwise.
Your original code looks like you want @first to contain independent copies of
the @one elements.
If I were refactoring your code, I would not assume that changing assignment to
binding would be OK to do.
(I suspect that you already knew that, and were just showing us all of the
variants that “worked”)
> ..but I wonder if there's an idiomatic approach -- or
> way of thinking about this -- that makes this flow more
> intuitive.
(continued from above)
I would probably write it as:
my @first = @both[0].list;
, but for this exact case I might instead say:
my @first = @( @both[0] );
. They do the same thing here, and both look idiomatic to me,
but .list looks like an action/command to @both[0],
and @(...) is the List Contextualizer,
so which one I would use would depend on the surrounding code,
and who I expected to be reading the code.
https://docs.raku.org/type/Any#index-entry-@_list_contextualizer
So, I recommend .list or @(…) as more intuitive, maybe even *sufficiently*
intuitive :^)
> thanks
> Brian
—
Hope this helps,
Bruce Gray (Util of PerlMonks)