Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Re^5: Module Announcement: Perl-Critic-1.01 (scalar)

by rir (Vicar)
on Jan 27, 2007 at 01:35 UTC ( [id://596811]=note: print w/replies, xml ) Need Help??


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

Your position is problematic because your model ignores a fundamental, from perlsub: The Perl model for function ... return values is simple: ... all functions ... return to their caller one single flat list of scalars.

lc doesn't return a scalar it returns a one element list.

Be well,
rir

Replies are listed 'Best First'.
Re^6: Module Announcement: Perl-Critic-1.01 ("scalar")
by tye (Sage) on Jan 27, 2007 at 02:26 UTC

    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        

      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.
        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

      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        

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (5)
As of 2024-04-23 08:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found