Language mavens: Is there a programming with "if then else ENDIF" syntax?

Steven D'Aprano steven at REMOVE.THIS.cybersource.com.au
Wed Nov 18 18:02:31 EST 2009


On Wed, 18 Nov 2009 01:53:50 -0800, Steve Howell wrote:

> On Nov 18, 1:32 am, Chris Rebert <c... at rebertia.com> wrote:
>> On Wed, Nov 18, 2009 at 1:15 AM, Steve Howell <showel... at yahoo.com>
>> wrote:
>> > On the topic of "switch" statements and
>> > even-more-concise-then-we-have- already if/elif/else/end constructs,
>> > I have to say that Python does occasionally force you to write code
>> > like the code below.  Maybe "force" is too strong a word, but Python
>> > lends itself to if/elif blocks like below, which get the job done
>> > just fine, but which are not syntactically pretty, due to the
>> > "(el){0,1}if kind ==" duplication. There are often cases where
>> > if/elif statements are just a smell that you do not know how to do
>> > dictionary lookups, but if you converted the below code to use
>> > dictionary lookups, you would complicate the code almost as much as
>> > you abstracted the code, if not more, unless I am just being very
>> > naive.
>>
>> I'm gonna have to disagree and say using the dictionary dispatch
>> technique would clean it up a good bit. Yes, it would entail creating
>> several functions, but those functions could then be documented (vs.
>> the currently opaque code blocks); and due to their separation and
>> smaller length, they would be easier to understand and test than the
>> given code. Additionally, the sheer length of the given code segment
>> probably constitutes a code smell in and of itself for the function
>> containing that code.
>>
>>
> If you introduce seven tiny little methods, aren't you increasing the
> length of the module by seven lines and introducing more complexity with
> the dispatch mechanism? (Actually, it's 14 lines more if you put a line
> of whitespace between your methods, and then you are also blurring an
> important cue that each of the seven code blocks all perform within the
> same context.)


Dear me, the 1960s called, they want their controversy over structured 
programming back *wink*

Yes, you increase the length of the module by having function headers. 
You also increase the length of the module by using meaningful names 
instead of calling everything "a", "b", "c" etc. We're not playing code 
golf, there's no reward for making unreadable code.

The dispatch mechanism is no more complicated than a series of if...elif 
statements. In fact, you can replace an arbitrary number of "if x == y" 
statements with a *single* lookup:

if x == 1:
    return 27
elif x == 2:
    return 14
elif x == 5:
    return 7
else:
    return -1

becomes the single line:

return {1: 27, 2: 14, 5: 7}.get(x, -1)

(although for more complicated examples, you would put the dict into its 
own line, or even lines).

Dispatching isn't a panacea. You can't (easily) replace a series of 
if...elif statements where the conditions being tested aren't equality 
tests, e.g.:

if 0 <= x <= 1:
    return 27
elif 1 < x < 1.5 or x == 2:
    return 14
elif ...

is hard to turn into a dispatcher. But it's still a very useful technique 
when it applies.



> I do agree with your point that separate methods lead to easier unit
> testing.
> 
> I'm a little more skeptical about the documentation/understanding
> argument, since code is often best understood within the context of
> surrounding code.

That depends on the code. In particular, it depends on how coupled the 
code is. Ideally, you should have loosely coupled code, not highly 
coupled. If the code is loosely coupled, then there's no problem with 
understanding it in isolation. If the code is highly coupled, then it is 
hard to refactor it into a separate function, but that's a side-effect of 
the original problem, namely the high degree of coupling.

As a general rule, if you need to know the context of the surrounding 
code to understand a function, you have too much coupling.



> I am also a bit skeptical of any coding technique
> that leads to lexical duplication like {'attr': process_attr, 'key':
> process_key, 'call': process_call}(ast); that is just replacing one
> smell with another.

But how is that different from this?

if condition(x, 'attr'):
    return process_attr(x)(ast)
elif condition(x, 'key'):
    return process_key(x)(ast)
elif ...


Lexical duplication is one of the weakest code smells around, because it 
is so prone to false negatives. You often can't avoid referring to the 
same lexical element multiple times:


def sinc(x):
    if x != 0:
        return sin(x)/x
    return 1

That's four references to x in a four line function. Is this a problem? 
No, of course not.



-- 
Steven



More information about the Python-list mailing list