Clarity vs. code reuse/generality

P

Paul Rubin

kj said:
sense = cmp(func(hi), func(lo))
assert sense != 0, "func is not strictly monotonic in [lo, hi]"

bisection search usually just requires the function to be continuous
and to have its value cross the target somewhere between the endpoints,
not be monotonic.
I regard the very special case of func(hi)==func(lo)==target as
pathological (analogous to the fact that a stopped watch is "exactly
right" twice a day), and not one I care to support.

I do think you should support that case, under the "do 'nothing'
gracefully" principle. Having your equation solver crash if the
equation is already solved is like having your sorting function crash
if the input is already sorted. E.g. the function should do something
reasonable if you end up calling it twice.
 
P

Paul Rubin

kj said:
This implies that code that uses *any* assert statement (other than
perhaps the trivial and meaningless ones like "assert True") is
liable to break, because whatever it is that these assert statements
are checking "on some occasions, ... would go unchecked, potentially
breaking your code."

Yes, that implication is absolutely valid. The purpose of assert
statements is to debug the code, by checking for conditions that are
supposed to be impossible. Unless the program is broken (i.e. the
impossible happened), no assert statement should ever trigger.

Invalid input data is not considered impossible and doesn't imply a
broken program, so assert statements are not the appropriate way to
check for it. I like to use a function like

def check(condition, msg="data error"):
if not condition: raise ValueError, msg

...
check (x >= 0, "invalid x") # raises ValueError if x is negative
y = sqrt(x)

instead.
 
M

MRAB

Paul said:
Yes, that implication is absolutely valid. The purpose of assert
statements is to debug the code, by checking for conditions that are
supposed to be impossible. Unless the program is broken (i.e. the
impossible happened), no assert statement should ever trigger.
Technically these are known as "invariants". An assertion will always be
True if the program is bug-free, no matter what the user might throw at
it; it's not the same as validation.
 
P

Paul Rubin

Scott David Daniels said:
And I curse such uses, since I don't get to see the troublesome value,
or why it is troublesome. In the above case, y = sqrt(x) at least
raises ValueError('math domain error'), which is more information than
you are providing.

How about:
if x >= 0: raise ValueError('x = %r not allowed (negative)?' % x)

Better still in these situations is to throw to pdb.
But yes, you can put a formatted string in a check message.
 
K

kj

In said:
kj said:
sense = cmp(func(hi), func(lo))
assert sense != 0, "func is not strictly monotonic in [lo, hi]"
bisection search usually just requires the function to be continuous
and to have its value cross the target somewhere between the endpoints,
not be monotonic.

Try the algorithm I posted with lo = -pi/4, hi = 2*pi, func = cos,
target = -1, and see what you get...
I do think you should support that case, under the "do 'nothing'
gracefully" principle.

You keep missing the point that this is an *internal* *helper*
*convenience* function, meant to abstract away common logic from
a handful of places and thus eliminate some code repetition within
a module. It is *not* a library function intended to be called
from elsewhere. So talk of "supporting" anything is besides the
point. Any internal use of this function that applies it to a
non-strictly-monotonic function is, by assumption, an error.

kj
 
K

kj

Yes, that implication is absolutely valid. The purpose of assert
statements is to debug the code, by checking for conditions that are
supposed to be impossible.

Precisely. As I've stated elsewhere, this is an internal helper
function, to be called only a few times under very well-specified
conditions. The assert statements checks that these conditions
are as intended. I.e. they are checks against the module writer's
programming errors.
 
K

kj

In said:
Technically these are known as "invariants". An assertion will always be
True if the program is bug-free, no matter what the user might throw at
it; it's not the same as validation.

What *user* are you talking about??? I've stated a bazillion times
that this function is meant to be called only from within this
module.
 
S

Simon Forman

This implies that code that uses *any* assert statement (other than
perhaps the trivial and meaningless ones like "assert True") is
liable to break, because whatever it is that these assert statements
are checking "on some occasions, ... would go unchecked, potentially
breaking your code."

I'm beginning to think that the original "precept" was simply "cargo
cult," i.e. one of those rules that are parroted without being
fully understood.

As I wrote in my original post, the function I posted was an internal
("helper") function, not to be used outside of the file.  This fact
was further (ahem) underscored by the leading underscore in the
function's name.  Under these circumstances, the assert statements
seem to me perfectly in keeping with the intended use for them.

kj

Assertions are for you, the programmer, to check that your
understanding of the code is correct (or not, i.e. if the assertion
fails.)

It's unfortunately not uncommon to see code where the assert statement
is used as an integral part of the processing, or, in other words,
where running the code with '-O' will cause changes in the way the it
runs.

In the code you posted, the assert statements seem to be checking that
other code/algorithms aren't (will never) pass that function "out of
bounds" arguments. Perfectly valid IMO. Since it's an internal
helper function you can (should) know that client code will /always/
call it with valid values, the asserts simply make your intuition or
knowledge explicit in a way that will show up dramatically if you're
wrong about that (i.e. if there's some problem elsewhere in the code
feeding that function.)

Assertions should never fail, which is why you can (or should be able
to without breaking your code) remove them entirely and know that your
program will run identically. That's why folks are quick to jump on
cases where assertions are used as control-flow constructs. (In your
function, however, they aren't.)

In a sense, assertions /are/ meaningless, except to the programmer.

FWIW, you can use "if __debug__:" and put anything you like in the
statement suite and the whole if statement goes away when run with '-
O'. Sort of a "super-assert".

Regards,
~Simon
 
P

Paul Rubin

kj said:
Try the algorithm I posted with lo = -pi/4, hi = 2*pi, func = cos,
target = -1, and see what you get...

Sorry, my comment was poorly phrase. Bisection search usually is
phrased in terms of solving f(x)=0 where f is continuous and if the
initial endpoints are a and b, then f(a) and f(b) have opposite sign.
You keep missing the point that this is an *internal* *helper*
*convenience* function, ... Any internal use of this function that
applies it to a non-strictly-monotonic function is, by assumption,
an error.

In that case there are better algorithms you can use. But didn't this
thread start out asking what version of the code was best suited for
presentation to students? In that case, the function's preconditions
should be stated explicitly and not be narrower than necessary.
 
D

Dennis Lee Bieber

Precisely. As I've stated elsewhere, this is an internal helper
function, to be called only a few times under very well-specified
conditions. The assert statements checks that these conditions
are as intended. I.e. they are checks against the module writer's
programming errors.

This implies that somewhere in said module you must be doing
validation of "end user" inputs -- but you've not shown that; you've
just presented a raw snippet of code, whether internal or not, which has
a strong smell of validating "end user" input data via assertions.

Furthermore, if this is supposed to be part of a module supplied to
students to assist in some exercise... why the concern for clarity vs
reuse... It would be a black box to the students; they invoke your
primary function and get some result back... OTOH; if the intent is for
them to study the module code as supplied, wouldn't it have been prudent
to supply "us" the entire module too?

From your initial post:
I'm will be teaching a programming class to novices,

Do you really want to inflict novices with the subtleties of when
"assert" is a proper structure to use? At least, not without giving
examples of what it applies to... You state the function is an internal
utility to the module -- does the module then include both a flawed main
function and a correct main function? By this, I mean something like one
version of the main entry point which DOES do validation of end-user
input, and a second version which skips the end-user input validation --
both then calling this utility function. Explain that the assertions are
part of a specification for the utility which states that end-user input
"... has been validated prior to calling ... " the utility. And then
demonstrate by running the same set of good and not-good inputs through
both versions of the main entry point? (Now you are getting down to
requirements specification and/or use case documentation... again
something rather over the head for novices, no?)

Are you teaching "intro to programming in Python" or "intro to
software analysis and design" (the latter, in my view, should be
language neutral, and heavy on documentation of what components are
responsible for which actions and maybe even how to perform them). Even
the convention of the "_" for "internal/private use" is something you'd
have to explain to them. Novice programmers will likely have enough
problems with the evaluation of boolean conditionals in "if" and "while"
statements, looping, and how "x = x + 1" is NOT an algebraic
impossibility (while at the same time "x = x * 1" is algebraic
truth){even accounting for the fact that in Python, the algebraic
versions would be written with "=="}

Dumping an undocumented utility function on novices for study is not
my idea of a learning experience... As a final exam, maybe...
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
S

Steven D'Aprano

What *user* are you talking about??? I've stated a bazillion times that
this function is meant to be called only from within this module.

In that case, you would be the user, and like any user, you might
accidentally call the function with invalid data.

I believe this discussion started because you are presenting this as code
for novices. In that case, it is absolutely important that you start off
by teaching them the Right Way to do things. As a general rule, the Right
Way is to do an explicit test and raise rather than use assert.

In production code, "internal use only" code is a grey area where assert
is sometimes justified (although I'll point out that in practice, code
written for internal use only has a habit of being called by others). But
you're not writing production code, you're writing *teaching code*, where
even more important than code correctness is user education.
 
P

Pablo Torres N.

       Do you really want to inflict novices with the subtleties of when
"assert" is a proper structure to use?

I'll second that. This very thread is proof that assertions are polemic, so
maybe you (kj) should just save your student's sanity and stick to good old
conditionals :)

As for your initial question, I think, thirty four emails after, that yes, your
function is a bit too clever and you should sacrifice some generality in order
to make it more readable.
 
D

David Smith

kj said:
In said:
kj said:
sense = cmp(func(hi), func(lo))
assert sense != 0, "func is not strictly monotonic in [lo, hi]"
bisection search usually just requires the function to be continuous
and to have its value cross the target somewhere between the endpoints,
not be monotonic.

Try the algorithm I posted with lo = -pi/4, hi = 2*pi, func = cos,
target = -1, and see what you get...
I do think you should support that case, under the "do 'nothing'
gracefully" principle.

You keep missing the point that this is an *internal* *helper*
*convenience* function, meant to abstract away common logic from
a handful of places and thus eliminate some code repetition within
a module. It is *not* a library function intended to be called
from elsewhere. So talk of "supporting" anything is besides the
point. Any internal use of this function that applies it to a
non-strictly-monotonic function is, by assumption, an error.

kj

First, let me say *I got the point*. I use asserts, but only in unit
testing where I want to test the result of some action for correctness.
In the course of programming product code, I personally don't think
they should ever be used exactly for the reasons everyone else is
pointing out. They can be disabled with the -O option and that changes
the program's behavior in ways that could break in production.

If you insist on teaching the assert statement, teach it in the context
of writing unit testing code. Its an extremely valuable skill.

--David
 
A

Andre Engels

In general, code clarity is more important than reusability.
Unfortunately, many novice programmers have the opposite impression. I
have seen too much convoluted code written by beginners who try to
make the code generic. Writing simple, clear, to-the-point code is
hard enough as it is, even when not aiming at making it reusable.

If in the future you see an opportunity to reuse the code, then and
only then is the time to make it generic.

Not just that, when you actually get to that point, making simple and
clear code generic is often easier than making
complicated-and-supposedly-generic code that little bit more generic
that you need.
 
J

Jean-Michel Pichavant

kj said:
I've rewritten it like this:

sense = cmp(func(hi), func(lo))
assert sense != 0, "func is not strictly monotonic in [lo, hi]"

Thanks for your feedback!

kj

As already said before, unlike other languages, sense in english does
**not** mean direction. You should rewrite this part using a better
name. Wrong informations are far worse than no information at all.

JM
 
T

Tim Rowe

2009/7/4 kj said:
Precisely.  As I've stated elsewhere, this is an internal helper
function, to be called only a few times under very well-specified
conditions.  The assert statements checks that these conditions
are as intended.  I.e. they are checks against the module writer's
programming errors.

Good for you. I'm convinced that you have used the assertion
appropriately, and the fact that so many here are unable to see that
looks to me like a good case for teaching the right use of assertions.
For what it's worth, I read assertions at the beginning of a procedure
as part of the specification of the procedure, and I use them there in
order to document the procedure. An assertion in that position is for
me a statement to the user of the procedure "it's your responsibility
to make sure that you never call this procedure in such a way as to
violate these conditions". They're part of a contract, as somebody
(maybe you) pointed out.

As somebody who works in the safety-critical domain, it's refreshing
to see somebody teaching students to think about the circumstances in
which a procedure can legitimately be called. The hostility you've
received to that idea is saddening, and indicative of why there's so
much buggy software out there.
 
D

David Niergarth

I remember in college taking an intro programming class (C++) where
the professor started us off writing a program to factor polynomials;
he probably also incorporated binary search into an assignment. But
people don't generally use Python to implement binary search or factor
polynomials so maybe you should start with a problem more germane to
typical novice users (and less algorithm-y). Wouldn't starting them
off with string processing or simple calculations be a practical way
to get comfortable with the language?

--David
 
S

Steven D'Aprano

kj said:
I've rewritten it like this:

sense = cmp(func(hi), func(lo))
assert sense != 0, "func is not strictly monotonic in [lo, hi]"

Thanks for your feedback!

kj
As already said before, unlike other languages, sense in english does
**not** mean direction. You should rewrite this part using a better
name. Wrong informations are far worse than no information at all.

Absolutely.

From Webster's Dictionary:

8. (Geom.) One of two opposite directions in which a line,
surface, or volume, may be supposed to be described by the
motion of a point, line, or surface.
[1913 Webster]


And from WordNet:

2: the meaning of a word or expression; the way in which a word
or expression or situation can be interpreted

Both meanings are relevant to the way KJ is using the word. Please take
your own advice and stop giving wrong information. As a native English
speaker, I had no difficulty understanding the meaning of "sense" in the
sense intended by KJ.
 
S

Steven D'Aprano

Good for you. I'm convinced that you have used the assertion
appropriately, and the fact that so many here are unable to see that
looks to me like a good case for teaching the right use of assertions.
For what it's worth, I read assertions at the beginning of a procedure
as part of the specification of the procedure, and I use them there in
order to document the procedure. An assertion in that position is for me
a statement to the user of the procedure "it's your responsibility to
make sure that you never call this procedure in such a way as to violate
these conditions". They're part of a contract, as somebody (maybe you)
pointed out.

As somebody who works in the safety-critical domain, it's refreshing to
see somebody teaching students to think about the circumstances in which
a procedure can legitimately be called. The hostility you've received to
that idea is saddening, and indicative of why there's so much buggy
software out there.

LOL.

Maybe the reason for "so much buggy software" is that people
inappropriately use assert, thus changing the behaviour of code depending
on whether it is run with the -O flag or not.

I don't know what "hostility" you're seeing. The only hostility I'm
seeing is from the OP, which is bizarre considering that he asked for
advice and we gave it. What I see is a bunch of people concerned that the
OP is teaching novices a bad habit, namely, using assert for error
checking. He claims -- angrily and over and over again -- that in his
code, the assertions should never fail. Great. Good for him for knowing
when to use assert. But are the novices going to learn that lesson, or
will they simply learn "use assert for error checking"?
 

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

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top