Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Readonly oddity

by Anonymous Monk
on Jan 26, 2016 at 11:00 UTC ( #1153664=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Consider the following script:

#!/usr/bin/perl use strict; use warnings; use Readonly; use feature qw/say/; Readonly my $foo = 'bar'; Readonly my @baz = (qw/glug argh/); say "foo is '$foo' and baz is '@baz' in main"; say "Perl version is $] and Readonly version is $Readonly::VERSION";

On this system, it generates the following output:

Use of uninitialized value $foo in concatenation (.) or string at read line 11. foo is '' and baz is 'glug argh' in main Perl version is 5.020003 and Readonly version is 2

You might have noticed that the script above is subtly broken. I should have written Readonly my $foo => 'bar' . But the mistake is easy to make, as it is very natural to write my $foo = 'bar' and it is plausible to think that "Readonly" works by merely extending this syntax.

The insidious thing is that on this system, with these versions of Perl and Readonly it doesn't complain at all, it just silently leaves the variable undef. Even worse, it appears to work with arrays!

For that matter, the very same script breaks on my other system with Perl 5.14 and Readonly 1.03.

Is this a known problem?

If not, I see two approaches to fix it:

  • Restore 1.03's behavior: break with an error message
  • Make it work for scalars, not just arrays and document it

Replies are listed 'Best First'.
Re: Readonly oddity
by ww (Archbishop) on Jan 26, 2016 at 13:16 UTC

    Your systax does NOT match that which is documented for 1.03 & 2.00:

    use Readonly; # Read-only scalar Readonly::Scalar $sca => $initial_value; Readonly::Scalar my $sca => $initial_value;

    Version 1.03 is very old: From CPAN: "This documentation describes version 1.03 of, April 20, 2004). " The more recent version is 2.00 (with a different maintainer) and a copyright date of "2013-2014." (And that doesn't begin to address the archaic versions of Perl you mention -- 5.14 and, even more so, 5.02... which, if correct makes the "aside" below relevant).)

    Aside; unknown relevance but perhaps of interest from the 2.00 doc:

    Please note that most users of Readonly no longer need to install the companion module Readonly::XS which is recommended but not required for perl 5.6.x and under. Please do not force it as a requirement in new code and do not use the package variable $Readonly::XSokay in code/tests. For more, see "Internals" in the section on Readonly's new internals.

    Updated markup and s/"The version you're using.../"Version 1.03...."/p>

      the archaic versions of Perl you mention -- 5.14 and, even more so, 5.02

      I think you've misread the latter. The OP refers to "5.020003" which is 5.20.3 when the leading zeroes are omitted.

      The Synopsis for Readonly 1.03 and 2.00 do in fact show this form:

      # Alternate form (Perl 5.8 and later) Readonly $sca => $initial_value; Readonly my $sca => $initial_value; Readonly @arr => @values; Readonly my @arr => @values; Readonly %has => (key => value, key => value, ...); Readonly my %has => (key => value, key => value, ...);
      Readonly-1.03 Readonly-2.00

        I think the OP knows the correct way to use the module, the point is that when it is used incorrectly it throws an error in v1.3 but doesn't in v2.0

      I just changed:

      Readonly my $foo = 'bar'; Readonly my @baz = (qw/glug argh/);


      Readonly my $foo => 'bar'; Readonly my @baz => (qw/glug argh/);
      And got fine output.

      I would ask the initial "Anonymous Monk" poster to please read "perldoc Readonly" a bit more carefully. Thx.

      As choroba noted the post did not do a good job of answering the question and the comment with a strike through it was not appropriate. I have taken some time and effort to research the question more carefully and have put some insights that I still feel may be useful in a new separate post in this thread.

        I would ask the initial "Anonymous Monk" poster to please read "perldoc Readonly" a bit more carefully. Thx.
        I'd ask you to please read the OP a bit more carefully. Thx.
        You might have noticed that the script above is subtly broken. I should have written Readonly my $foo => 'bar'
        ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Readonly oddity
by tangent (Vicar) on Jan 26, 2016 at 16:28 UTC
    FYI. Perl 5.18 with Readonly 2.0 behaves the same as you have - just uninitialised value error.

    Using Perl 5.14 with Readonly 1.3:

    Type of arg 1 to Readonly::Readonly must be one of [$@%] (not scalar assignment) near "'bar';" Type of arg 1 to Readonly::Readonly must be one of [$@%] (not list assignment) near ");"
    I compared the source of Readonly 1.3 with 2.0 and couldn't see any obvious difference. Will need to try v1.3 and v2.0 with the same version of Perl.
Re: Readonly oddity
by mr_ron (Chaplain) on Jan 28, 2016 at 00:58 UTC

    > ... with these versions of Perl and Readonly ... it (fails silently)
    > the ... script breaks ... with Perl 5.14 and Readonly 1.03

    The script with Perl 5.20/Readonly 2 looked broken to me too, but I now understand that instead of failing silently the older Perl/Readonly refused to run and probably issued an error message like:

    Type of arg 1 to Readonly::Readonly must be one of [$@%] (not scalar a +ssignment) at line 8, near "'bar';" Execution of aborted due to compilation errors.

    So allowing assignment to run and leave an undef value seems at first to be a new surprising feature (bug) with Readonly.

    On looking closer at the problem the change is, as tangent suggested, more because of changes in Perl than in Readonly. With Perl 5.14 I get the same error whether I use Readonly 1.03 or 2. If I use Perl 5.22 then Readonly 1.03 gives the same silent/undef behavior as Readonly 2.

    Readonly uses subroutine prototypes and there has been a change in subroutine prototype behavior with newer versions of Perl. As described in proto documentation, both 5.14 and 5.22 allow:

    perl -Mstrict -we 'sub f(\$){}; f(my $x)'

    Newer Perls also allow:

    perl -Mstrict -we 'sub f(\$){}; f(my $x = 3)'

    whereas in Perl 5.14 the above code gives the familiar looking error:

    Type of arg 1 to main::f must be scalar (not scalar assignment) at -e +line 1, near "3)

    The new behavior seems to me to be a reasonable extension from a language perspective but, even if Perl is causing the change, one result of the new proto behavior in Perl is a change in the behavior of Readonly that I understand to be undesirable even if it makes undocumented use of the module. I agree and understand the mistake could be easy to make.

    In the original post the poster asked:

    > Is this a known problem?

    I did not see it listed under the CPAN/git module issues or anything in RT. There is an odd hint added to the documentation for Readonly 2 that wasn't in the older documentation.

    Readonly $var;
    Note that you can create implicit undefined variables with this function like so Readonly my $var; while a verbose undefined value must be passed to the standard Scalar, Array, and Hash functions.

    So allowing for a Readonly scalar with a default value of undef is now documented as allowed. "Readonly my $var;" actually worked with Perl 5.14 and Readonly 1.03 too.

    The original poster had two suggestions:

    • Restore 1.03's behavior: break with an error message (Meaning have the script refuse to run and issue an error message.)
    • Make it work for scalars, not just arrays and document it (hashes - I happened to check and "Readonly my %h = (...)" worked OK but "Readonly my %h = { ... }" may present some problems.)

    I played with patching both ways and view either to be feasible. For somewhat complicated reasons that sort of boil down to extensive efforts by the Readonly code bending over backwards to be backward compatible, I am somewhat in favor of the first approach of issuing an error for this usage.

    In summary, I feel a bug report for the module is merited by the problem but would be interested in feedback from other monks.

    A sample patch for the problem for Readonly 2 is included for informational purposes. It passes the test suite but shouldn't be applied by anyone depending on the module.

    *** lib/ 2016-01-27 11:21:02.875021600 -0500 --- lib/ 2016-01-27 19:12:43.312714100 -0500 *************** *** 298,303 **** --- 298,305 ---- my $badtype = _is_badtype (ref tied ${$_[0]}); croak "$REASSIGN $badtype" if $badtype; croak "Readonly scalar must have only one value" if @_ > 2; + croak 'You meant $sca => $val not $sca = $val' + if @_ == 1 && defined ${$_[0]}; my $tieobj = eval {tie ${$_[0]}, 'Readonly::Scalar', $_[1]}; # Tie may have failed because user tried to tie a constant, +or we screwed up somehow.

      The haphazard syntax of Readonly (not to mention its dueling APIs) trips up more people and generates more email than any of you guys can imagine... An extra warning that perl itself no longer throws may help clear up some of the 'weird' things people run into so I'll take this patch.

      Generating a new fatal error though may 'break' a lot of code. Then again if they were using this incorrect syntax their code was already broken, right? ((sigh)) I'll push it to PAUSE as a dev build for a few weeks. At the end of February (or March), I'll make it stable along with the mutable clone stuff I wrote almost two years ago.

      Edit: Well! It's a minor change but I like giving credit where it's due so please feel free to submit a pull request here.

Re: Readonly oddity
by dmitri (Priest) on Jan 26, 2016 at 20:58 UTC
    Readonly was a subject of my lightning talk Quick, to the Volcano! It is also on YouTube somewhere.

    I know you did not ask this, but (we people from USSR love to give advice): avoid Readonly and use constant instead.

      I disagree with your advice to avoid "Readonly" for several reasons:

      Readonly variables interpolate into strings.

      Readonly can protect an entire complex data structure.

      Readonly can be removed easily from production code if constraints on speed or space require it.

      I don't agree with this advice either. I personally don't like constant because it is not suitable for any complex structures and you can't interpolate those constants in strings. Neither I like Readonly because you have to type "Readonly", which is quite a lot and the capital 'R' makes it simply unbearable. So I use Const::Fast.
        > I personally don't like constant because it is not suitable for any complex structures

        For example?

Re: Readonly oddity
by mr_ron (Chaplain) on Mar 03, 2016 at 18:02 UTC

    There is now a new release, VERSION 2.01, of Readonly that addresses the problems in the original post. On closer examination it turned out that while

    Readonly my @baz = (qw/glug argh/); Readonly my %h = (key => 42);

    initialized variables with the right values, the resulting variables were not read only and could be modified unlike variables correctly initialized with the '=>' operator. So the new patch/release disallows initialization by assignment with a 'croak' message.

    An issue was opened and now closed for the concern in this post: Readonly GitHub issue 18. It turns out that Readonly has old issues on as well as new issues on GitHub. There actually was a ticket for the posted problem on but I didn't know enough to check there when I searched. The rt ticket 86086 may now be closed AFAICT.


Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1153664]
Front-paged by Arunbear
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (3)
As of 2023-03-27 00:14 GMT
Find Nodes?
    Voting Booth?
    Which type of climate do you prefer to live in?

    Results (63 votes). Check out past polls.