"no variable or argument declarations are necessary."

  • Thread starter James A. Donald
  • Start date
S

Steve Holden

+1

Some people just don't get the simple fact that declarations are
essentially kind of unit test you get for free (almost), and the compiler
is a testing framework for them.
Hmm. Presumably introspection via getattr() is way too dangerous, then?
Might as well throw the function away ...

regards
Steve
 
S

Steven D'Aprano

Fine, it is still better than python which will crash each time
one of these is encountered.

Python doesn't crash when it meets an undeclared variable. It raises an
exception.

This lets you do things like:

try:
False
except NameError:
print "bools are not defined, making fake bools..."

False = 0
True = not False

def bool(obj):
if obj: return True
else: return False

# not identical to real bools, but close enough to fake it (usually)

So? He has to write all those lines of code too.

People often promote unittesting here. Writing all those unittest is
an added burden too. But people think this burden is worth it.

Yes, but there is no evidence that pre-declaration of variables is a
burden worth carrying. It doesn't catch any errors that your testing
wouldn't catch anyway.

I think writing declaration is also worth it. The gain is not as
much as with unittesting but neither is the burden, so that
balances out IMO

Speaking as somebody who spent a long time programming in Pascal, I got
heartedly sick and tired of having to jump backwards and forwards from
where I was coding to the start of the function to define variables.

It got to the stage that sometimes I'd pre-define variables I thought I
might need, intending to go back afterwards and delete the ones I didn't
need. When the programmer is having to to jump through hoops to satisfy
the compiler, there is something wrong.

Well maybe we should remove all those comments from code too,
because all it does is add more lines for people to read.

Well-written comments should give the reader information which is not in
the code. If the comment gives you nothing that wasn't obvious from the
code, it is pointless and should be removed.

Variable declarations give the reader nothing that isn't in the code. If I
write x = 15, then both I and the compiler knows that there is a variable
called x. It is blindingly obvious. Why do I need to say "define x" first?

Pre-defining x protects me from one class of error, where I typed x
instead of (say) n. That's fine as far as it goes, but that's not
necessarily an _error_. If the typo causes an error, e.g.:

def spam(n):
return "spam " * x # oops, typo

then testing will catch it, and many other errors as well. Declaring the
variable doesn't get me anything I wouldn't already get.

But if it doesn't cause an error, e.g.:

def spam(n):
if n:
return "spam " * n
else:
x = 0 # oops, typo
return "spam " * n

This may never cause a failure, since n is always an integer. Since my
other testing guarantees that n is always an integer, it doesn't matter
that I've created a variable x that doesn't get used. Yes, it would be
nice for the compiler to flag this, but if the cost of that niceness is to
have to define every single variable, I can live without it.

Which is good. Just as you have to keep the unittests in line as code
changes over time.

That is not the same at all. Changing variable declarations needs to be
done every time you modify the internal implementation of a function.
Changing the unittests, or any other testing for that matter, only needs
to be done when you change the interface.

In principle, if you have an interface designed up front before you write
any code, you could write all your tests at the start of the project and
never change them again. You can't do that with variable declarations,
since every time you change the implementation you have to change the
declarations.
 
G

Guest

errors and not rely on the compiler. No compiler will catch this error:

x = 12.0 # feet
# three pages of code
y = 15.0 # metres
# three more pages of code
distance = x + y
if distance < 27:
fire_retro_rockets()

And lo, one multi-billion dollar Mars lander starts braking either too
early or too late. Result: a new crater on Mars, named after the NASA
employee who thought the compiler would catch errors.

So, I guess, you have a spare Mars lander especially for unit-testing? :)
 
S

Steven D'Aprano

Some people just don't get the simple fact that declarations are
essentially kind of unit test you get for free (almost), and the compiler
is a testing framework for them.

No. Some people just don't get it that declarations aren't almost
free, because they cost a lot in human labour, and that they give you
practically nothing that your unit testing wouldn't give you anyway.
 
G

Guest

Hmm. Presumably introspection via getattr() is way too dangerous, then?

Sure, it is dangerous. Not a showstopper, though.

I mean, the absolute address access in the C is too dangerous, yes, but it
doesn't make declarations in C any less useful.
 
M

Mike Meyer

Steven D'Aprano said:
On Mon, 03 Oct 2005 06:59:04 +0000, Antoon Pardon wrote:
Declared variables have considerable labour costs, and only marginal
gains. Since the steps you take to protect against other errors will also
protect against mistyping variables, declarations of variables is of
little practical benefit.

As far as I can tell, this is as much hearsay and personal experience
as the alternate claim that not having them costs you lots of
debugging time and errors. If anyone has pointers to real research
into this area (I've heard the TRAK folks did some, but haven't been
able to turn any up), I'd love to hear it.

My gut reaction is that it's a wash. The time taken to declare
variables in well-written code in a well-designed language - meaning
the declarations and use will be close together - isn't all that
great, but neither are the savings.

The other win from declaring variables is that if you compile the code
you can make assumptions about the types of variables and thus save
doing (some of) the type determination at run time. But there are type
inferencing languages and systems - and they've been around since the
70s - that can do that for you without having to declare the
variables, so that doesn't buy you much.

If I'm going to get compiler support for semantic checking like this,
I want it to serious levels. I want function pre/post conditions
checked. I want loop and class invariant checked. I want subsumption
in my inheritance tree. Nuts - I want a complete, well-designed
inheritance tree. Duck typing is great stuff, but if I'm going to be
doing the work to declare everything, I want *everything* that can be
checked checked.

<mike
 
R

Ron Adam

x = 12.0 # feet
# three pages of code
y = 15.0 # metres
# three more pages of code
distance = x + y
if distance < 27:
fire_retro_rockets()

And lo, one multi-billion dollar Mars lander starts braking either too
early or too late. Result: a new crater on Mars, named after the NASA
employee who thought the compiler would catch errors.

Yes, and a reserved position in the unemployment line as well, I would bet.
Declared variables have considerable labour costs, and only marginal
gains. Since the steps you take to protect against other errors will also
protect against mistyping variables, declarations of variables is of
little practical benefit.

Also checking types is not the same as checking values.

In most cases where critical code is used you really want value testing
not type checking. This is where self validating objects are useful and
there is nothing preventing anyone from using them in Python.

Cheers,
Ron
 
M

marduk

egold = 0:
while egold < 10:
if test():
ego1d = egold + 1

Both pylint and pychecker pick this up. I wrapped the code in a
function (to prevent importing from running in an infinite loop) and ran
both pylint and pychecker:

plyint: W: 5:myfunc: Unused variable 'ego1d'
pychecker: test.py:4: Local variable (ego1d) not used


I make a habit to run pylint or pychecker on my code often. They pick
up a lot of stuff like unused variables, etc.

But you can also do this:

/* initialize variables i'm gonna use */
int vara = 0;
int varb = 0;
while (vara < 10) {
varb = vara + 1;
}

So we can make a similar mistake in C if you type the wrong (declared)
variable name. Moreover, "gcc -Wall" did not report the "unused"
variable so it might be even more difficult to track down the problem.
 
A

Antoon Pardon

Op 2005-10-03 said:
Python doesn't crash when it meets an undeclared variable. It raises an
exception.

Your nit-picking. For the sake of finding misspelled variables the
difference is irrelevant.
Yes, but there is no evidence that pre-declaration of variables is a
burden worth carrying. It doesn't catch any errors that your testing
wouldn't catch anyway.

Maybe not, but it may catch them earlier and may make them easier
to recognize.

Declarations also allow easier writable closures. Since the declaration
happens at a certain scope, the run time can easily find the correct
scope when a variable is rebound.

They also relieve a burden from the run-time, since all variables
are declared, the runtime doesn't has to check whether or not
a variable is accesible, it knows it is.

And if you provide type information with the declaration, more
efficient code can be produced.
Speaking as somebody who spent a long time programming in Pascal, I got
heartedly sick and tired of having to jump backwards and forwards from
where I was coding to the start of the function to define variables.

I have programmed a lot in Pascal too and Modula II and other languages.
I never found declarations that much a burden. That you got heartedly sick
of having to use declarations says very little about declarations and
says more about you.

I think language matters shouldn't be setlled by personal preferences.
It got to the stage that sometimes I'd pre-define variables I thought I
might need, intending to go back afterwards and delete the ones I didn't
need. When the programmer is having to to jump through hoops to satisfy
the compiler, there is something wrong.

Maybe it was your way of working. I never thought I had to go through
hoops to satisfy the compiler. You have to satisfy that compilor anyway,
for the moment I have more problems with colons that have to be put
after an "if", "else" etc than I ever had with declarations.
Well-written comments should give the reader information which is not in
the code. If the comment gives you nothing that wasn't obvious from the
code, it is pointless and should be removed.

Variable declarations give the reader nothing that isn't in the code. If I
write x = 15, then both I and the compiler knows that there is a variable
called x. It is blindingly obvious. Why do I need to say "define x" first?

Because it isn't at all obvious at which scope that x is. Sure you can
define your language that rebinding is always at local scope, but that
you need to define it so, means it isn't that obvious.

Also the question is not whether or not there is a variable x, the
quesntions whether or not there should be a variable x. That is not
at all that obvious when you write x = 15.
Pre-defining x protects me from one class of error, where I typed x
instead of (say) n. That's fine as far as it goes, but that's not
necessarily an _error_. If the typo causes an error, e.g.:
def spam(n):
return "spam " * x # oops, typo

then testing will catch it, and many other errors as well. Declaring the
variable doesn't get me anything I wouldn't already get.

Yes it would have caught this error even before you had to begin
testing.
But if it doesn't cause an error, e.g.:

def spam(n):
if n:
return "spam " * n
else:
x = 0 # oops, typo
return "spam " * n

This may never cause a failure, since n is always an integer. Since my
other testing guarantees that n is always an integer,

This is naive. Testing doesn't guarantee anything. If this is what you
think about testing, then testing gives you a false impression of
security. Maybe we should drop testing.
it doesn't matter
that I've created a variable x that doesn't get used. Yes, it would be
nice for the compiler to flag this, but if the cost of that niceness is to
have to define every single variable, I can live without it.

But we should decide what language features are usefull and which are
not by what some individual can or can't live without.
That is not the same at all. Changing variable declarations needs to be
done every time you modify the internal implementation of a function.

Well now I'll nitpick too. No you don't. You only have to do so
if this modification needs other variables.
Changing the unittests, or any other testing for that matter, only needs
to be done when you change the interface.

Which will be often enough.
In principle, if you have an interface designed up front before you write
any code, you could write all your tests at the start of the project and
never change them again. You can't do that with variable declarations,
since every time you change the implementation you have to change the
declarations.

This argument has very little value since all you are saying is that
if you were smart enought to choose the right interface to begin with,
you won't have to change interface related stuff like unittests, while
if you were not that smart in choosing your implementation, you will
have to change implemantation related stuff like declarations.
 
A

Antoon Pardon

Op 2005-10-03 said:
Well I'm a bit getting sick of those references to standard idioms.
There are moments those standard idioms don't work, while the
gist of the OP's remark still stands like:

egold = 0:
while egold < 10:
if test():
ego1d = egold + 1

for item in [x for x in xrange(10) if test()]:

But it isn't about the idioms. It is about the trade-offs. Python allows
you to do things that you can't do in other languages because you
have much more flexibility than is possible with languages that
require you to declare variables before using them. The cost is, some
tiny subset of possible errors will not be caught by the compiler. But
since the compiler can't catch all errors anyway, you need to test for
errors and not rely on the compiler. No compiler will catch this error:

x = 12.0 # feet
# three pages of code
y = 15.0 # metres
# three more pages of code
distance = x + y
if distance < 27:
fire_retro_rockets()

And lo, one multi-billion dollar Mars lander starts braking either too
early or too late. Result: a new crater on Mars, named after the NASA
employee who thought the compiler would catch errors.

Using (unit)tests will not guarantee that your programs is error free.

So if sooner or later a (unit)tested program causes a problem, will you
then argue that we should abondon tests, because tests won't catch
all errors.
 
S

Steven D'Aprano

Mike said:
As far as I can tell, this is as much hearsay and personal experience
as the alternate claim that not having them costs you lots of
debugging time and errors. If anyone has pointers to real research
into this area (I've heard the TRAK folks did some, but haven't been
able to turn any up), I'd love to hear it.

Sorry, I have no hard research to point to. If I did, I
would have referenced it.
My gut reaction is that it's a wash. The time taken to declare
variables in well-written code in a well-designed language - meaning
the declarations and use will be close together - isn't all that
great, but neither are the savings.

It isn't the typing time to declare variables, it is
the context switching. You're focused on implementing
an algorithm, realise you need to declare another
variable, your brain does a mini-context switch, you
scroll up to the declaration section, you declare it,
you scroll back to where you were, and now you have to
context switch again.

You've gone from thinking about the implementation of
the algorithm to thinking about how to satisfy the
requirements of the compiler. As context switches go,
it isn't as big as the edit-compile-make-run method of
testing, but it is still a context switch.

Or you just code without declaring, intending to go
back and do it later, and invariably forget.

[snip]
If I'm going to get compiler support for semantic checking like this,
I want it to serious levels. I want function pre/post conditions
checked. I want loop and class invariant checked. I want subsumption
in my inheritance tree. Nuts - I want a complete, well-designed
inheritance tree. Duck typing is great stuff, but if I'm going to be
doing the work to declare everything, I want *everything* that can be
checked checked.

We like to think of programming as a branch of
engineering. It isn't, not yet.

I've worked for architects who were involved in some
major engineering projects. One of the things I learnt
is that there are times when you need to specify
objects strictly. When only a 5% titanium stainless
steel alloy will do, then *only* a 5% titanium
stainless steel alloy will do. That's when you want
strict type-checking.

But the rest of the time, just about any stainless
steel will do, and sometimes you don't even care if it
is steel -- iron will do the job just as well. If the
nail is hard enough to be hammered into the wood, and
long enough to hold it in place, it is good enough for
the job. That's the real-world equivalent of duck typing.

Static typed languages that do all those checks are the
equivalent of building the space shuttle, where
everything must be specified in advance to the nth
degree: it must not just be a three inch screw, but a
three inch Phillips head anti-spark non-magnetic
corrosion-resistant screw rated to a torque of
such-and-such and capable of resisting vaccuum welding,
extreme temperatures in both directions, and exposure
to UV radiation. For that, you need a compiler that
will do what Mike asks for: check *everything*.

I don't know whether there are languages that will
check everything in that way. If there aren't, then
perhaps there should be. But Python shouldn't be one of
them. Specifying every last detail about the objects
making up the space shuttle is one of the reasons why
it costs umpty-bazillion dollars to build one, and
almost as much to maintain it -- and it still has a
safety record worse than most $10,000 cars.

That level of specification is overkill for most
engineering projects, and it's overkill for most
programming projects too. It is all well and good to
tear your hair and rip your clothes over the
possibility of the language allowing some hidden bug in
the program, but get over it: there is always a trade
off to be made between cost, time and risk of bugs.
Python is a language that makes the trade off one way
(fast development, low cost, high flexibility, moderate
risk) rather than another (slow development, high cost,
low flexibility, low risk).

We make the same trade offs in the real world too: the
chair you sit on is not built to the same level of
quality as the space shuttle, otherwise it would cost
$100,000 instead of $100. Consequently, sometimes
chairs break. Deal with it.
 
F

Frithiof Andreas Jensen

bruno modulix said:
"horrified" ???

Ok, so I'll give you more reasons to be 'horrified':
- no private/protected/public access restriction - it's just a matter of
conventions ('_myvar' -> protected, '__myvar' -> private)
- no constants (here again just a convention : a name in all uppercase
is considered a constant - but nothing will prevent anyone to modify it)
- possibility to add/delete attributes to an object at runtime
- possibility to modify a class at runtime
- possibility to change the class of an object at runtime
- possibility to rebind a function name at runtime
....

If you find all this horrifying too, then hi-level dynamic languages are
not for you !-)

Not to mention that since the O.P. seem to assume that the compiler will
protect against deliberate subversion by evil programmers then he must be
further "horrified" to learn that, although it is harder to do the above in
f.ex. C++, it is not at all impossible, a carefully crafted pointer or a
little devious sub-classing goes a long way.

If all else fails, The humble Linker holds the Word of Power!

Tampering with linking is both the easiest way to subvert code reviews,
language checks and boundaries and also the hardest to discover because the
tampering will be buried somewhere deep inside the build process, the part
that never, ever gets reviewed because it is automated anyway and too
complex entirely so nobody sane will actually mess with it once it "works"
i.e. produces runnable code!.

Finally, given proper permissions, one can of course re-link the binary
executable, should the occasion merit. Like when one needs HIP in Telnet
which is an absolute b****rd to build on a modern Linux box. (Somebody build
that *once* in maybe 1978, I think ;-) One can replace classes in Jar
archives too - possibly one can even get the Java runtime to load the "new
version" of a jar archive in preference to the shipped one ...


I.O.W:

Superficially, the compile-time checks of Java and C++ provides some checks
& boundaries but it comes at the expense of much more machinery with many
more intricate movable parts than *also* can be interfered with (or broken).

Python is simple and self-contained, thus it is pretty obvious - or at least
not too difficult, to check what *actually* goes on with an application.

If there is no trust, nothing can be done safely. If there is trust, then
most of the percieved safety just get in the way of work.
 
A

Antoon Pardon

Op 2005-10-04 said:
Sorry, I have no hard research to point to. If I did, I
would have referenced it.


It isn't the typing time to declare variables, it is
the context switching. You're focused on implementing
an algorithm, realise you need to declare another
variable, your brain does a mini-context switch, you
scroll up to the declaration section, you declare it,
you scroll back to where you were, and now you have to
context switch again.

You've gone from thinking about the implementation of
the algorithm to thinking about how to satisfy the
requirements of the compiler. As context switches go,
it isn't as big as the edit-compile-make-run method of
testing, but it is still a context switch.

Nobody forces you to work this way. You can just finish
your algorithm and declare your variables afterwards.

Besides likewise things can happen in python. If you
suddenly realise your class needs an instance variable,
chances are you will have to add initialisation code
for that instance variable in the __init__ method.
So either you do a context switch from implementing
whatever method you were working on to the initialisation
in __init__ and back or you just code without
initialisation, intending to go back an do it later.
Or you just code without declaring, intending to go
back and do it later, and invariably forget.

What's the problem, the compilor will allert you
to your forgetfullness and you can then correct
them all at once.
 
P

Paul Rubin

Antoon Pardon said:
What's the problem, the compilor will allert you
to your forgetfullness and you can then correct
them all at once.

Thiat in fact happens to me all the time and is an annoying aspect of
Python. If I forget to declare several variables in C, the compiler
gives me several warning messages and I fix them in one edit. If I
forget to initialize several variables in Python, I need a separate
test-edit cycle to hit the runtime error for each one.
 
S

Steve Holden

Paul said:
Thiat in fact happens to me all the time and is an annoying aspect of
Python. If I forget to declare several variables in C, the compiler
gives me several warning messages and I fix them in one edit. If I
forget to initialize several variables in Python, I need a separate
test-edit cycle to hit the runtime error for each one.

Well I hope you aren't suggesting that declaring variables makes it
impossible to forget to initalise them. So I don;t really see the
relevance of this remark, since you simply add an extra run to fix up
the "forgot to declare" problem. After that you get precisely one
runtime error per "forgot to initialize".

regards
Steve
 
A

Antoon Pardon

Op 2005-10-04 said:
Well I hope you aren't suggesting that declaring variables makes it
impossible to forget to initalise them. So I don;t really see the
relevance of this remark, since you simply add an extra run to fix up
the "forgot to declare" problem. After that you get precisely one
runtime error per "forgot to initialize".

Declaration and initialisation often go together. So the fixup
to declare is often enough a fixup for the initialisation too.
 
I

Istvan Albert

What can one do to swiftly detect this type of bug?

While I can only speak from my own experience I can't remember a
single instance where this type of bug caused any kind of serious
problem. IMHO these are very trivial errors, that get caught
immediately and I would not even qualify them as bugs, more like
typos, spelling mistakes, etc.

Real bugs are a lot more insidious than that, and they might even occur
more frequently if there was type checking ... since it might even
lead to longer code

just my $0.01

Istvan.
 
B

Brian Quinlan

Have those of you who think that the lack of required declarations in
Python is a huge weakness given any thought to the impact that adding
them would have on the rest of the language? I can't imagine how any
language with required declarations could even remotely resemble Python.

And if you want to use such a different language, wouldn't a different
existing language better fit your needs...?

Cheers,
Brian
 
M

Mike Meyer

Antoon Pardon said:
Declarations also allow easier writable closures. Since the declaration
happens at a certain scope, the run time can easily find the correct
scope when a variable is rebound.

If it happens at runtime, then you can do it without declarations:
they're gone by then. Come to think of it, most functional languages -
which are the languages that make the heaviest use of closures - don't
require variable declarations.
They also relieve a burden from the run-time, since all variables
are declared, the runtime doesn't has to check whether or not
a variable is accesible, it knows it is.

Not in a dynamic language. Python lets you delete variables at run
time, so the only way to know if a variable exists at a specific
point during the execution of an arbitrary program is to execute the
program to that point.
And if you provide type information with the declaration, more
efficient code can be produced.

Only in a few cases. Type inferencing is a well-understood
technology, and will produce code as efficient as a statically type
language in most cases.
I think language matters shouldn't be setlled by personal preferences.

I have to agree with that. For whether or not a feature should be
included, there should either be a solid reason dealing with the
functionality of the language - meaning you should have a set of use
cases showing what a feature enables in the language that couldn't be
done at all, or could only be done clumsily, without the feature.

Except declarations don't add functionality to the language. They
effect the programing process. And we have conflicting claims about
whether that's a good effect or not, all apparently based on nothing
solider than personal experience. Which means the arguments are just
personal preferences.

Until someone does the research to provide hard evidence one way or
another, that's all we've got to work with. Which means that languages
should exist both with and with those features, and if one sides
experiences generalize to the population at large, they alternative
languages will die out. Which hasn't happened yet.
But we should decide what language features are usefull and which are
not by what some individual can or can't live without.

Um - that's just personal preference (though I may have misparsed your
sentence). What one person can't live without, another may not be able
to live with. All that means is that they aren't likely to be happy
with the same programming language. Which is fine - just as no
programming language can do everything, no programming language can
please everyone.

Antoon, at a guess I'd say that Python is the first time you've
encountered a dynamnic language. Being "horrified" at not having
variable declarations, which is a standard feature of such languages
dating back to the 1950s, is one such indication.

Dynamic languages tend to express a much wider range of programming
paradigms than languages that are designed to be statically
compiled. Some of these paradigms do away with - or relegate to the
level of "ugly performance hack" - features that someone only
experienced with something like Pascal would consider
essential. Assignment statements are a good example of that.

Given these kinds of differences, prior experience is *not* a valid
reason for thinking that some difference must be wrong. Until you have
experience with the language in question, you can't really decide that
some feature being missing is intolerable. You're in the same position
as the guy who told me that a language without a goto would be
unusable based on his experience with old BASIC, FORTRAN IV and
assembler.

Pick one of the many languages that don't require declarations. Try
writing a code in them, and see how much of a problem it really is in
practice, rather than trying to predict that without any
information. Be warned that there are *lots* of variations on how
undeclared variables are treated when referenced. Python raises
exceptions. Rexx gives them their print name as a value. Other
languages do other things.

<mike
 

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,780
Messages
2,569,611
Members
45,279
Latest member
LaRoseDermaBottle

Latest Threads

Top