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

T

TerryP

This is a question for the language mavens that I know hang out here.
It is not Python related, except that recent comparisons of Python to
Google's new Go language brought it to mind.

NOTE that this is *not* a suggestion to change Python.  I like Python
just the way it is.  I'm just curious about language design.

For a long time I've wondered why languages still use blocks
(delimited by do/end, begin/end, { } , etc.) in ifThenElse statements.

I've often thought that a language with this kind of block-free syntax
would be nice and intuitive:

    if <condition> then
        do stuff
    elif <condition> then
        do stuff
    else
        do stuff
    endif

Note that you do not need block delimiters.

Obviously, you could make a more Pythonesque syntax by using a colon
rather then "then" for the condition terminator.  You could make it
more PL/I-like by using "do", etc.

You can write shell scripts using if ... fi, but other than that I
don't recall a language with this kind of syntax.

Does anybody know a language with this kind of syntax for
ifThenElseEndif?

Is there any particular reason why this might be a *bad* language-
design idea?


If you search Google, you will likely find some huge comparason of if
or flow control statements in general, in zillions of languages.


I personally like

if expr [then | { | do | :]
the code
[done | } | ]

As long as it starts with if and is immediately followed by expr, I
could care less about the rest, unless someone forgets to document
it ;-). Lisps cond is also sexy.
 
B

bartc

Steve Ferg said:
This is a question for the language mavens that I know hang out here.
It is not Python related, except that recent comparisons of Python to
Google's new Go language brought it to mind.

NOTE that this is *not* a suggestion to change Python. I like Python
just the way it is. I'm just curious about language design.

For a long time I've wondered why languages still use blocks
(delimited by do/end, begin/end, { } , etc.) in ifThenElse statements.

I've often thought that a language with this kind of block-free syntax
would be nice and intuitive:

if <condition> then
do stuff
elif <condition> then
do stuff
else
do stuff
endif

Note that you do not need block delimiters.

Obviously, you could make a more Pythonesque syntax by using a colon
rather then "then" for the condition terminator. You could make it
more PL/I-like by using "do", etc.

You can write shell scripts using if ... fi, but other than that I
don't recall a language with this kind of syntax.

Does anybody know a language with this kind of syntax for
ifThenElseEndif?

I think Algol 68 was responsible for a lot of this syntax, but there must be
huge numbers of languages using that those forms now, apart from C-family
languages which evolved their own syntax (and even in C, the preprocessor
uses #if, #elif, #endif).

Some syntaxes insist on ugly begin...end blocks, but the bracketing
introduced by Algol68 (maybe even earlier) showed that these are redundant:

if s then s [elsif s]* [else s] fi

where s is any sequence of statements. The statements after the 'then' are
delimited by an elsif, else or fi (endif) keyword.

The endif (or fi) is necessary to show where the final statement sequence
ends, and fixes the dangling-else problem. (And Algol68 also allowed this
short form:

(a | b | c)

which is just if a then b else c fi, although it gets a bit hairy with elsif
in there too..)
I've often thought that a language with this kind of block-free syntax
would be nice and intuitive: ...

You're not the first to think that.. I use these forms in my own language
designs for that reason. And when people write pseudo-code, it always seems
to look like this, then they go and implement it in some god-forsaken syntax
like C++...

....For some reason I can't quite understand, this sort of clear syntax seems
to be looked down on by 'real' programmers, who perhaps think code like this
can't be taken seriously.
 
D

Dotan Cohen

2009/11/16 Steve Ferg said:
This is a question for the language mavens that I know hang out here.
It is not Python related, except that recent comparisons of Python to
Google's new Go language brought it to mind.

NOTE that this is *not* a suggestion to change Python.  I like Python
just the way it is.  I'm just curious about language design.

For a long time I've wondered why languages still use blocks
(delimited by do/end, begin/end, { } , etc.) in ifThenElse statements.

I've often thought that a language with this kind of block-free syntax
would be nice and intuitive:

   if <condition> then
       do stuff
   elif <condition> then
       do stuff
   else
       do stuff
   endif

Note that you do not need block delimiters.

Obviously, you could make a more Pythonesque syntax by using a colon
rather then "then" for the condition terminator.  You could make it
more PL/I-like by using "do", etc.

You can write shell scripts using if ... fi, but other than that I
don't recall a language with this kind of syntax.

Does anybody know a language with this kind of syntax for
ifThenElseEndif?

PHP has exactly this:

if (condition) {
// stuff
} elseif (otherContition) {
// otherStuff
} elseif (yetAnotherCondition) {
// yetOtherStuff
}


Furthermore, PHP has the switch statement:
http://php.net/manual/en/control-structures.switch.php

switch ($i) {
case 0:
echo "i equals 0";
break;
case 1:
echo "i equals 1";
break;
case 2:
echo "i equals 2";
break;
}

The break commands end the switch, and they can be removed to have
multiple matches perform multiple functions.

Is there any particular reason why this might be a *bad* language-
design idea?

It is about as far from OO as one could get. Whether or not that is
"bad" depends on the use case.
 
S

Steve Howell

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. Anonymous methods would help to a certain
degree. I am not saying I want either anonymous methods or switch
statements, but the lack of either in a language leads to very
procedural looking code when you use number-of-lines-of-code as a
(possibly dubious) metric.

Maybe this excerpt can be golfed down to something simpler, I would
love to see it!

if kind == 'dict':
return dictionary_schema(ast)
elif kind == 'list':
method = dictionary_schema(ast)
return lambda lst: map(method, lst)
elif kind == 'attr':
return ((lambda obj: getattr(obj, ast.field)), ast.field)
elif kind == 'key':
return (lambda obj: obj.get(ast.field), ast.field)
elif kind == 'as':
method, old_name = schema(ast.parent)
return (method, ast.synonym)
elif kind == 'call':
method, old_name = schema(ast.parent)
def call(obj):
return method(obj)()
return (call, old_name)
elif kind == 'recurse':
expr = ast.expr
kind = expr.kind
method, field_name = schema(ast.parent)
if kind in ['attr', 'key']:
new_method, new_field_name = schema(expr)
field_name = new_field_name
elif kind in ['dict', 'list']:
new_method = schema(expr)
else:
raise Exception('unknown kind!')
def recurse(obj):
obj = method(obj)
return new_method(obj)
return (recurse, field_name)
else:
raise Exception('unknown kind!')
 
C

Chris Rebert

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.

Cheers,
Chris
 
S

Steve Howell

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

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. 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. Of course, you could do something like __modules__
[kind](ast) too, but that gets a bit overly abstract for a simple
dispatch.

Having said all that, I'm gonna take your suggestion...thanks for the
response!
 
S

Steve Howell

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.


Here's the rewrite. The indirection from the definition of _dict to
the first use of _dict is a little painful (36 lines away), but apart
from that I am pleased with how the new code looks, so your points are
well taken:

kind = ast.kind
def _dict(ast):
return dictionary(ast, metavar)

def _list(ast):
ignore, code = dictionary(ast, 'item')
code = '[%s for item in %s]' % (code, metavar)
return label, code

def _attr(ast):
return ast.field, '%s.%s' % (metavar, ast.field)

def _key(ast):
return ast.field, '%s[%s]' % (metavar, repr(ast.field))

def _as(ast):
ignore, parent = _code_generate(ast.parent, metavar)
return ast.synonym, parent

def _call(ast):
parent_name, parent = _code_generate(ast.parent, metavar)
parent += '()'
return parent_name, parent

def _recurse(ast):
parent = ast.parent
expr = ast.expr
parent_name, parent = _code_generate(parent, metavar)
kind = expr.kind
if kind in ['attr', 'key', 'list']:
parent_name, parent = _code_generate(expr, parent,
parent_name)
else:
subparent_name, subparent = _code_generate(expr,
parent_name)
parent = '(lambda %s:\n' % parent_name + indent_block
(subparent+')(%s)' % parent)
return parent_name, parent

dispatches = {
'dict': _dict,
'list': _list,
'attr': _attr,
'key': _key,
'as': _as,
'call': _call,
'recurse': _recurse,
}
if kind in dispatches:
return dispatches[kind](ast)
else:
raise Exception('unknown kind!')

Thanks!

P.S. The underscores before the method names might look a little funny
for inner methods, but it's the nature of the code..._dict and _list
would lead to confusion with builtins, if not actual conflict.
 
B

bartc

Dotan said:
2009/11/16 Steve Ferg (e-mail address removed):

PHP has exactly this:

if (condition) {
// stuff
} elseif (otherContition) {
// otherStuff
} elseif (yetAnotherCondition) {
// yetOtherStuff
}

The OP explicitly said no block delimiters. Your example uses {..}, and
doesn't have endif.
 
S

Simon Forman

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.  Anonymous methods would help to a certain
degree.  I am not saying I want either anonymous methods or switch
statements, but the lack of either in a language leads to very
procedural looking code when you use number-of-lines-of-code as a
(possibly dubious) metric.

Maybe this excerpt can be golfed down to something simpler, I would
love to see it!

       if kind == 'dict':
           return dictionary_schema(ast)
       elif kind == 'list':
           method = dictionary_schema(ast)
           return lambda lst: map(method, lst)
       elif kind == 'attr':
           return ((lambda obj: getattr(obj, ast.field)), ast..field)
       elif kind == 'key':
           return (lambda obj: obj.get(ast.field), ast.field)
       elif kind == 'as':
           method, old_name = schema(ast.parent)
           return (method, ast.synonym)
       elif kind == 'call':
           method, old_name = schema(ast.parent)
           def call(obj):
               return method(obj)()
           return (call, old_name)
       elif kind == 'recurse':
           expr = ast.expr
           kind = expr.kind
           method, field_name = schema(ast.parent)
           if kind in ['attr', 'key']:
               new_method, new_field_name = schema(expr)
               field_name = new_field_name
           elif kind in ['dict', 'list']:
               new_method = schema(expr)
           else:
               raise Exception('unknown kind!')
           def recurse(obj):
               obj = method(obj)
               return new_method(obj)
           return (recurse, field_name)
       else:
           raise Exception('unknown kind!')


This code is perhaps not a great example of your point. Every
"(el){0,1}if" clause (other than the ones in the 'recurse' branch) end
in return statements, so the "el's" are pointless.

FWIW I might write it like so:

class ASTthing:

def processAST(self, ast, kind):
try:
method = getattr(self, 'do_' + kind)
except AttributeError:
raise Exception('unknown kind!')

self.ast = ast

return method()

def do_dict(self):
return dictionary_schema(self.ast)

def do_list(self):
method = dictionary_schema(self.ast)
return lambda lst: map(method, lst)

def do_attr(self):
field_name = self.ast.field
return lambda obj: getattr(obj, field_name), field_name

def do_key(self):
field_name = self.ast.field
return lambda obj: obj.get(field_name), field_name

def do_as(self):
method, old_name = schema(self.ast.parent)
return method, self.ast.synonym

def do_call(self):
method, old_name = schema(self.ast.parent)
return lambda obj: method(obj)(), old_name

def do_recurse(self):
expr = self.ast.expr
kind = expr.kind

method, field_name = schema(self.ast.parent)

if kind in 'attrkey':
new_method, field_name = schema(expr)

elif kind in 'dictlist':
new_method = schema(expr)

else:
raise Exception('unknown kind!')

def recurse(obj):
obj = method(obj)
return new_method(obj)

return recurse, field_name


IMO, that's more pythonic and less complicated.
 
D

Dotan Cohen

The OP explicitly said no block delimiters. Your example uses {..}, and
doesn't have endif.

Just out of habit. I think that PHP, like C, lets you avoid the block
deliminators so long as the block all fits on one line.
 
S

Steven D'Aprano

It is about as far from OO as one could get. Whether or not that is
"bad" depends on the use case.


That's crazy talk. IF...ENDIF is *syntax*, not a programming model. How
is this hypothetical Python-like syntax not object oriented?

class Parrot:
def speak(self):
if self.name is None:
name = "Polly"
else:
name = self.name
endif
return "%s wants a cracker!" % name


Syntax controls *how* you instruct the compiler, the programming model
controls *what* you instruct the compiler to do.
 
S

Steven D'Aprano

P.S. The underscores before the method names might look a little funny
for inner methods, but it's the nature of the code..._dict and _list
would lead to confusion with builtins, if not actual conflict.


Then name them something sensible that tells what they do!

The convention (which you break at your peril) is that functions are
verbs, and classes are nouns. Even "handle_dict" is better than _dict --
the latter looks like you're calling a private mapping type.
 
S

Steven D'Aprano

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

Steve Howell

Then name them something sensible that tells what they do!

The convention (which you break at your peril) is that functions are
verbs, and classes are nouns. Even "handle_dict" is better than _dict --
the latter looks like you're calling a private mapping type.

Do you verbify functions with no side effects? Do you prefer
calculate_cosine(angle) to cosine(angle)?
 
S

Steve Howell

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.

Different blocks of an if/elif/elif/elif/elif/end are never directly
coupled to each other, since you know that only one of the blocks is
gonna execute. The blocks inherently do not effect each other.

What you really achieve by extracting the code from an inside an elif
block into a method is decoupling the elif block from the code ABOVE
the if. This in and of itself is a good thing, of course, because you
get a nice clean namespace in the extracted method, and you make the
coupling between the code ABOVE the if and the extracted method
explicit by specifying which parameters get passed. And in the case
of my original code, all seven methods that were extracted only
depended on a single parameter, the variable I was calling "ast," so
it was easy to dispatch them all using the same mechanism. (In my
case I didn't achieve much in terms of cleaning the local namespace,
since the only variable defined above the if/elif/elif/elif/end was
"kind," but I did make my code less brittle to future changes.)

I am not going to defend "if/elif/elif/elif/elif/end" too vigorously
here. There are obvious advantages to extracting methods, even
methods that only ever get called from one parent. I do not miss
switch statements one bit in Python. I am little more ambivalent
about anonymous methods. It pains me just a tiny bit whenever I have
write code like this:

dispatches = {
'dict': handle_dict,
'list': handle_list,
'attr': handle_attr,
'key': handle_key,
'as': handle_as,
'call': handle_call,
}
if kind in dispatches:
return dispatches[kind](ast)
else:
raise Exception('unknown kind!')

I have used the idiom that Simon suggested in an earlier post, shown
below, but it is still brittle to name changes in a way that anonymous
methods are not, because you can't break an inline anonymous method
with a name change (the benefits on anonymity!!):

try:
method = getattr(self, 'do_' + kind)
except AttributeError:
raise Exception('unknown kind!')

self.ast = ast

return method()
 
S

Steven D'Aprano

Do you verbify functions with no side effects? Do you prefer
calculate_cosine(angle) to cosine(angle)?


Neither, I prefer cos(angle).

Mathematical functions are a special case. They have been around since
long before computers, and there is a whole different tradition for them.
(The tradition is that most functions are named after Euler, or the first
person to discover them after Euler.)

But you're right, the convention of using verbs for functions isn't as
strong as the convention of using nouns for classes and types. Functions
that return some property of a noun are often named after the property:

len(x) rather than get_len(x)
sqrt(x) rather than calculate_sqrt(x)

and similar.

Likewise, conversion functions are often named after the return result:

int(x) returns an int
str(x) returns a str

[pedant]
these are actually types, not functions, in Python
[/pedant]


But I'm not sure what naming convention you used. It seems fairly
arbitrary to me, but whatever it is, it clashes with built-ins, which is
a good sign that you need a different set of names. Since naming the
functions with leading underscores also clashes with the convention that
such functions are private, that's a further sign that you should use a
different naming convention. At the point that you are apologizing for
the convention you use, maybe you should rethink it.

But of course it's your code, and you're free to use whatever convention
you like.
 
S

Steve Howell

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

The existence of one code sample where lexical duplication is a false
negative should not get you into the habit of disregarding it all
together.

In my rewritten code, here is the smell:

dispatches = {
'dict': _dict,
'list': _list,
'attr': _attr,
'key': _key,
'as': _as,
'call': _call,
'recurse': _recurse,
}
if kind in dispatches:
return dispatches[kind](ast)
else:
raise Exception('unknown kind!')


There is nothing wrong with the code taken out of context, but one of
the first things that lexical duplication should alert you to is the
fact that you are creating code that is brittle to extension. In my
example, the reference to _dict is 36 lines of code away from its
implementation, which forces indirection on the reader. So maybe the
methods in between def _dict and _dict are too long. I can pretty
quickly rule that out, as the methods average about four lines each.

So maybe the dispatch method is calling out the need for a class, as
Simon suggested in another post. I also wonder if an inline decorator
is in order.

One thing I promise not to do is revert the code to if/elif/elif. :)
 
S

Steve Howell

[...]

But I'm not sure what naming convention you used. It seems fairly
arbitrary to me, but whatever it is, it clashes with built-ins, which is
a good sign that you need a different set of names. Since naming the
functions with leading underscores also clashes with the convention that
such functions are private, that's a further sign that you should use a
different naming convention. At the point that you are apologizing for
the convention you use, maybe you should rethink it.

But of course it's your code, and you're free to use whatever convention
you like.

The code in question, with the _list and _dict, is used as a part of
mini-compiler-like-thingy that generates code from an expression
syntax. If you've ever worked with code to implement compilers,
interpreters, VMs, etc., you probably know the naming challenges
involved. The whole reason I mentioned the wierd names in a "P.S."
was to say that I realized it was a little wierd, and it was kind of
besides the point. But I have enjoyed your responses. They are
slightly pedantic, of course, but in a good way! Makes me think about
the code more...

If you want more context on the code itself (apart from if/elif
discussion and other digressions), I describe it in more detail here:

http://showellonprogramming.blogspot.com/2009/11/more-on-python-deep-copy-schema.html
 
S

Steven D'Aprano

In my rewritten code, here is the smell:

dispatches = {
'dict': _dict,
'list': _list,
'attr': _attr,
'key': _key,
'as': _as,
'call': _call,
'recurse': _recurse,
}
if kind in dispatches:
return dispatches[kind](ast)
else:
raise Exception('unknown kind!')


There is nothing wrong with the code taken out of context, but one of
the first things that lexical duplication should alert you to is the
fact that you are creating code that is brittle to extension.

Really? It is very simple to extend it by adding another key:item to the
dict.

In my
example, the reference to _dict is 36 lines of code away from its
implementation, which forces indirection on the reader.

It gets worse -- the reference to the in operator is in a completely
different module to the implementation of the in operator, which may be
in a completely different language! (C in this case.)

I'm being sarcastic, of course, but I do have a serious point. I'm not
impressed by complaints that the definition of the function is oh-so-very-
far-away from where you use it. Well duh, that's one of the reasons we
have functions, so they can be used anywhere, not just where they're
defined.

If a function *only* makes sense immediately where you use it, then the
level of coupling is too great and you should either reduce the coupling
or bite the bullet and live with all the hassles of inline code. (Code
duplication, difficulty in testing, etc.)

Of course it would be convenient if, having noticed the reference to
(say) function _recurse, you could see its definition without needing to
do more than just glance up a line or two. But you should rarely need to
care about the implementation -- that's another point of functions. You
should be able to treat _recurse as a black box, just as you use the
built-in function len as a black box without needing to see its
implementation every time you use it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top