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

There has been discussion in the ChatBox recently about the need for unless. It was triggered by the suggestion that Perl should have an otherwise for use as a modifier:

a if b; c otherwise;

which looks ok iff the otherwise binds to the condition in the previous if. But do we need otherwise when we have unless? Consider:

a if (my $o) = b; c unless $o;

Then again, do we need unless. Many people find if ! $o at least as readable as unless $o and actually should be preferred because we are lazy, right?

When experienced Perlers (no, no, not me!) stand up in public and admit that they have fallen into traps posed by unless, perhaps it is time to deprecate the useless thing?


Perl is Huffman encoded by design.

Replies are listed 'Best First'.
Re: Useless unless
by chromatic (Archbishop) on Aug 06, 2005 at 00:58 UTC
    Perl should have an otherwise for use as a modifier:

    That's an immensely huge syntax change, making expressions span statements.

    Many people find if ! $o at least as readable as unless $o...

    I can believe they think they find it as unreadable, but I think they're wrong. How can it be as readable as unless? It's two distinct units instead of one, and one of the units is symbolic. It's worse when you nestle the bang against another symbolic unit, such as an opening parenthesis.

    I hate to sound like a self-proclaimed usability expert, but I do not see how this could be.

      /me waits for tye to rebut this ;-)

      Anyway, I agree on both the spanning statements and the usability of unless. To me, unless is way better than "if not $foo". However, anything more complex is often easier to write and read as an if:

      ... unless $foo and not $bar->baz()
      Switch that around to if, and it's easier to parse, I think.
      ... if not $foo or $bar->baz()
      Still not easy, but I think that's an improvement of at least half a step.

      Back to the otherwise statement. Here's just something to throw out there, more to provoke thought than as a serious suggestion. Rather than separating two statements, what would you think of:

      $a if $b, otherwise $c;
      Arguably, it's a single statement still. I think I still prefer the full if ($b) { $a } else { $c } version. But at least it deals with the "span statements" issue. Somewhat.

        The problem with not and unless is double-negation, which is complex. I only use unless in postfix statement form, and I never use it if there's a not.

        The problem with a negation and multiple conditionals is the same as in English -- ambiguity of the distributiveness of the negation. That's a case where parenthesizing helps.

        I still don't think I quite get this otherwise groove. Would

        $a if $b, otherwise $c;

        Be equivalent to

        $b ? $a : $c;

        ?

        If so, I guess that's kinda cool, if a little confusing. We already have differently-binding text equivalents for the usual symbolic logical operators, why not the good ol' ternary conditional?

        FWIW, I use unless instead of if ! all the time. In fact, I am routinely dissapointed by the fact that we don't have a built-in elsunless. :)

      That's an immensely huge syntax change, making expressions span statements.
      Is it? Is such a thing unprecedented in perl? At any rate, it seems entirely consistent with Perl's philosophy.
        Yes, expressions spanning statements would be unprecedented. And the 'otherwise EXPRESSION;' construct would be an syntax construct spanning statement, which also would be unprecedented. And it's omision doesn't contradict Perls philosophy - I bet Larry's first reaction would be:

        What would
        print "Hello"; otherwise print "world";
        do?
        And the smartass who would say otherwise looks at the last expression evaluated, and hence would only print "Hello", as print returns 1, I'd like to point that in such a case:
        $x = 0 if 3 > 2; print "foo" otherwise;
        will both assign 0 to $x, and print "foo" - as the assignment to $x is the last expression evaluated.
      Most programmers are only semi-literate; they never learned English well in the first place.

      Things like word order, natural language flow, and the basic concept of structuring code like well-written prose doesn't occur to them, because they can't write prose to begin with.

      It's all just a string of wierd symbols to them: 'u','n','l','e','s','s' is more characters than 'i','f',' ','!', and thus harder to understand.

      I'm no grammarian, nor do I claim to be good at English, but I can't count the number of comments I've read that don't even start with a capital letter, let alone clearly explain the topic at hand. :-(

      It's not just foreigners, either. :-( One native speaker who I worked with wrote endless run on sentences fragments, which were often self-contradictory. He capitalized things randomly.

      When you don't know what readable prose should look like, everything looks equally readable. :-(

Re: Useless unless
by revdiablo (Prior) on Aug 05, 2005 at 23:55 UTC

    Woe be the day someone goes through and removes all the redundant features from Perl, based on what he considers "useless." The better solution is much simpler: if you don't like unless, don't use it.

Re: Useless <c>unless</c>
by davidrw (Prior) on Aug 05, 2005 at 22:07 UTC
    a if b; c otherwise;
    doesn't the ternary operator accomplish this already with b ? a : c; ? (And second example is just (my $o = b) ? a : b;)

    Do you have any examples of uses of unless that have created traps?

    For better or for worse, I personally actually use ! $x, !$x, and unless $x ... depends on how it reads in context, and how it fits in the coding style of whatever code i'm modifying..

    Update: First one could also be: (b && a) || c; But it requires that a only ever return a true value.

      Can't use the ternary operator where the if/unless/otherwise is being used as a modifier.

      Any negated statement requires one more step of mental processing and it seems to take an extra step with unless to realise that it is negating the condition. The trap is simply that it is unless rather than if.


      Perl is Huffman encoded by design.
        Can't use the ternary operator where the if/unless/otherwise is being used as a modifier.
        modifier? do you mean assignment like my $x = $A ? $B : $C; ?
        The trap is simply that it is unless rather than if.
        How is that different a statement being !$x rather than $x and just missing/misreading the 'bang'?
      My issue with unless is that humans do not do De Morgan's Laws on the fly while debugging. We do it fine while developing and reading software, but when we're trying to figure out what happened, and we have several variables juggled around, we do very poorly at figuring out whether we hit an unless.

      I didn't believe this until I personally encountered it.

      Therefore as soon as I have 2 variables and in a non-trivial combination, I write an if.

        I didn't believe this until I personally hit it and now I've hit it personally more than once (even after I've been aware of the potential) and I've seen others hit it. And it doesn't even take two variables. The "unless" can make the "not" blend into the background and you can go over the code 5 times trying to figure out what is wrong and not realize that you've got one too few or too many implicit "not"s involved.

        There are lots of possible implicit "not"s. You can have an implicit "not" because your variable is called $disabled. You can have an implicit "not" because you are using < instead of >= or such. You can have an implicit "not" because the object lets you ask "are you empty" when you want to ask "do you have any members" or vice versa. Then you can have explicit "not"s (or "!"s).

        But the "not" implicit in "unless" is just too subtle and is too easy for your mind (if you are over 40) to drop. And you should try to never have more than one implicit "not" per conditional. But you are likely to not notice the other implicit "not"s when you are writing the code. Or the implicit "not" may not be there when you write the code because you are thinking at it from one direction but the bug forces you to think at it from another direction (which may partially explain why you've got a bug there).

        I also dislike swapping out keywords when maintaining code. So I dislike changing "if" to "unless" or vice versa when a conditional is adjusted. Phrase the test so it works with "if" and is as clear as possible, and if that requires that you add a not, then add an explicit one. "if not" will be less likely to confuse you than "unless" when you are agitated trying to debug something and it flows just as well even if you are trying to make your code read like lovely prose.

        So avoid the one sure source of an implicit "not": unless. Remove it from the language? Well, I wouldn't miss it, but having it in the language doesn't bother me. It can be useful in obfu and poetry, anyway.

        - tye        

Re: Useless unless
by spiritway (Vicar) on Aug 06, 2005 at 00:18 UTC

    It seems to me that the whole idea behind Perl is to give programmers freedom to choose what they want - and to skip what they don't. I encounter the "unless" construct often enough, and it doesn't create problems for me. I don't use it, though - I prefer if-then-else, probably because of the languages I first learned had nothing else.

    I don't have any great objection to introducing an "otherwise" into Perl, except that there doesn't seem to be any crying need for it. If it does become part of the language I probably won't use it, either - but it wouldn't be difficult to add one more word to the vocabulary of terms I need to understand.

Re: Useless unless
by neniro (Priest) on Aug 06, 2005 at 08:02 UTC
    I often use 'unless' as statement-modifier. It's one of the syntactic sugars in perl that even made the way into ruby. Of course it's just a negated if-clause, but to me it's more natural and I think that was the idea why Larry put it into perl.

    Beside this, where do we stop if we remove such features from perl? Do we get 'yet another context-free language' like python, with it's poor set of terms to express the programmer thoughts?

      Do we get 'yet another context-free language' like python, with it's poor set of terms to express the programmer thoughts?

      print "Stop " + "trolling%s"%(chr((60+60)%87))

Re: Useless unless
by tinita (Parson) on Aug 06, 2005 at 09:22 UTC
    it's like with so many things in life - if you use them in moderation and only when appropriate, then they're great. but don't overuse them.
    unless is one of the many reasons why i'm programming Perl.
Re: Useless unless
by jonadab (Parson) on Aug 06, 2005 at 12:14 UTC

    Am I the only one who uses unless in completely different circumstances from if not? To me, using unless implies that an exception to the rule is being given, an edge or corner case being handled; the protasis will *usually* be false, and so the apodosis will *usually* be carried out, but on the occasion that the protasis is true, then the apodosis is skipped. In other situations, when the protasis is likely to be true a lot, I use if not. I find this semantic distinction useful, in terms of making the code more legible when I come back to it later to make changes.

Re: Useless unless
by ysth (Canon) on Aug 07, 2005 at 04:52 UTC
    a if (my $o) = b; c unless $o;
    Um, that's a list assignment in scalar context you've got there; probably not what you intended:
    $ perl -wl print "if!" if (my $o) = 0; print "unless!" unless $o; __END__ if! unless!
Re: Useless unless
by sk (Curate) on Aug 07, 2005 at 07:01 UTC
    I agree with tilly's Re^2: Useless <c>unless</c>. I don't use unless frequently in my code. The only place I use it is in the following construct -  die $usage unless (@ARGV>0);. Every other place I switch to if as it is easy to read (at least to me)

    That said, I would not advocate for removing this feature from the language itself. I feel removing a feature should be done if pretty much (subjective huh?) everyone in the user community do not use it (or) the feature has a significant overhead to the parser and has little benefit or other reasons that my mind cannot grasp.

    I am aware of unless traps. But I cannot blame the lang if i fall for it. I always have the choice to pick if.

    Regarding the otherwise suggestion i like it, if it is not going to span multiple lines. What i miss the most is something like this -

    doX() if ($cond) otherwise doY();

    I know i could do

    if ($cond) { doX(); } else { doY(); }

    (or)

    ($cond) ? doX() : doY();

    The last one is pretty much the same as otherwise but just that otherwise gives a natural lang feel

    -SK

      doX() if ($cond) otherwise doY();

      I think I may be the only person in the world that would (actively) prefer to see that as:

      $cond and doX or doY;

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
        What happens if doX returns a false value?
Re: Useless unless
by jdporter (Paladin) on Aug 08, 2005 at 15:55 UTC
    What I was trying to get at was an 'else' for statement-modifier 'if'. The idea is to avoid the (quasi)equivalent
    foo() if $x; bar() unless $x;
    because that has serious potential problems. Namely, the conditional expression ($x) could be non-idempotent, or it could be expensive to evaluate. (Note that simply being a scalar variable doesn't avoid this problem: it could be tied.)

    The workaround is to use a temporary variable:

    { | for my $b ( $x ) | for $x -> my $b my $b = $x; | { | { foo() if $b; | foo() if $b; | foo() if $b; bar() unless $b; | bar() unless $b; | bar() unless $b; } | } | }
    but that's getting ugly. You may as well use the standard if/else and avoid a temporary.

    As for the suggestion of using the trinary operatory, I would just point out that the intended purpose of the trinary operator is as a selector between two expressions rather than two statements — that is, between data rather than code. Of course you can use it to select between expressions which are there for their side-effects... but using the trinary operator in void context is — or should be, to seasoned Perl programmers — as distasteful as using map in void context: Sure, it works, and has no ill effects, but as a matter of style, it's just bad.

    Another approach to 'otherwise' would be
    foo() else bar() if $x;
    but that has the disadvantage of putting the "false" action right next to the 'if', so that a human might (mistakenly) see "... bar() if $x;" which is completely wrong.

    Anyway, I really like 'otherwise', though I'm not seriously proposing it as an addition to the language. It's too late for such things. :-)

      Sure, it works, and has no ill effects, but as a matter of style, it's just bad.
      That's just a matter of opinion. A piece of opinion that doesn't come with a rational. Knowing you find the style bad is interesting for census takers, programmers will find it more interesting why you find it bad style.

      I rank using the trinary in void context on the same level as using 'or' or 'and' in void context:

      /^#/ and next; open ... or die ...; -d $_ ? handle_directory($_) : handle_file($_);
      and those are almost on the same level as using statement modifiers:
      next if /^#/; die ... unless open ...; parse_file($_) for @files;
      Either they are all bad, or none of them are. And for me, none of them are.
      You may as well use the standard if/else and avoid a temporary.
      No temporary variable, and no if/else, replacing
      EXPR1 if $x; EXPR2 unless $x;
      can be done as:
      ((sub {EXPR1}, sub {EXPR2})[!!$x || 0])->();
      except that EXPR1 and EXPR2 in the latter have their own scopes - but you have that with an if/else construct as well.

      If EXPR1 and EXPR2 are function calls using the same arguments, as in:

      foo($arg, @args) if $x; bar($arg, @args) unless $x;
      one could write:
      ((\&foo, \&bar)[!!$x || 0])->($arg, @args)