http://www.perlmonks.org?node_id=750376

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

In one of my modules I have a statement like this:

$MyApp::once{x} ||= 1;
Perl warns Name "MyApp::once" used only once: possibly typo. While it is true that this happened to be the only usage of %Pck::v in my program at the time, I wonder what is the best way to suppress this warning. Clearly I can't use use vars nor our here. Sure I can suppress the warnings with no warnings in the surrounding block. Any other suggestion?

OK, maybe we have a XY problem here, so this is how I stumbled into this problem (maybe you have a better suggestion, but I still would be interested in an answer to my original question as well): I have some functions, which gets executed often, but are supposed to log some information only during the first time they are executed:

sub f { ... log('some information'); # but should be called only the first time. ... }
In languages such as C++, I would use a local static for this purpose:
static LogRetType const once=log('some information');
We don't have local statics in Perl, so one possibility would be to use a 'my' variable on file scope:
my $once1=0; sub f { ... $once1 ||= (log('some information'),1); ... }
But I neither like the fact that the definition of $once1 is not logically close to its (only) usage, but also that I need to invent a new variable every time I do this in my package. So I thought that I would instead have a single variable of this kind for my whole application, but use a hash for this purpose, and the file name and line number as a key:
sub f { ... $MyApp{+__FILE__.' '.__LINE__} ||= (log('some information'),1); ... }
This works, as long as I have two or more lines of this type in my package; but if there is only one occurence, I get the aforementioned warning.

-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: Why do I get a "used only once" warning here?
by moritz (Cardinal) on Mar 13, 2009 at 12:06 UTC
    I don't see why you should do anything else than no warnings "once"; in a small scope. It does what you want, and every reader of the code immediately know what you wanted to achieve.
Re: Why do I get a "used only once" warning here?
by ikegami (Patriarch) on Mar 13, 2009 at 13:49 UTC
      Isn't there an error in this ?!

      It neighter seems to suppress the warning, nor do I understand how this is supposed to work. :(

      Thanks,

      Krambambuli
      ---

        Why didn't you show what you tried instead of just saying what amounts to "Does not!"

        >perl -wle"$MyApp::once{x} ||= 1;" Name "MyApp::once" used only once: possible typo at -e line 1. >perl -wle"%MyApp::once if 0; $MyApp::once{x} ||= 1;" >
Re: Why do I get a "used only once" warning here?
by bellaire (Hermit) on Mar 13, 2009 at 12:04 UTC
    Well, presumably you are storing keys in %MyApp because you intend to check them at some other point in your package. As soon as that check is in place, the warning will go away on its own. I'm not sure why you'd want to suppress the warning unless you are literally going to store a variable that you will never check or use, and then I'm not sure why you'd want to do that. So I guess the question is, why is it necessary for you to set the value but not check it? Or are you only wanting to suppress warnings until such time as you get around to adding the code to check the variable?
      presumably you are storing keys in %MyApp because you intend to check them at some other point in your package.

      Actually, I do check them, but not at another point in the package, but at the same. Note the usage of ||=.

      -- 
      Ronald Fischer <ynnor@mm.st>
        Ah, I missed the side-effect of the function, sorry. In fairness, Perl has no way of knowing (as I should have from the name of the function, "log") that your function has a side-effect. If this function were to simply return a value, then the warning would make sense, because you would be storing a value and never using it. With the side-effect, though, everything becomes clear.

        If you really want that syntax, then you should turn warnings off as others have suggested. Alternatively, you could use a syntax which mentions the variable twice. I don't know if the fact that I missed the meaning of your code is indicative of unclear syntax, or if it's just me being blind this morning.
Re: Why do I get a "used only once" warning here?
by JavaFan (Canon) on Mar 13, 2009 at 12:05 UTC
    We don't have local statics in Perl,
    What's wrong with state? Seems to me:
    sub f { state $once1 = log('some information') || 1; }
    will do.

    As for the warning in your solution, that's the stuff no warnings is for. I don't get the point of not wanting to use it.

      Most people are still on Perl 5.8.x or earlier, and so do not have state available.

      They do have statics through the horrible my $foo if 0; hack, but that is a bad way to do it.

      You can also use my module Tie::Static, with the associated speed penalty that ties always have.

Re: Why do I get a "used only once" warning here?
by AnomalousMonk (Archbishop) on Mar 13, 2009 at 18:10 UTC
    Clearly I can't use use vars nor our here.
    I don't get this point. Why can't they be used? Either one seems to do the job without the need to turn off warnings, although with perhaps a bit more verbosity (but also less worry about proper scoping).
    >perl -wMstrict -le "package L; use vars qw(%lone); our %once; sub l { return print __PACKAGE__, qq{: @_} } package main; print 'do some stuff in ', __PACKAGE__; package L; use vars qw(%singular); our %onetime; package main; $L::once{x} ||= (L::l('hi from', __PACKAGE__), 1); $L::lone{x} ||= (L::l('hello from', __PACKAGE__), 1); $L::onetime{x} ||= (L::l('hiya from', __PACKAGE__), 1); $L::singular{x} ||= (L::l('greets from', __PACKAGE__), 1); $L::once{foo} ||= L::l('this should print'); $L::once{foo} ||= L::l('but this should NOT print!'); " do some stuff in main L: hi from main L: hello from main L: hiya from main L: greets from main L: this should print
    Update: Replaced rather verbose example with much more succinct one.

      Indeed, the way you wrote it, it would work. In my original design, the variable would be in a different package, so neither

      use vars qw(%MyApp::once);
      nor
      our %MyApp::once;
      would have worked, because package qualifications are not allowed in use vars and our; put actually I don't need to put once in its own package; I can simply have it in my current package, and this would make your solution indeed feasible.

      -- 
      Ronald Fischer <ynnor@mm.st>
        ... the variable would be in a different package ...
        ... I don't need to put  once in its own package; I can simply have it in my current package, and this would make your solution indeed feasible.
        I'm still a bit confused, and I suspect we may be posting at cross-purposes.

        What I wanted to illustrate is that a package variable may be declared in any package at any time (with either use vars or our), and may then be accessed from any other package at any (executionally subsequent) time.

        >perl -wMstrict -le "package Some::Package; our $scalar = 'foo'; package Some::Other::Package; print $Some::Package::scalar; print 'current package is ', eval { scalar caller }; " foo current package is Some::Other::Package
        Update: Oops... Forgot to include example code.
Re: Why do I get a "used only once" warning here?
by Porculus (Hermit) on Mar 13, 2009 at 23:44 UTC

    Is it that important that there be only one variable for the entire application? Why not just

    sub f { our $OnceOnly{+__LINE__} ||= (log(...),1); }

    which would, ISTM, be largely equivalent (and somewhat simpler), but with no risk of warnings?

      I was not aware that I can use 'our' this way inside a block, and to my surprise, no warning appears! Thanks for the suggestion!

      UPDATE: I did not look closely enough. Your solution would not work, because

      our $v{...}=...
      is syntactically invalid. After all, you can only put the name of a variable after "our", not a hash element.

      -- 
      Ronald Fischer <ynnor@mm.st>
Re: Why do I get a "used only once" warning here?
by ig (Vicar) on Mar 13, 2009 at 19:58 UTC

    Maybe something like the following would suffice:

    package MyModule; my %OneTime; sub f { $OneTime{+__FILE__.' '.__LINE__} ||= (print("logging\n"), 1); return(); } 1;
      The OPer seems to want to control one-time printing/logging from outside the package/module, so that approach would not seem to do the trick. (Also, I don't know what the  + in front of the  __FILE__ is supposed to do.)

      One can play around with caller to get a general solution closer to what I think the OPer wants:

        The OPer seems to want to control one-time printing/logging from outside the package/module

        If one really wants a global in some other package, then...

        package MyModule; use strict; use warnings; sub f { $MyApp::OneTime{+__FILE__.' '.__LINE__} ||= (print("logging\n"), 1 +); return(); } 1;
        I don't know what the + in front of the __FILE__ is supposed to do

        In the current form, it has no meaning. It is a leftover from an earlier version, which read something like:

        $MyApp::once{+__LINE__} ....
        and so the + stayed there and when posting my problem, I copied it without much thinking. If you have only __LINE__ as key, the + is needed because __LINE__ is a bareword.

        -- 
        Ronald Fischer <ynnor@mm.st>
Re: Why do I get a "used only once" warning here?
by repellent (Priest) on Mar 15, 2009 at 04:05 UTC
    You seem to be spreading functionality that can be contained within the logging function. Why not:
    use warning; use strict; { my %log_seen; sub log_info { # do not continue if done before return if $log_seen{(caller(1))[3]}++; warn(@_, "\n"); } } sub f { log_info('some information'); print "called f()\n"; } f(); f(); __END__ some information called f() called f()

      The reason why I haven't put it into the log package is that this "log once" logic is supposed to be done only for a few selected logs. If I put it into the log package, I would have to write a new logging function, say log_once. Actually this is a serious alternative and I was thinking to do it, but when I started to implement this feature, I thought it would be easily possible too to have this logic implemented without changing/extending the logging package, then was trapped by the unexpected warning, and then my curiosity took over....

      -- 
      Ronald Fischer <ynnor@mm.st>
        Understood - you have to weigh the pros and cons of a log_once(). Just some thoughts:

        If you had a logging package that expected all other functions to be logged, except for a select few, and then for the select few you sprinkle some state variables around, doesn't that introduce some action-at-a-distance & maintenance difficulties in the far future?

        Granted, if there was just one or two state variables around, it would just be easy to remember, but wouldn't it also be that much harder to locate later?
Re: Why do I get a "used only once" warning here?
by sundialsvc4 (Abbot) on Mar 16, 2009 at 16:36 UTC
    How interesting... I had never encountered the phrase "XY problem" before. (Well, except in reference to the inhabitants of Venus and Mars ...) ;-)