Welcome to the Monastery PerlMonks

### Re: Re^4: Pattern Matching Query

by PhiRatE (Monk)
 on Sep 18, 2002 at 22:44 UTC ( #199026=note: print w/replies, xml ) Need Help??

in reply to Re^4: Pattern Matching Query

Ah. Interesting. Tell me then, which is more readable:

if an assertion fails, return false.
if an assertion fails, return false.
if an assertion fails, return false.
return true

or

return the combined success of assertion one and assertion two and assertion three

Wait, lets try that again:

if an assertion fails, return false.
if an assertion fails, return false.
if an assertion fails, return false.
if an assertion fails, return false.
if an assertion fails, return false.
if an assertion fails, return false.
return true

vs:

return the combined success of assertion one and assertion two and assertion three and assertion four and assertion five and assertion six

Now I have little doubt this will prove to be debatable. In my opinion however, for humans, the first is easier to read, and more importantly, more consistant (WHAT! You say consistant? why is this consistant evl PhiRatE person! well, explainations shall be forthcoming).

The concept I utilised is one known as a "Guard clause". It is, by my own admission, entirely overkill in the two and maybe in the three condition simple function that we're playing with now, however it becomes very important in more complex functions, and thus the principle of being consistant with your code suggests that (given no appreciable efficiency or readability loss) you should use it in simpler functions as well.

The normal "best case" example for guard clauses is like this:

```function a (\$fruit, \$vegetable) {
if (!\$fruit) { return; } # Guard clause
if (\$vegetable) {
print "Fruit and Vege";
} else {
print "Just the fruit ma'am";
}
}

The idea being that arguments to the function and trivial tests are done immediately upon entry. The reasoning behind this is simple, it is easier to read nothing than it is to read anything, no matter how readable anything is. Thus, if you are tracking a bug through a series of functions, the earlier you encounter something that says "that condition is taken care of in this manner" the sooner you can stop reading that function and get back to finding the typo in line 700 of ObscureFoo.pm.

As such, I maintain that, except in simple cases (such as the one we are really supposed to be talking about :) guard clauses are a superior method of laying out a set of "deny" rules as it were (or accept rules if your function suits it), rather than creating a stacked boolean expression. It also has the benefit of extending elegantly to multi-line operations and individual operations depending on exactly what failed (Too many items! Item not recognised!) without completely refactoring the expression (into guard clauses anyway). (If you wish an exercise to test this, take the function we were working on, and get it to throw an exception when there are too many items. In mine you can do it with one change, in yours, you must effectively write mine, then make the change.)

So, while I agree that in the case specified, your example is marginally easier to read than mine, I maintain that my structure provides, overall, more consistency with the rest of the code likely to be written, and supports later modification with less issue.

I agree that the primary purpose of source code is to be read by a programmer, but I also recall that the biggest cause of failure in source code is incorrect modification. It would seem to me then, that a very light cost in readability for the advantage of seamless and elegant modification is a win, as is a general consistency with other, similar but more complex operations elsewhere in the code.

Of course, such things seem to be to some extent subjective. Interestingly I'm usually annoyed by this assertion since I believe very few things in programming are really as subjective as they are made out to be, however I have one particular aspect which "warps" my opinion of certian practices. I have a bloody awful memory :)

Thus, any practice which means I need to read less code (in ops, not in lines or characters) and remember fewer things (contents of variables, meanings of obscurely named functions, constants) tends to weigh higher with me than with some other programmers I know.

Guard clauses fall squarely into this category, with the amount of code read per debugging hunt being considerably reduced in the average case by the presence of negative assertions right at the start of each function.

People with a better memory may see less benefit from this technique :)

Replies are listed 'Best First'.
Re^6: Pattern Matching Query
by Aristotle (Chancellor) on Sep 18, 2002 at 23:22 UTC
You comparison was unfair. Note my initial example used formatting for neither. The second case really should have been
return the combined success
of assertion one
and assertion two
and assertion three
and assertion four
and assertion five
and assertion six

If you're using formatting to line one up, you have to use formatting to line the other up as well. I think I can stop here, as far as this particular example is concerned..

I have nothing against the concept of a guard clause per se, though your example would be much more readable if you emphasize what it is by putting the return in the spotlight and avoiding the ! linenoise:

return unless \$fruit;

As far as bad memory is concerned, I don't see how my style requires a good one. To the contrary, actually, I tend to be very visually oriented. My code formatting follows that rule - I want to be able to glean the structure of a function's body without having to actually read and grok it all, and indentation carefully follows that motto. Functions should usually not be longer than about 25 lines. Some have to be of course, but those are few and far between. Most of mine tend to be around 15. That's so little you need no memory - you can take the entire function in in a single look. It's beneficial to have as little line noise as possible. Each statement should do as much as possible while being readable at a glance. Repetition is to be avoided. Contrary to intuition, spotting subtle differences in a repeated pattern is difficult - it's easy to detect their presence, but hard to actually make them out. (Have you ever seen much repetition in literature outside of poems?)

Basically I try to write Perl close to how I'd write the same thing in English.

Makeshifts last the longest.

Hrm. I agree, I had not intentionally mis-formatted yours, I had simply cut&paste what I had several times :) ah cut&paste, I love thee and yet thy use doth cause me pain.

I dislike the return - unless structure, as I said, I have a bad memory, I prefer my condition first, so that I can compare my mental state against that and continue or discard the block depending on the result. If I have to read the operation first, I then have to remember what that was when I reach the condition :)

```if (you're using formatting to line one up) { you have to use formatti
+ng to line the other up as well };
{I think I can stop here} unless (another example is used).

You're not kidding me :) you really do write your code like you write your english :) :)

For reference, this is how I write the same thing in english:

```Regarding this particular example, if you're using formatting to line
+one up you have to use formatting to line the other up as well.

Thats highly amusing. Notice that the way I do it, I've put a guard clause in at the start of the sentence, effectively:

```if (!this particular example) { return }
if (you're using formatting to line one up) { you have to use formatti
+ng to line the other up as well }

Thats kinda cool :)

Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://199026]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2023-05-31 03:00 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?

No recent polls found

Notices?