Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Readonly vs ReadonlyX

by rlauer (Novice)
on Mar 10, 2023 at 13:27 UTC ( [id://11150885]=perlquestion: print w/replies, xml ) Need Help??

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

This may have been answered but my search results did not show any relevant information. Reading the documentation for both Readonly and ReadonlyX seems to indicate that using Readonly today will yield acceptable performance. However, running perlcritic (brutal) on a script that contains Readonly yields:
Used module Readonly at line 9, column 3. Readonly.pm is buggy and sl +ow. Use Const::Fast or ReadonlyX instead, or the core pragma constant +. (Severity: 2)
Further, as I am tasked with maintaining legacy code and using perlcritic to assure some level of adherence to PBP I find clashes between Readonly and ReadonlyX when these two modules are used by other modules within the same script. Case in point File::BOM which uses Readonly and one my scripts that I have now converted to use ReadonlyX based on the advice of perlcritic (blindly following advice results in stuff like this). So I ask, is it ok to ignore the advice of perlcritic and use Readonly? Is the advice outdated? Readonly does appear to be slower than ReadonlyX:

Readonly:
Benchmark: timing 10000000 iterations of const, literal, normal, ro, r +o_simple, rotie, tglob... const: 0 wallclock secs ( 0.37 usr + 0.00 sys = 0.37 CPU) @ 27 +027027.03/s (n=10000000) (warning: too few iterations for a reliable count) literal: 0 wallclock secs ( 0.37 usr + 0.00 sys = 0.37 CPU) @ 27 +027027.03/s (n=10000000) (warning: too few iterations for a reliable count) normal: 0 wallclock secs ( 0.40 usr + 0.00 sys = 0.40 CPU) @ 25 +000000.00/s (n=10000000) <b>ro: 1 wallclock secs ( 0.90 usr + 0.00 sys = 0.90 CPU) @ + 11111111.11/s (n=10000000)</b> ro_simple: 5 wallclock secs ( 5.71 usr + 0.00 sys = 5.71 CPU) @ 17 +51313.49/s (n=10000000) rotie: 5 wallclock secs ( 4.68 usr + 0.00 sys = 4.68 CPU) @ 21 +36752.14/s (n=10000000) tglob: 1 wallclock secs ( 0.66 usr + 0.00 sys = 0.66 CPU) @ 15 +151515.15/s (n=10000000)
ReadonlyX:
Benchmark: timing 10000000 iterations of const, literal, normal, ro, r +o_simple, rotie, tglob... const: 0 wallclock secs ( 0.34 usr + 0.00 sys = 0.34 CPU) @ 29 +411764.71/s (n=10000000) (warning: too few iterations for a reliable count) literal: 0 wallclock secs ( 0.38 usr + 0.00 sys = 0.38 CPU) @ 26 +315789.47/s (n=10000000) (warning: too few iterations for a reliable count) normal: 0 wallclock secs ( 0.49 usr + 0.00 sys = 0.49 CPU) @ 20 +408163.27/s (n=10000000) <b>ro: 1 wallclock secs ( 0.42 usr + 0.00 sys = 0.42 CPU) @ + 23809523.81/s (n=10000000)</b> ro_simple: 2 wallclock secs ( 0.45 usr + 0.00 sys = 0.45 CPU) @ 22 +222222.22/s (n=10000000) rotie: 1 wallclock secs ( 0.76 usr + 0.00 sys = 0.76 CPU) @ 13 +157894.74/s (n=10000000) tglob: 0 wallclock secs ( 0.59 usr + 0.00 sys = 0.59 CPU) @ 16 +949152.54/s (n=10000000)
Clearly ReadonlyX is faster than Readonly, however when the verbose form (Readonly::Scalar) is used the difference in timing is really only teased out when the number of iterations is very large...my conclusion(?) is that using Readonly::Scalar is acceptably fast and I should ignore the advice of perlcritic when faced with the issue that is produced when Readonly and ReadonlyX clash. Consider:
package Boo; # some module I don't control from CPAN use strict; use warnings; use Readonly; Readonly::Scalar our $BOO => 'boo'; 1;
use strict; use warnings use ReadonlyX; # take the advice of perlcritic! use Boo; # this module uses Readonly Readonly::Scalar my $FOO => 'bar';
...and then
perl -I . foo.pl Prototype mismatch: sub Readonly::Scalar ($;$) vs ($$) at /usr/share/p +erl5/Readonly.pm line 257. Prototype mismatch: sub Readonly::Scalar ($;$) vs ($$) at /usr/share/p +erl5/Readonly.pm line 343. Prototype mismatch: sub Readonly::Readonly (\[%@$]$) vs (\[$@%]@) at ( +eval 7) line 42.Management is only worried about my (our) code...not +other CPAN modules that use Readonly
Management only cares that my/our code adheres to PBP - we can't control all of the modules the legacy code uses...so this tends to work (sometimes):
use strict; use warnings; BEGIN { use Module::Loaded; use ReadonlyX; mark_as_loaded('Readonly'); } use Boo; Readonly::Scalar my $FOO => 'bar';
...but not always because ReadonlyX is not a drop in replacement (in all cases) for Readonly
package Boo; use strict; use warnings; use File::BOM; 1;
...and we see this when the use of Readonly is incompatible with ReadonlyX
perl -I . foo.pl Useless use of reverse in void context at /home/rclauer/lib/perl5/File +/BOM.pm line 205. Odd number of elements in hash assignment at /home/rclauer/lib/perl5/F +ile/BOM.pm line 187. Compilation failed in require at Boo.pm line 6. BEGIN failed--compilation aborted at Boo.pm line 6. Compilation failed in require at foo.pl line 12. BEGIN failed--compilation aborted at foo.pl line 12.
So perhaps we should use constant...but PBP suggests that we don't. But more importantly, our/my scripts are not always going to be the ones mixing and matching Readonly and ReadonlyX...and if we happen upon two modules one that use Readonly and one that use ReadonlyX we cross our fingers and hope our Module::Loaded situation works or we need to figure out which module we can adopt, replace or otherwise alter in order to make all of this legacy code work AND adhere to PBP. I think this is probably enough to get the point across...I'm confused...and seek the wisdom of the monks.

Replies are listed 'Best First'.
Re: Readonly vs ReadonlyX
by choroba (Cardinal) on Mar 10, 2023 at 14:09 UTC
      The Preface of PBP is explicit about the thinking required before any of its suggestions are accepted. Unfortunately, it seems that software QA managers do not always read (or believe) the Preface.
      Bill
        As already elaborated, this has nothing to do with PBP.

        The OP just didn't do his homework and keeps blaming PBP and his company for their PC-policies.

        Cheers Rolf
        (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
        Wikisyntax for the Monastery

      Thank you for the reference, I will read that for more insight. It's true I can mitigate issues of using ReadonlyX in my code by using constant when constant fits the use case, however it still leaves the conundrum of Readonly vs ReadonlyX clashing when two modules not under my control are involved...
        The author of ReadonlyX explains at length that he "improved" the API to be a "drop in replacement"

        (--> https://metacpan.org/pod/ReadonlyX#ReadonlyX-vs.-Readonly)

        And this aliasing of namespaces happens in his module

        BEGIN { *ReadonlyX:: = *Readonly:: } package # hide from PAUSE Readonly; # I wish...

        that's unfortunate to word it mildly.

        Especially since the author explicitly admits incompatibilities. (quote: "break 16 years of code out there in Darkpan.")

        When another module loads the original Readonly, different implementations and prototypes will collide.

        This "collides" with my understanding of a "drop-in-replacement".

        If you want to use ReadonlyX, force it to use it's own namespace.

        If there is no option for that, file a feature request.°

        Regarding the strong wording of the PC-Policy - which I also dislike for its style- I doubt it originates in PBP.

        And it seems to me this module is also far newer (2016) than Damian's book (2005), so please stop referencing it as justification.

        Cheers Rolf
        (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
        Wikisyntax for the Monastery

        °) I could also come up with a wrapper module which is fixing that by unaliasing the namespaces, but let's wait what the author says.

Re: Readonly vs ReadonlyX
by hippo (Bishop) on Mar 10, 2023 at 17:21 UTC

    You don't seem to have referenced this issue in your post, although you are obviously aware of it, having contributed.

    This entire problem seems to be an unholy mess arising from good intentions less-than-perfectly executed. I don't know how you got to here with your codebase but now might be a good time to write some tests and then factor out one or other of these apparently incompatible modules. Good luck.


    🦛

Re: Readonly vs ReadonlyX
by LanX (Saint) on Mar 10, 2023 at 14:05 UTC
    > 'Readonly.pm is buggy and slow.'

    Could you provide us with details why? Does this rule list an author or references?

    PBP is quite old already, many things could have changed in the meantime.

    And this wording "buggy and slow" doesn't sound like the tone in PBP...

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

      Hi! I can take the blame for some of Readonly and all of ReadonlyX!

      So, I inherited Readonly to fix a bug that was breaking a dependency 2 or 3 levels up from what I was working on at the time. From what I remember (way back in 2013 or 2014), it was flat out crashing on what was then bleadperl and became 5.20.x. p5p was refactoring magic around tied variables and some undocumented behavior Readonly had been taking advantage of since at least 2002 was finally being fixed in perl itself.

      The showstopper I needed fixed didn't change how Readonly behaved but there are still several major bugs that cannot be fixed without harming people. I groan whenever I see Readonly as a dependency to this day because it is so fundamentally broken. I made the most used of Readonly's 3 different APIs almost 50 times (not 50%) faster according to benchmarks without an issue but the guy who talked me into wasting months on deeply, CLONE-able, tie-based immutable vars that'll never work properly better avoid dark alleys. :) If it works for you, great. I don't suggest using Readonly for new code though.

      ReadonlyX was just a gist on github until someone asked for it to be uploaded to CPAN and I pushed it as a dist in the middle of the night without much of a plan, updated some of the docs a day or so later, and never even created a repo for it. It was never really meant to be a true drop in replacement for Readonly despite that hasty claim because I consider Readonly broken by design (not the fault of the original author; even brilliant code will become unwieldy and begin to smell after 20 years). ReadonlyX uses perl's internal, fast magic to mark an SV * as immutable, borrowed the least broken of Readonly's 3 different APIs, and maintains predictable behavior over different versions of perl which Readonly cannot promise without breaking darkpan. It never should have been given the name ReadonlyX but, well, it's out there now.
        Hi

        Thanks for the feedback and sorry for my maybe overly harsh reaction.

        I think the issue with Readonly and ReadonlyX colliding could be easily fixed if you released an extra module - probably in the same distribution - which put's ReadonlyX subs into another namespace before exporting.

        Something like ReadonlyX::Proper or ReadonlyXXX (There are for sure better names)

        Like this modules using the subs qw/Scalar Array Hash/ from there, won't see strange collision effects, if other modules deeper down use Readonly or ReadonlyX.

        I think this could be done without adding much code by refactoring all the code into an extra readonly_code.pl w/o namespace and letting ReadonlyX and ReadonlyX::Proper do the file after declaring their packages.

        But of course you are the expert here :)

        Thanks for your work!!!

        update

        OTOH Const::Fast exists and seems to do the job already with a new interface.

        If your point was to have a drop-in replacement, I doubt this can be done with those fully qualified subs like Readonly::Scalar without caveats.

        Maybe you should just mention those drawbacks in the module's POD.

        Cheers Rolf
        (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
        Wikisyntax for the Monastery

      The "buggy and slow" is output from perlcritic, not PBP.
        As I said you should track down the source and reasoning behind this policy, before overly relying on it.

        Furthermore, perlcritic has plenty of config options and severity levels.

        TIMTOWTDI.

        Cheers Rolf
        (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
        Wikisyntax for the Monastery

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11150885]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (4)
As of 2024-04-25 09:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found