54

What are some guidelines for the best use of if versus unless in Perl code? Are there strong reasons to prefer one or the other in some situations?

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
JSBձոգչ
  • 40,684
  • 18
  • 101
  • 169

9 Answers9

61

In Perl Best Practices, the advice is to never use unless. Personally, I think that's lunacy.

I use unless whenever there's a simple condition that I would otherwise write as if( ! ... ). I find the unless version to be more readable, especially when used as a postfix:

do_something() unless $should_not_do_that;

I recommend avoiding unless anytime things get more complicated, such as when you will have elsif or else blocks. (Fortunately, or perhaps unfortunately, depending on your perspective, there is no elsunless)

Also, any time a conditional is a complex expression made up of other booleans. For example,

unless( $foo and !$bar )

Is pretty damn confusing, and does not have any advantage over the equivalent if.

Borodin
  • 126,100
  • 9
  • 70
  • 144
friedo
  • 65,762
  • 16
  • 114
  • 184
  • 5
    I don't think `unless` need be automatically avoided for complex expressions. `if (!($foo and !$bar))` isn't necessarily clearer and the programmer shouldn't have to (and may not want to) apply DeMorgan's Theorem to get the cleaner `if (!$foo or $bar)`. – Michael Carman Jun 15 '10 at 21:41
  • 4
    @Michael The problem with not applying DeMorgan's Theorem is that the next programmer to come along will have to apply it in his head when he reads the code. I ran across this at work recently with a bit of code like `unless ($obj->has_errors || !$obj->is_valid) { return 1 } # error handling code follows` Or in English "If the prior actions on the object were not not successful don't run the error handling code." – Ven'Tatsu Jun 16 '10 at 15:24
  • Just my perspectives: Five years and counting, I've never regretted I left Perl for Python. `unless` is the keyword I never used during my three years with Perl; not because I read the Best Practices, the don't use the word in normal conversation neither. The best thing I got from Perl, by the way, is regular expression. Period. – biocyberman Feb 20 '17 at 20:17
36

Aside from one esoteric case1 unless is just syntactic sugar for if !. It exists to allow you to write code that is clearer and more expressive. It should be used when it achieves this goal and shunned when it impairs it.

I find unless to be most useful for flow control in loops. e.g.

while (<$fh>) {
    next unless /\S/;
    # ...
}

For simple negations I find it clearer than a negated if -- it's easy to miss that leading ! when reading code.

unless ($condition) {
    do_something();
}

if (!$condition) {
    do_something();
}

But don't write unless ... else, because that's just jarring.

In postfix form it provides a hint about what the expected path through the code is.

do_normal_thing() unless $some_unlikely_condition;


1) The last expression evaluated is different, which can affect the behavior of subs without an explicit return.
Michael Carman
  • 30,628
  • 10
  • 74
  • 122
  • 2
    Sometimes I find the negation reads more clearly if the keyword 'not' is used rather than '!', e.g.: get_ready() if not $ready; – Grant McLean Jun 16 '10 at 02:07
  • 3
    Absolutely great response. `unless` exists to improve readability. Remember that and everything else follows. – daotoad Jun 17 '10 at 05:41
  • Can somebody provide more detail about the footnote ("The last expression evaluated is different, which can affect the behavior of subs without an explicit return")? I can't find any documentation related to it. – jfritz42 Oct 22 '13 at 20:47
  • 3
    @jfritz42: Implicit return values are documented in [perlsub](http://perldoc.perl.org/perlsub.html#DESCRIPTION). There's no documentation of the difference between `unless (EXPR)` and `if (!EXPR)` with respect to implicit returns; it's just a natural consequence of how they're used. Assuming that EXPR is true (so the conditional's block is not entered) and that there's no `else` block, the condition is the last evaluated expression and thus its result is the implicit return value. With `unless` that expression is `EXPR` (true), whereas with `if` it's `!EXPR` (false). – Michael Carman Oct 23 '13 at 17:47
  • I'm not sure I agree that ! is easy to miss. If it is then should we change all single char operators to words, eg + to plus, = to equals etc? – MikeKulls Jan 15 '14 at 02:54
12

A rule of thumb is that 'unless' should probably be used infrequently.

It is particularly useful in the postfix form, e.g.:

delete_old_widgets() unless $old_widget_count == 0

Situations where you should never use unless:

  • with a compound condition (and, or, not)
  • with an else clause
Grant McLean
  • 6,898
  • 1
  • 21
  • 37
11

I spent an hour recently trying to explain to someone how two nested 'unless' clauses worked, it was difficult to decypher without having to invert them into if statements with boolean logic.

If you try to convert it into English, it helps to guide you.

A simple unless works fine. For example.

'Unless you are quiet, I am going to ignore you'.

unless ($quiet) {
    ignore();
}

Although I think this works equally well

'If you are not quiet, I am going to ignore you'

if (not $quiet) {
    ignore();
}

when it starts getting complicated is when you have negation.

'Unless you are not noisy, I am going to ignore you'

unless ( ! $noisy) {
    ignore();
}

Far better written as

'If you are noisy, I am going to ignore you'

if ($noisy) {
    ignore();
}

So, don't use an 'unless' if you also have a negate.

Also don't use 'unless else'

unless ($quiet) {
    ignore();
}
else {
    give_a_sweet();
}

'Unless you are quiet, I will ignore you, otherwise I will give you a sweet'

Change it by inverting the condition.

if ($quiet) {
    give_a_sweet();
}
else {
    ignore();
}

'If you are quiet, I will give you a sweet, otherwise I will ignore you'.

with more than one condition, it get's messy.

unless ($quiet and not $fidgit) {
    punish();
}

'Unless you are quiet and you don't fidgit, I will punish you'.

(sorry my comprehension is failing here!)

again, negate it.

if (not $quiet or $fidgit) {
    punish();
}

'If you are not quiet, or you fidgit, I will punish you'.

the problem with using 'unless' even for the simplest cases, they often (by yourself or by outhe

I hope that makes it clear when you should, or should not, use unless?

(unless you don't have another opinion?)

user3043717
  • 37
  • 1
  • 4
2

Though I understand the motivations for these sorts of questions I don't believe it's really sensible to boil down the use of something like unless into concrete rules of thumb. This, like a lot of auxiliary syntax in Perl is provided as a minor convenience for programmers to help them articulate themselves clearer at their own discretion; I've seen as much said in more ways than one by the lead developers throughout "Programming Perl". There's no higher purpose or rationalization. I'm well aware this finesses the question, but the only constraint I'd impose on using it is to see that it serves its broader purpose of making the code clearer. If it does, then all is well. Recognizing whether code is understandable is intuitive in itself and can't be reduced to a large handful of overly generalized usage conditions concerning every nuance of the built-in modifiers/operators/overall syntax, and where constraints and guidelines are needed in large group projects I don't think it would make sense to split hairs this finely.

cikkle
  • 458
  • 5
  • 12
1

My opinion would be to never use unless. My reasons are:

  • I think the syntax makes code more difficult to read. Having a single method of doing an if makes thing simpler and more consistent.
  • If you need to add an else statement later on you should really change the unless to an if. It's just easier if it is already an if.
  • If the logic inside the unless statement becomes more complex then you can end up with odd code like "unless (x == 5 && y != 7). This is odd because there is a double negative on the second check.
  • There are other ways to negate things, ie x != 5
  • It's more consistent with other languages. I don't know of any other language that has an unless statement and I think there is a very good reason for this.

In perl there are really 4 ways to write an if statement, if, unless and then putting the check at the end of the line instead of the start. I much prefer a single consistent method that is consistent with other langauges also.

Just my $0.02 worth.

MikeKulls
  • 873
  • 1
  • 10
  • 22
  • 2
    So what you're saying is `do_use($feature) unless (!grep($_->name ne "perl" && $_->has_feature($feature), @languages));` Following that to its logical conclusion would have us all writing assembler or FORTRAN. :-) I think even if I'd never seen Perl before I might guess correctly what `unless` meant. – Denis Howe Mar 05 '14 at 12:27
  • Hi Denis, I don't really buy the argument that the logical conclusion is that we will end up using assembler. I write perl code as if it was any other language. I don't even write the "or die" any more. I will always write "if(!doSomething()) { die; }. It makes for VERY readable code, something that is lacking in a lot of perl code. If you're a maintenance programmer and you come in after me you get a lot of very clear code that uses no tricks and has lots of clearly defined functions. Did you read code you wrote above, it has a triple negative on name eq perl, or is it ne? It's hard to tell. – MikeKulls Mar 07 '14 at 03:53
  • 2
    @MikeKulls Don't write Perl code as if it was any other language. Write idiomatic Perl code, making appropriate use of the special tools Perl gives you that no other language does. – Medlock Perlman Mar 02 '16 at 16:46
1

Apropos...

In Perl Best Practices, the advice is to never use unless. Personally, I think that's lunacy.

After 20+ years preferring Perl over any of its alternatives (most of which would not exist had not Perl provided the template), I not only concur with the 'lunacy' verdict, I'm surprised (concerned) to hear that 'Best Practices' wants to get rid of it.

That said, I strongly prefer code written for clarity, as opposed to the implicitly obfuscated alternatives some Perl programmers adopt 'just because they can'. 'unless' is the unambiguous opposite of 'if', and therefore a very useful alternative to embedding a negation in an 'if' conditional, particularly if the conditional contains multiple operators. And that rationale applies even if followed by else/elsif.

edwin
  • 1
  • 1
  • Remember that *Perl Best Practises* also says that every regex pattern should use the `/msx` modifiers. Damian is generally better than that, and I think he may well have changed his mind about some of the contents. – Borodin Jun 02 '18 at 14:52
0

Just a personal opinion maybe, but I like to use Unless when the if condition would start with a !

David Brunelle
  • 6,528
  • 11
  • 64
  • 104
0

The syntax if(!$condition) is equivalent to unless($condition), and likewise if you swap the NOT'ing of the condition.

I personally prefer using only IF statements. Why? Because it's less to memorize. If you have 100 lines of code, half of it using unless() and the other half using if(), you'll need to spend more time debugging it than if it was just if() statements.

You may "get" the hang of unless and if, though, so that going between the two takes no time at all. But it's not just about if() versus unless(). Don't forget that...

if($condition) {
    #passed-condition code
} else {
    #failed-condition code
}

...is equivalent to...

unless(!$condition) {
    #passed-condition code
} else {
    #failed-condition code
}

But what about if(){...}elsif(){...}? How would you make an equivalent of that into unless(){...}elsunless(){...}? The more complicated the logic, the more difficult it becomes to go between if() and unless(). The less you have to remember and balance in your memory, the quicker you will be to debug your own code.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133