Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

:= inconsistent semantics #5532

Open
p6rt opened this issue Aug 4, 2016 · 14 comments
Open

:= inconsistent semantics #5532

p6rt opened this issue Aug 4, 2016 · 14 comments
Labels
Bug

Comments

@p6rt
Copy link

@p6rt p6rt commented Aug 4, 2016

Migrated from rt.perl.org#128842 (status was 'open')

Searchable as RT128842$

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From zefram@fysh.org

sub ss0(Scalar $s, $nv) { my $v := $s; $v = $nv }
sub ss0 (Scalar $s, $nv) { #`(Sub|76872624) ... }
my $a = 3
3
ss0($a.VAR, 5)
5
$a
5
sub ss1($s, $nv) { my $v := $s; $v = $nv }
sub ss1 ($s, $nv) { #`(Sub|76873232) ... }
ss1($a.VAR, 7)
Cannot assign to a readonly variable or a value
$a
5

In the above, the := binding operator exhibits two very different
behaviours. In ss0() its effect is to set the container of its lhs to
be the value of its rhs. This is used to modify the value stored in
the container that was passed in as a parameter. In ss1() the same code
doesn't work. Based on behaviour I've seen from the binding operator in
other contexts, I think here its effect is to set the container of its
lhs to be the *container* of its rhs. The documentation doesn't admit to
this double meaning of the binding operator. doc/Language/operators.pod
describes only the second behaviour.

I would expect := to have consistent semantics, unaffected by parameter
type constraints. I'm not sure which semantic is intended. The first
is very useful, and needs to be available somehow. I have not found any
other way of accessing the content of a Scalar container from a reference
to the container, but maybe I missed something. The second semantic can
always be achieved in terms of the first, by adding an explicit ".VAR"
to the rhs. I have only ever wanted the first semantic, but I take no
position on Huffman coding.

-zefram

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From @timo

The := operator only has one behaviour/semantics in this example.
However, in one of your examples you're actually getting a Scalar inside
a Scalar. Check this out​:

timo@​schmand ~> perl6 -e 'my $a = 3; sub ss1(Scalar $s, $nv) { say
$s.VAR.DUMP }; ss1($a.VAR, 7)'
3
timo@​schmand ~> perl6 -e 'my $a = 3; sub ss1($s, $nv) { say $s.VAR.DUMP
}; ss1($a.VAR, 7)'
3

Does that help clear things up a little?

I'm not sure what mechanism causes the example where Scalar is supplied
in the signature to not re-containerize, though.

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

The RT System itself - Status changed from 'new' to 'open'

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From zefram@fysh.org

Timo Paulssen via RT wrote​:

However, in one of your examples you're actually getting a Scalar inside
a Scalar. Check this out​:

Huh, there is indeed a difference there that I wasn't aware of. Thanks.
Can also be seen by​:

sub tt0(Scalar $s) { say $s.WHAT; }
sub tt0 (Scalar $s) { #`(Sub|69930648) ... }
sub tt1($s) { say $s.WHAT; }
sub tt1 ($s) { #`(Sub|69930800) ... }
my $a = 3
3
tt0($a.VAR)
(Int)
tt1($a.VAR)
(Scalar)

In this respect, tt1() is behaving the way I expect, and tt0() looks like
a bug. The sub is receiving, as the apparent value of the parameter,
the value contained in what the caller thought it was sending as the
parameter value. So what I'd perceived as useful behaviour of := seems
to be a bug in parameter handling.

This issue has some slight resemblance to [perl #​128409] and [perl
#​128407], but probably isn't the same bug as either.

Is there some way I missed to get access to the content of a Scalar?
Something other than exploiting this bug? That's what I was originally
after when I ran into this.

-zefram

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From @timo

On 04/08/16 20​:09, Zefram wrote​:> Is there some way I missed to get
access to the content of a Scalar? > Something other than exploiting
this bug? That's what I was > originally after when I ran into this. >

-zefram

Aye, you can .<> the value to "decont" it, which will give you the
content of the container. If there's another container in it (like with
the thing that dumps as 3), you'll want to .<>.<> to get the value itself.

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From zefram@fysh.org

Timo Paulssen via RT wrote​:

Aye, you can .<> the value to "decont" it,

Doesn't work as an lvalue​:

my $a = 3
3
my $b = $a.VAR
3
$b.WHAT.say
(Scalar)
$b.<>.WHAT.say
(Int)
$b.<> = 5
Cannot modify an immutable Int
  in block <unit> at <unknown file> line 1

Has some value for reading, at least.

-zefram

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From @timo

On 08/04/2016 08​:31 PM, Zefram wrote​:

Timo Paulssen via RT wrote​:

Aye, you can .<> the value to "decont" it,
Doesn't work as an lvalue​:

That should be very obvious; I may have communicated
less-than-effectively. You can only assign to a Scalar. You just used
.<> to get rid of the Scalar and get the Value itself, which then can't
be assigned to (because it's no longer tied to a storage location that
could be changed).

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From zefram@fysh.org

Timo Paulssen via RT wrote​:

You can only assign to a Scalar.

How? Specifically, in the situation where I have a Scalar as a value,
stored in a variable or as a sub parameter. How do I assign into it?
The usual assignment operator, if applied to the variable or parameter,
assigns to that variable (or fails to assign to the parameter).
It shouldn't do otherwise, of course. Some other operator is required
to store into a Scalar.

-zefram

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From @timo

On 08/04/2016 08​:47 PM, Zefram wrote​:

Timo Paulssen via RT wrote​:

You can only assign to a Scalar.
How? Specifically, in the situation where I have a Scalar as a value,
stored in a variable or as a sub parameter. How do I assign into it?
The usual assignment operator, if applied to the variable or parameter,
assigns to that variable (or fails to assign to the parameter).
It shouldn't do otherwise, of course. Some other operator is required
to store into a Scalar.

-zefram

Well, assignment is implemented by calling .STORE on the scalar. Don't
forget that unless you "is rw" or "is raw" a parameter, you'll get a new
scalar that has the same value, but won't allow you to write to it.
(using a sigil-less parameter is equivalent to "is raw").

It was not correct to say "only assign to scalars", as there's also
LexicalRef, NativeRef, Proxy, ...

The assignment operator is the operator to store into a Scalar.

I'm getting the feeling we're not getting through to each other :)

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From zefram@fysh.org

Timo Paulssen via RT wrote​:

Well, assignment is implemented by calling .STORE on the scalar.

That's not usable in the way I'd expect​:

my $a = 3
3
my $b = $a.VAR
3
$b.STORE(5)
Cannot modify an immutable Int
  in block <unit> at <unknown file> line 1

The assignment operator is the operator to store into a Scalar.

The assignment operator requires an lvalue on its lhs. Where I have a
Scalar as an rvalue, I don't know how to make an lvalue from it.

I'm getting the feeling we're not getting through to each other :)

Yeah. Let me try to make it clearer. In the above situation, with
a reference to $a's Scalar container in $b, I'd like to achieve what
the assignment "$a = 5" would, but by an operation using $b and not
directly mentioning $a. The result should be that $b is unchanged
(still referencing $a's container) and $a subsequently has the value 5.
Equivalent Perl 5​:

  $ perl -lwe '$a = 3; $b = \$a; $$b = 5; print $a'
  5

"$b = 5", unsurprisingly, doesn't achieve it​: that leaves $a untouched,
and changes $b so that it no longer references $a's container. Using the
code from my initial message on this ticket, "ss0($b, 5)" achieves what
I want, but apparently that's only due to the parameter handling bug
that you identified. I'd like a way that doesn't depend on exploiting
a bug. The answer could well look somewhat like either "$b.STORE(5)"
(a method on the Scalar object) or "$b.<> = 5" (making an lvalue from
the Scalar object), though as we've seen neither of these is correct.

-zefram

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From @pmichaud

On Thu, Aug 04, 2016 at 09​:36​:18PM +0100, Zefram wrote​:

Yeah. Let me try to make it clearer. In the above situation, with
a reference to $a's Scalar container in $b, I'd like to achieve what
the assignment "$a = 5" would, but by an operation using $b and not
directly mentioning $a. The result should be that $b is unchanged
(still referencing $a's container) and $a subsequently has the value 5.

So are you looking for...?

  my $a = 3;
  my $b := $a; # $b is bound to $a

  $b = 5; # Assign 5 to $b (which is also $a)
  say $a; # "5"

Pm

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From zefram@fysh.org

Patrick R. Michaud wrote​:

So are you looking for...?

No. I want a writable reference that I can pass around as a value,
store in a data structure, and so on. The Scalar object obtained by
"$a.VAR" is clearly the thing to pass around as a value; I'm looking
for the way to write through it.

-zefram

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From @pmichaud

On Thu, Aug 04, 2016 at 09​:54​:54PM +0100, Zefram wrote​:

Patrick R. Michaud wrote​:

So are you looking for...?

No. I want a writable reference that I can pass around as a value,
store in a data structure, and so on. The Scalar object obtained by
"$a.VAR" is clearly the thing to pass around as a value; I'm looking
for the way to write through it.

"writable reference" --> "Scalar"

"store in a data structure" --> "bind using :="

I think that using := is the canonical way to do these sorts of operations​:

my $a = 123;
123
my $b := $a; $b = 5; say $a;
5
my @​c; @​c[2] := $a; $a = 5; say @​c[2];
5
my %d; %d<abc> := $a; $a = 'abc'; say %d<abc>;
abc
say "$a $b @​c[2] %d<abc>";
abc abc abc abc
$a = 4; say "$a $b @​c[2] %d<abc>"
4 4 4 4

More generally -- when I last checked Perl 6 doesn't really have "references" in the same style as Perl 5 -- i.e., objects that can be passed around and are magically transparent when you need them to be and opaque when you don't. So far I think we've been able to use binding to do all of the things that Perl 5 references allowed (but more cleanly and efficiently).

Pm

@p6rt
Copy link
Author

@p6rt p6rt commented Aug 4, 2016

From zefram@fysh.org

Patrick R. Michaud wrote​:

"store in a data structure" --> "bind using :="

That's not going to pass well through anything that treats the data
as values. For example, after your

my @​c; @​c[2] := $a; $a = 5; say @​c[2];
5

if I then do

  my @​d = @​c.map({ $_ })

then @​d[2] is not an alias of @​c[2] or $a. It's also quite inconvenient
to bind into a data structure like that. The actual code I'm working
on at the moment has the form

  my $flange_width;
  my $farthingale_height;
  my @​computations = (
  { output_loc => $flange_width.VAR, formula => "aaa" },
  { output_loc => $farthingale_height.VAR, formula => "bbb" },
  );
  loop {
  # ...
  for @​computations {
  # some computation using $_<formula>
  if $got_it { write_to($_<output_loc>, $result); }
  }
  }
  # later code uses $flange_width and $farthingale_height

There are multiple computations to perform, which are done by similar
processes, and they benefit from partial computations being interleaved.
So they benefit from the partial computations being described by an
array that's looped over. But once it's done, the final outputs are
treated quite individually.

To do this with := means that I can't create @​computations in a neat table
like that, with each entry self-contained. I'd have to do something like

  my @​computations = (
  { formula => "aaa" },
  { formula => "bbb" },
  );
  @​computations[0]<output> := $flange_width;
  @​computations[1]<output> := $farthingale_height;

Note that related items (output location and formula, for a particular
computation) are now separated. The .VAR version had the nice feature
that the reference, being an ordinary value, can be mentioned inline in
the constructor expression.

I suppose I could add an extra level of data structure​: create a
one-element array and bind my target into it, then use that array as my
valueish reference. The array can be passed around by value, put into
a data structure, and so on. Something like

  sub ref_to(Mu $a is rw) { my @​s; @​s[0] := $a; @​s }
  sub read_ref(@​s) { @​s[0] }
  sub write_ref(@​s, Mu $nv) { @​s[0] = $nv }
  my $a = 3;
  my $b = ref_to($a);
  write_ref($b, 5);

But that seems perverse, when the Scalar object itself is visible to
the language. In principle the Scalar object is exactly what I'm after;
it's just missing a write method.

More generally -- when I last checked Perl 6 doesn't really have
"references" in the same style as Perl 5

I'm not particularly looking for the same style. I'm looking for the
basic ability. In Perl 5 it's done by reference scalars, in C it's done
by pointers; it is surprising that Perl 6 would not support this paradigm.

magically transparent when you need them to be

I'm not clear what you're referring to here. The way I interpret those
words, Perl 5 references are never magically transparent​: there is
always an explicit distinction between the reference and the referent.
(Except for the now-removed experimental autoderef feature on a
few builtins.) Perl 6, on the other hand, frequently has operations
silently pass through a container. (Named method calls on the Scalar,
where not handled by the Scalar class, used to be passed through to the
contained value, but that seems to have stopped working.)

-zefram

@p6rt p6rt added the Bug label Jan 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant
You can’t perform that action at this time.