in reply to Re^5: Module Announcement: Perl-Critic-1.01 (scalar)
in thread Module Announcement: Perl-Critic-1.01

That statement doesn't contradict my position. I never said that a single scalar can't also be considered as a list. I'm not playing some semantic game. I'm talking about practical implications.

If we want to play your semantic game, then replace "scalar" with "single-item list" throughout what I wrote (which requires patching Perl to rename the scalar() function, but such is life).

If you have a function that always returns exactly one item (as the list of items that it returns), then it doesn't make sense to change this function to return a zero-item list in one special case. (Why? Read what I wrote above.)

lc doesn't return a scalar

Repeat that to a few random people who know Perl and see how silly this particular semantic argument is. lc() always returns just a scalar. The list of scalars that lc() returns always has a size of one. Those are both true. *shrug*

- tye        

  • Comment on Re^6: Module Announcement: Perl-Critic-1.01 ("scalar")

Replies are listed 'Best First'.
Re^7: Module Announcement: Perl-Critic-1.01 (scalar)
by chromatic (Archbishop) on Jan 27, 2007 at 02:44 UTC
    If you have a function that always returns exactly one item (as the list of items that it returns), then it doesn't make sense to change this function to return a zero-item list in one special case.

    That's not what return; does. It returns something that's false in boolean, scalar, and lists contexts. I know you know this; it just seems valuable to be very explicit about what we're discussing.

    Now (on topic), it's difficult for Perl::Critic to recognize the semantic need for that with regard to any single specific Perl function. It seems like a variant of the semi-predicate problem; what does return undef; mean?

    Damian's rule is Use a bare return to return failure. I believe that's good advice.

      If you have a function that always returns exactly one item (as the list of items that it returns), then it doesn't make sense to change this function to return a zero-item list in one special case.
      That's not what return; does.

      return; doesn't return a zero-item list? Yes it does and I know you know this.

      It returns something that's false in boolean, scalar, and lists contexts.

      I don't think there is a such a thing as "false in list context". The advantage of return; is that it makes a list assignment false (and that it makes an array that you assigned the return value to false). But I don't find myself doing such types of assigning a scalar to a list in a boolean context and when I consider that, I find it unnatural.

      I don't find it unnatural to write:

      my %results= ( foo => doFoo(), bar => getBar(), );

      Where "foo" and "bar" are each the type of thing represented by a scalar value and one might fail to do "foo" or fail to get "bar". And it is convenient to be able to place in a scalar something that represents "oops, we failed to get this value" and that is why Perl has undef and I see no problem with using return undef; to represent a failure in a function that returns a scalar value (or to use return ''; to indicate failure in a fucntion that always returns a non-empty, true string value -- especially since it avoids gyrations to avoid "use of uninit val" warnings).

      Damian's rule is Use a bare return to return failure. I believe that's good advice.

      And I agree with that advice up to the point where you have a function that has always returned one scalar value in every other situation. I think I've covered rather thuroughly why I believe that. If the function usually returns just a scalar but sometimes doesn't, then I'd err on the side following that advice.

      In another reply you write:

      for those functions where their return value should be unambiguously false when it's false

      And I find that return; breaks that very rule in quite natural code such as the above followed by:

      if( $result{foo} ) {

      or natural code like:

      my( $foo, $bar )= ( doFoo(), getBar() ); if( $foo ) {

      Your advice would give us "bar" (which isn't false) after doFoo() failed.

      And I realize that when I write %hash= ( key => ... ) that "..." needs to be a scalar and so if someFunc() returns a list (that might have other than 1 element) then I need to write one of:

      %hash= ( key => scalar someFunc() ); %hash= ( key => [ someFunc() ] ); %hash= ( key => 0 + someFunc() ); ...

      But I would find it very unnatural to add to my mental model "if someFunc(), which is meant to return just a scalar, might want to return a failure indication then I have to put scalar() in because a failure indication can't be a scalar value (according to Damian)".

      - tye        

      Now (on topic), it's difficult for Perl::Critic to recognize the semantic need for that with regard to any single specific Perl function.

      Halleluyah!

      And that is the very essence of my aversion to Perl::Critic. You cannot (yet, and probably never) replace the human mind with a set of rules that can be interpreted and applied by a machine.

      When PBP was published, we could read it, take the arguements for each guide line on board; and then apply them subject to our own logic, reasons and emotion. For PHBs to enforce a 'Must comply with PBP' edict, they would have to read the book; read every line of code; and then make their arguments subject to both their own and the coders reason.

      The existance of Perl::Critic has now puts the tool in the hands of the PHBs to enforce the edict without the intervention of human reason. And that is always a bad thing.

      Imagine a world where human beings where unable to apply their reason and discrimination to rules and guide lines.

      You're working in one of those huge skyscrapers. A guy steps out of the lavatory having forgotten to zip his fly. He is seen by a cow-orker and that person hits the alarm button. Five or ten thousand people are evacutated to the carpark in the pouring rain for 2 hours whilst the local emergancy service respond to the alarm, verify the safety of the entire building and certify it safe for the workers to reoccupy. Thousands of people get wet; hundreds of customers get frustrated; dozens of companies take substantial financial hits; because no discrimination was applied to the situation.

      Ridulous you say. It would never happen. But that is exactly analogous to the absence of a particular RCS markup; the absence of an explicit package statement in a program (as opposed to a module); and 90% of the other 255 guidelines in PBP--when applied without descrimination.

      Yes, Perl::Critic can be applied with optional, discriminatory configuration. But if the default is for no discrimination, then it will become the defacto standard. Just as I feared it might when I first saw the module.

      There has to be discrimination between those practices that can genuinely lead to errors, bugs, or even simple misunderstandings; and those that are simple preferences--desirable in certain places, at certain times, on certain projects.

      The example above is proof by reduction to absurdity, but lets try something a little less absurd. The same scenario, but this time the problem is a laptop who's battery has gone open ciruit and is starting to smoke. You could hit the fire alarm with a similar result to the above. Or, you could walk over, unplug the charger from the mains and flip the battery eject lever. You might also use a pencil to slide the hot battery into an empty metal waste bin.

      Is it safe to take the latter course? It depends upon the circumstances--and that's where human reason and discrimination come in.

      That might sound like a contrived example, but it's very close to a real life situation, though I only know of it through hearsay. The person chose to take the latter action, with the addition of discharging a CO2 fire extinguisher into the waste bin to cool the battery down. He was formaly rebuked for not setting off the fire alarm and forcing a couple of thousand people from more than a dozen financial services companies from spending a couple of hours in the rain!


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        I seem to get by okay saying no strict 'refs'; once in a while, though you know as well as I do that people here holler if they don't see the always mandatory without questions asked strict and warnings pragmas in code.

        I'm really struggling to see how the existence of a tool and the existence of ignorant people who will use it badly suddenly means that the tool itself is a bad idea.

        You have my sympathy, but I find your argument Luddite-crous.

        I stopped using Perl because it would not scale up to the tasks I was addressing. Then the strict pragma was created; that automatic application of a set of contraints to code was helpful. The many real mistakes caught were well worth the few false positives.

        In the general case, it is foolish to assume that bosses are morons and underlings have the correct answers. A consortium of underlings may conspire and through insubordination save a project from a mandate of some stupid practice, but that just helps to prove the mandate worthwhile when a review is done.

        That there are 256 Perl best practices is just too cute.

        Be well,
        rir

        Update: Added when a review is done because the sentence could be read with either of two opposing meanings.

      My function doesn't return true/false. It returns the scalar reference of the weapon in the hand of the body object of a guy. Sometimes it's not defined, but it's not a list of weapons. if( $dude->weapon ) works. In fact, @weapons_in_left_hand = $dude->weapon also works because it returns the list of one weapon in his hand even when it's undefined (eg, if it were thrown). @weapons_in_left_hand == 1 should always be true for this function.

      Suggesting that all functions have to return success or failure — just in case someone tests it in an list context — seems just a little myopic.

      Although, I don't think I say it nearly as well as tye (ie: {left => $dood->query_weapon(0), right => $dood->query_weapon(1)} ), so I probably should have just left it alone.

      -Paul

        Suggesting that all functions have to return success or failure — just in case someone tests it in an list context — seems just a little myopic.

        I agree. When did I suggest otherwise? I believe I have been consistent in implying (if not outright saying, which is probably my mistake here) that it's functions that do return either success or failure that ought to watch for list context.

Re^7: Module Announcement: Perl-Critic-1.01 ("scalar")
by rir (Vicar) on Jan 27, 2007 at 05:19 UTC
    Other than populating lists about to become hashes, what advantages do you find with the explicit return undef convention?

    I wasn't trying to contradict you, play semantic games, nor make a silly argument. My concern was that your statements would mislead people with less understanding of the context issue.

    Perhaps, I was too brief before, because

    my %hash= ( key1 => genValue1(), key2 => genValue2() };

    was an obviously made-up example and I didn't care to pick at it.

    Why couldn't genValue1() generate a solitary first value or die and so conform to a bare return convention? Update: which is just to say either convention is workable; also key => val() || undef,

    Be well,
    rir

      Other than populating lists about to become hashes, what advantages do you find with the explicit return undef convention?

      I've already given other examples in this thread. In particular, passing arguments to functions and assigning values to a list of scalar variables.

      because [assigning a list to a hash] was an obviously made-up example and I didn't care to pick at it.

      Well, I don't often use "key1" as a key name nor name a function "genValue", but otherwise that code isn't an outlandish made-up example stretched beyond reason just to prove some point. It is an example of quite typical code that I chose exactly because I consider it quite typical, reasonable code that I've written often and see often. You never assign a list to a hash? Or do you never call functions when you do this? Perhaps you sometimes use "named arguments" to functions? That has identical problems.

      I don't know how to come up with examples that meet your expectations for being worth picking at if you can't tell me what is unreasonable about assigning a list to a hash (or passing (named or just positional) arguments to functions or assigning a list of scalar expressions to a list of scalar variables).

      You find val() || undef more readable / maintainable? We aren't talking about whether or not you can work around the over-use of return;, we are talking about whether it improves code in all cases. val() // undef would at least be closer in meaning to scalar val() but val() || undef assumes that all false values mean the same as undef. Just like it can be quite appropriate for "failure" to be returned as '' (as opposed to () or even explicit undef), it can be even more important for '' to be distinct from undef.

      Even push @results, doTask(); suffers if "failure" can only ever be the empty list. That'd have to be push @successes, doTask();.

      - tye        

        I don't have any significant disagreement since you modified your assertion that using bare returns is a bad practice. And thanks--I found your other examples after my post.

        Regarding:

           my %hash= ( key1 => genValue1(), key2 => genValue2() };

        I did not find assigning a list to a hash artificial; just the naming indicated a made-up example. I didn't mean to imply that your code is outlandish or stretched beyond reason. If I understood how I implied that, I would try to avoid doing so in the future.

        List to list assignments are expressive, but they can increase the complexity of error checking.

        The bare return convention can be seen as filling arrays without useless elements and as promoting early validation.

        I don't find your arguments about the convention breaking code convincing, because if the convention were in use the code would just be written differently. We are coming at this issue from very different places; I would not have said that I was talking about bare returns improving code in all cases. You are arguing from the bottom up, presuming the author's knowledge of what genValue1() can return; I see the value of these conventions in helping a reader to avoid the need to look up genValue1(). From that perspective, your example with scalar lc is beside the point because it is a builtin and so should be known.

        If I were running a large team and only one of these conventions was lintable, I would find lintability to be a compelling argument. The readability-in-the-small aspect becomes less significant.

        Be well,
        rir

        # bare return push @premiums, get_value( $_ ) for ( @xactions ); if ( @premiums ) { } # return undef for ( @xactions ) { push @premiums, defined( my $val=get_value($_) ? $val : (); } if ( @premiums ) { } ### # bare return my %hash; $hash{name} = get_name(); $hash{tag} = get_tag(); # return undef my %hash = ( name => get_name(), tag => get_tag() ); ### # bare return ( $second, $first ) = ( seconding(), firsting() ); if ( $first ) { } # return undef ( $first, $second ) = ( firsting(), seconding() ); if ( $first ) { }