perl6 rakudo 2016.11 match tries to assign to read-only variable, why not in 2016.07?
Asked Answered
H

4

6

I have the following method in an action class that worked well in Rakudo 2016.07, but I just installed 2016.11 and now the new Rakudo says my method tries to assign to read-only varible, and I just don't see the problem:

method ptName ($/) { 
    my $nameStr = $/.Str, my $lastName, my $firstName; 
    my $newMatch  # this is line 182; 
                  # Cannot assign to a readonly variable or a value
       = $nameStr.match(/ \" (<alpha>+) .*? \, \s* (<alpha>+) .*? \" /);
    $lastName  = $newMatch[0];
    $firstName = $newMatch[1];
    make "$lastName $firstName"; 
}

The whole error message is

Cannot assign to a readonly variable or a value
  in method ptName at /home/lisprog/Binary/grammar.pl line 182
  in regex ptName at /home/lisprog/Binary/grammar.pl line 151
  in regex TOP at /home/lisprog/Binary/grammar.pl line 137
  in block <unit> at /home/lisprog/Binary/grammar.pl line 217

What language spec has changed? Please help. Thanks.

=====================================================

Thank you raiph, Christoph, ZZ !! I don't know how to add a long comment with with right formatting. So, I am adding comments to my own post.

I wrote a test program, and now it seems that if I don't use ($/) in a method signature because I have to use .match inside the method, I can no long make anything. What did I do wrong? Here is the test program and the results:

The test program:

grammar test {
    regex TOP   { <foo><bar> }
    regex foo   { :i \s* foo \s* }
    regex bar   { :i \s  bar \s* }
}

class actTest {
    method foo ($x) { # program fails if I use $/ in signature
      print "1 "; say $x; # how to combine the 2 and show $x as match?
      print "2 "; say $x.WHAT;
      my $newStr = $x.Str;
      print "3 "; say $newStr;
      my $newMatch 
           = $newStr.match(/:i(f)(oo)/); # adverb cannot be outside?
      print "4 "; say $newMatch.WHAT;
      print "5 "; say $newMatch;
      print "6 "; say $/;
      my $oo = $newMatch[1].Str;
      print "10 "; say $oo;
      my $f = $newMatch[0].Str;
      print "11 "; say $f;
      my $result = $oo ~ $f;
      print "12 "; say $result;
      make $result; # now I cannot make anything; huh???
    }
    method TOP ($/) { 
      print "8 "; say $<bar>;
      print "9 "; say $<foo>.made; # failed, method 'foo' makes nothing
      make $<bar> ~ $<foo>.made; 
    }
}

my $m = test.parse("Foo bar", actions => actTest.new);
print "7 "; say $m;

And the results:

1 「Foo 」
2 (Match)
3 Foo 
4 (Match)
5 「Foo」
 0 => 「F」
 1 => 「oo」
6 「Foo」
 0 => 「F」
 1 => 「oo」
10 oo
11 F
12 ooF
1 「Foo」
2 (Match)
3 Foo
4 (Match)
5 「Foo」
 0 => 「F」
 1 => 「oo」
6 「Foo」
 0 => 「F」
 1 => 「oo」
10 oo
11 F
12 ooF
8 「 bar」
9 (Any)
Use of uninitialized value of type Any in string context.
Methods .^name, .perl, .gist, or .say can be used to stringify it to
something meaningful.
in method TOP at matchTest.pl line 28
7 「Foo bar」
 foo => 「Foo」
 bar => 「 bar」
Has answered 5/12, 2016 at 8:35 Comment(4)
I vaguely recall something about this. What happens when you write method ptName ($/ is copy) {? What about method ptName ($/ is rw) {?Owl
Thank you raiph ! When I use ($/ is copy) on any $a=$someString.match(), then the program runs, but fails to give meaningful $a[0] or $a[1]; when I use ($/ is rw), then new error occurs and it says "Parameter '$/' expected a writable container, but got Match value". I did not have this problem in Rakudo 2016.07. I think something broke in 2016.11. Last time I tried to report a bug, the email bounced back as undeliverable. Thanks.Has
@lisprogtor: people seem to be able to open tickets again, cf perl6.failIkhnaton
The current way to open tickets is by emailing to [email protected] The "New Ticket" button has been disabled due to massive spam abuse and the issue is still being worked on.Iong
I
8

There are two questions here and I'll answer each in turn:

1) match tries to assign to read-only variable

It tries to set the $/, which you already have in your scope and as read-only. You can simply use a different name in the signature for the grammar's $/. I see you're coercing it to Str right away, so why not let the signature do that:

method ptName (Str() $nameStr) { ... }

2) why not in 2016.07?

The new behaviour is not a bug and it was decided by the core team the current behaviour is desirable (EDIT: actually, on further examination it appears the old behaviour was a bug where .match silently failed to set $/ if it were readonly).

So the bigger question is why did the behaviour change at all?

While there's an obvious goal to keep behaviour the same so users can rely on it, we also need to improve stuff and move forward. The way it works is we have a huge test suite we call Roast. If a change to code is made that breaks a test, that change cannot be made in the current language and must be postponed until next language release (since then users can use the use v6.c or whatever to still get old behaviour).

However, if the test suite passes, that means whatever changed hasn't been nailed down and we can change it without waiting for next language release. That is the case here, with the .match.

Obviously, we don't want our users to rake through our test suite looking for what is tested and what isn't, which is why our documentation follows the policy that we don't document stuff that's not covered by the roast.

That is currently the case with the .match method you've used. I'm unsure where you've learned about it, as at the end of September I created a doc Issue indicating .match wasn't documented, so it wasn't in the docs then and was awaiting the overhaul lizmat worked on, which changed the behaviour that bit you.

So I hope that answers some questions. I do understand code breaking like that is quite unfortunate and we'd love to make that happen as rarely as possible, but at the same time we need to improve the language. Hopefully, as the implementation matures there will be fewer and fewer of these untested and undocumented features.

Cheers, ZZ

Iong answered 6/12, 2016 at 15:11 Comment(5)
what was the rationale for the decision? personally, I preferred the old behaviourIkhnaton
@Christoph, I wasn't involved in that change so I can't answer for sure. You can search the dev channel logs (it was around October) or hop on to our dev channel and try to catch lizmat++ who did all the work.Iong
@Christoph, but playing around with code, I see .match did set $/ even before the change, it just silently failed when $/ was read-only, leading to unexpected behaviour. So I would guess this was just a bug fix and not a behaviour change.Iong
Thank you ZZ, Christoph, raiph !! Now there seems to be a new problem: it seems that if I don't use ($/) in a method signature because I have to use .match inside the method, I can no long make anything. What did I do wrong? Please see the test program and results in my original post that I edited.Has
@lisprogtor, make is just a shortcut to call $/.make. So since you're not using $/, use $whatever.make(...)Iong
I
1

According to git blame src/core/Str.pm, lizmat worked on this in October. Str.match will now fiddle with the lexical $/ of the calling block when it apparently did not do so before.

This means Str.match will return its result twice: First as its return value, and a second time out of band by setting $/. While regex application via ~~ does this as well (which is necessary so you can use syntactic sugar like $0 or $<foo>), I'm not convinced Str.match should. If it formerly did not do so, I suspect it might be a bug: Either report it as such or ask in #perl6 on Freenode about it.

As Zoffix points out, it's the old behaviour that was buggy as $/ always was supposed to get set, but being unable to do so just did not trigger an exception.

Ikhnaton answered 5/12, 2016 at 8:56 Comment(4)
Thanks Christoph ! I am not sure what you mean by "no need to do so."Has
@lisprogtor: cf say "foo".match(/f/).WHICH === $/.WHICH; I'll clarify...Ikhnaton
FWIW, the ~~ also returns the resultant Match object: my $res = 'foo' ~~ /.+/; say $resIong
@ZoffixZnet: it does indeed (as it should); I figured setting of $/ might be one of the things that differentiates $s ~~ /.../ from $s.match(/.../) besides one of them coming with less squiggles...Ikhnaton
H
1

I want to thank ZZ, Christoph, raiph, and smls for helping me. My problem has been solved by:

  1. using a method signature other than $/, and
  2. using the signature match object to make instead of using $/ to make.

Please also refer to answers at: perl6 grammar actions: unable to make anything if not using $/

Has answered 8/12, 2016 at 5:49 Comment(0)
L
0

You can also use an immediately-invoked lambda: -> $x { my $/; $x.match(/regex/) }($/)

Leahy answered 23/8, 2020 at 15:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.