Python from Wise Guy's Viewpoint

D

Duncan Booth

Yes -- which is exactly why many non-programmers would prefer the
parentheses-less notation -- with more obvious names of course;-).
E.g.:
emitwarning URGENT "meltdown imminent!!!"
DOES look nicer to non-programmers than
emitwarning(URGENT, "meltdown imminent!!!")

Indeed, such languages as Visual Basic and Ruby do allow calling
without parentheses, no doubt because of this "nice look" thing.

I know we are agreed that Visual Basic is fundamentally broken, but it
might be worth pointing out the massive trap that it provides for
programmers in the subtle difference between:

someProcedure x

and

someProcedure(x)

and

call someProcedure(x)

If 'someProcedure' is a procedure taking a single reference parameter, and
modifying that parameter, then the first and third forms will call the
procedure and modify 'x'. The second form on the other hand will call the
procedure and without any warning or error will simply discard the
modifications leaving 'x' unchanged.
 
A

Alex Martelli

Duncan Booth wrote:
...
I know we are agreed that Visual Basic is fundamentally broken, but it
might be worth pointing out the massive trap that it provides for

I'm not sure, but I think that's one of the many VB details changed
(mostly for the better, but still, _massive_ incompatibility) in the
current version (VB7 aka VB.NET) wrt older ones (VB6, VBA, etc).


Alex
 
J

Joachim Durchholz

Pascal said:
AFAIK, while this parameter was out of range, there was no
instability and the rocket was not uncontrolable.

Actually, the rocket had started correcting its orientation according to
the bogus data, which resulted in uncontrollable turning. The rocket
would have broken into parts in an uncontrollable manner, so it was
blewn up.
(The human operator decided to press the emergency self-destruct button
seconds before the control software would have initiated self destruct.)
My point. This "can't possibly happen" failure did happen, so
clearly it was not a "can't possibly happen" physically, which means
that the problem was with the software. We know it, but what I'm
saying is that a smarter software could have deduced it on fly.

No. The smartest software will not save you from human error. It was a
specification error.
The only way to detect this error (apart from more testing) would have
been to model the physics of the rocket, in software, and either verify
the flight control software against the rocket model or to test run the
whole thing in software. (I guess neither of these options would have
been cheaper than the simple test runs that were deliberately omitted,
probably on the grounds of "we /know/ it works, it worked in the Ariane 4".)
We all agree that it would be better to have a perfect world
and perfect, bug-free, software. But since that's not the case,
I'm saying that instead of having software that behaves like simple
unix C tools, where as soon as there is an unexpected situation,
it calls perror() and exit(), it would be better to have smarter
software that can try and handle UNEXPECTED error situations,
including its own bugs. I would feel safer in an AI rocket.

This all may be true, but you're solving problems that didn't cause the
Ariane crash.

Regards,
Jo
 
D

Duncan Booth

Duncan Booth wrote:
...

I'm not sure, but I think that's one of the many VB details changed
(mostly for the better, but still, _massive_ incompatibility) in the
current version (VB7 aka VB.NET) wrt older ones (VB6, VBA, etc).
Yes, I just checked and VB7 now requires parentheses on all argument lists,
so:

someProcedure x

is now illegal.

someProcedure(x)

and

call someProcedure(x)

now do the same thing. The Visual Studio.Net editor will automatically
'correct' the first form into the second (unless you tell it not to).

Of course, while it is less likely to cause a major headache, the confusing
behaviour is still present, just pushed down a level. These are both legal,
but the second one ignores changes to x. At least you are less likely to
type it accidentally.

someProcedure(x)

someProcedure((x))
 
J

Joachim Durchholz

Tim said:
1. f(x,y,z) sucks. f x y z would be much easier to type (see Haskell)
90% of the code is function applictions. Why not make it convenient?

9. Syntax for arrays is also bad [a (b c d) e f] would be better
than [a, b(c,d), e, f]

Agreed with your analysis, except for these two items.

#1 is a matter of opinion, but in general:

- f(x,y) is the standard set by mathematical notation and all the
mainstream programming language families, and is library neutral:
calling a curried function is f(x)(y), while calling an uncurried
function is f(x,y).

Well, in most languages, curried functions are the standard.
This has some syntactic advantages, in areas that go beyond mathematical
tradition. (Since each branch of mathematics has its own traditions,
it's probably possible to find a branch where the functional programming
way of writing functions is indeed tradition *g*)
- "f x y" is unique to the Haskell and LISP families of languages, and
implies that most library functions are curried.

No, Lisp languages require parentheses around the call, i.e.
(f x y)
Lisp does share the trait that it doesn't need commas.
> Otherwise you have a
weird asymmetry between curried calls "f x y" and uncurried calls
which translate back to "f(x,y)".

It's not an asymmetry. "f x y" is a function of two parameters.
"f (x, y)" is a function of a single parameter, which is an ordered pair.
In most cases such a difference is irrelevant, but there are cases where
it isn't.
> Widespread use of currying can lead
to weird error messages when calling functions of many parameters: a
missing third parameter in a call like f(x,y) is easy to report, while
with curried notation, "f x y" is still valid, yet results in a type
other than what you were expecting, moving the error up the AST to a
less useful obvious.

That's right.
On the other hand, it makes it easy to write code that just fills the
first parameter of a function, and returns the result. Such code is so
commonplace that having weird error messages is considered a small price
to pay.
Actually, writing functional code is more about sticking together
functions than actually calling them. With such use, having to write
code like
f (x, ...)
instead of
f x
will gain in precision, but it will clutter up the code so much that I'd
exptect the gain in readability to be little, nonexistent or even negative.
It might be interesting to transform real-life code to a more standard
syntax and see whether my expectation indeed holds.
In general, I'm wary of notations like "f x" that use whitespace as an
operator (see http://www.research.att.com/~bs/whitespace98.pdf).

That was an April Fool's joke. A particularly clever one: the paper
starts by laying a marginally reasonable groundwork, only to advance
into realms of absurdity later on.
It would be unreasonable to make whitespace an operator in C++. This
doesn't mean that a language with a syntax designed for whitespace
cannot be reasonable, and in fact some languages do that, with good
effect. Reading Haskell code is like a fresh breeze, since you don't
have to mentally filter out all that syntactic noise.
The downside is that it's easy to get some detail wrong. One example is
a decision (was that Python?) to equate a tab with eight blanks, which
tends to mess up syntactic structure when editing the code with
over-eager editors. There are some other lessons to learn - but then,
whitespace-as-syntactic-element is a relatively new concept, and people
are still playing with it and trying out alternatives. The idea in
itself is useful, its incarnations aren't perfect (yet).

Regards,
Jo
 
F

Frode Vatvedt Fjeld

Alex Martelli said:
[..] the EXISTING call to foo() will NOT be "affected" by the "del
foo" that happens right in the middle of it, since there is no
further attempt to look up the name "foo" in the rest of that call's
progress. [..]

What this and my other investigations amount to, is that in Python a
"name" is somewhat like a lisp symbol [1]. In particluar, it is an
object that has a pre-computed hash-key, which is why
hash-table/dictionary lookups are reasonably efficient. My worry was
that the actual string hash-key would have to be computed at every
function call, which I believe would slow down the process some 10-100
times. I'm happy to hear it is not so.

[1] One major difference being that Pyhon names are not first-class
objects. This is a big mistake wrt. to supporting interactive
programming in my personal opinion.
As for your worries elsewhere expressed that name lookup may impose
excessive overhead, in Python we like to MEASURE performance issues
rather than just reason about them "abstractly"; which is why Python
comes with a handy timeit.py script to time a code snippet
accurately. [...]

Thank you for the detailed information. Still, I'm sure you will agree
that sometimes reasoning about things can provide insight with
predictive powers that you cannot achieve by mere experimentation.
 
T

Terry Reedy

Frode Vatvedt Fjeld said:
What this and my other investigations amount to, is that in Python a
"name" is somewhat like a lisp symbol [1].

This is true in that names are bound to objects rather than
representing a block of memory.
In particluar, it is an object that has a pre-computed hash-key,

NO. There is no name type. 'Name' is a grammatical category, with
particular syntax rules, for Python code, just like 'expression',
'statement' and many others.

A name *may* be represented at runtime as a string, as CPython
*sometimes* does. The implementation *may*, for efficiency, give
strings a hidden hash value attributre, which CPython does.

For even faster runtime 'name lookup' an implementation may represent
names
as slot numbers (indexes) for a hiddem, non-Python array. CPython
does this (with C pointer arrays) for function locals whenever the
list of locals is fixed at compile time, which is usually. (To
prevent this optimization, add to a function body something like 'from
mymod import *', if still allowed, that makes the number of locals
unknowable until runtime.)

To learn about generated bytecodes, read the dis module docs and use
dis.dis.
For example:.... b=a+1
.... 0 SET_LINENO 1

3 SET_LINENO 2
6 LOAD_FAST 0 (a)
9 LOAD_CONST 1 (1)
12 BINARY_ADD
13 STORE_FAST 1 (b)
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
This says: load (onto stack) first pointer in local_vars array and
second pointer in local-constants array, add referenced values and
replace operand pointers with pointer to result, store that result
pointer in the second slot of local_vars, load first constant pointer
(always to None), and return.

Who knows what *we* do when we read, parse, and possibly execute
Python code.

Terry J. Reedy
 
F

Frode Vatvedt Fjeld

Terry Reedy said:
[..] For even faster runtime 'name lookup' an implementation may
represent names as slot numbers (indexes) for a hiddem, non-Python
array. CPython does this (with C pointer arrays) for function
locals whenever the list of locals is fixed at compile time, which
is usually. (To prevent this optimization, add to a function body
something like 'from mymod import *', if still allowed, that makes
the number of locals unknowable until runtime.) [..]

This certainly does not ease my worries over Pythons abilities with
respect to interactivity and dynamism.
 
A

Alex Martelli

Frode Vatvedt Fjeld wrote:
...
excessive overhead, in Python we like to MEASURE performance issues
rather than just reason about them "abstractly"; which is why Python
comes with a handy timeit.py script to time a code snippet
accurately. [...]

Thank you for the detailed information. Still, I'm sure you will agree
that sometimes reasoning about things can provide insight with
predictive powers that you cannot achieve by mere experimentation.

A few centuries ago, a compatriot of mine was threatened with
torture, and backed off, because he had dared state that "all
science comes from experience" -- he refuted the "reasoning
about things" by MEASURING (and fudging the numbers, if the
chi square tests about his reports about the sloping-plane
experiments are right -- but then, Italians _are_ notoriously
untrustworthy, even though sometimes geniuses;-).

These days, I'd hope not to be threatened with torture if I assert:
"reasoning" is cheap, that's its advantage -- it can lead you to
advance predictive hypotheses much faster than mere "data
mining" through masses of data might yield them. But those
hypotheses are very dubious until you've MEASURED what they
predict. If you don't (or can't) measure, you don't _really KNOW_;
you just _OPINE_ (reasonably or not, justifiably or not, etc). One
independently repeatable measurement trumps a thousand clever
reasonings, when that measurement gives numbers contradicting
the reasonings' predictions -- that one number sends you back to
the drawing board.

Or, at least, that's how we humble engineers see the world...


Alex
 
P

Pascal Bourguignon

Andrew Dalke said:
[...]
Nor have you given any sort of guideline on how to implement
this sort of AI in the first place. Without it, you've just restated
the dream of many people over the last few centuries. It's a
dream I would like to see happen, which is why I agreed with you.
[...]
Truely I believe that programming languages as we know
them are not the (direct) solution, hence my pointers to
evolvable hardware and similar techniques.

You're right, I did not answer. I think that what is missing in
classic software, and that ought to be present in AI software, is some
introspective control: having a process checking that the other
processes are live and progressing, and able to act to correct any
infinite loop, break down or dead-lock. Some hardware may help in
controling this controling software, like on the latest Macintosh:
they automatically restart when the system is hung. And purely at the
hardware level, for a real life system, you can't rely on only one
processor.
 
J

John Atwood

Andrew Dalke said:
The best examples of resilent architectures I've seen come from
genetic algorithms and other sorts of feedback training; eg,
subsumptive architectures for robotics and evolvable hardware.
There was a great article in CACM on programming an FPGA
via GAs, in 1998/'99 (link, anyone?). It worked quite well (as
I recall) but pointed out the hard part about this approach is
that it's hard to understand, and the result used various defects
on the chip (part of the circuit wasn't used but the chip wouldn't
work without it) which makes the result harder to mass produce.

something along these lines?
http://www.cogs.susx.ac.uk/users/adrianth/cacm99/node3.html


John
 
M

Marshall Spight

Jarek Zgoda said:
Yes. By deleting a name from namespace. You better read some tutorial,
this will save you some time.

Forgive my ignorance, but why would one want to
delete a function name? What does it buy you?
I can see a use for interactive redefinition of a function
name, but deleting?


Marshall
 
G

Gerrit Holl

Alex said:
Yes -- which is exactly why many non-programmers would prefer the
parentheses-less notation -- with more obvious names of course;-).
E.g.:
emitwarning URGENT "meltdown imminent!!!"
DOES look nicer to non-programmers than
emitwarning(URGENT, "meltdown imminent!!!")

So let's write:

raise URGENT, "meltdown imminent!!!"

Gerrit.

--
182. If a father devote his daughter as a wife of Mardi of Babylon (as
in 181), and give her no present, nor a deed; if then her father die, then
shall she receive one-third of her portion as a child of her father's
house from her brothers, but Marduk may leave her estate to whomsoever she
wishes.
-- 1780 BC, Hammurabi, Code of Law
 
M

Marshall Spight

Scott McIntire said:
It seems to me that the Agency would have fared better if they just used
Lisp - which has bignums - and relied more on regression suites and less on
the belief that static type checking systems would save the day.

I find that an odd conclusion. Given that the cost of bugs is so high
(especially in the cited case) I don't see a good reason for discarding
*anything* that leads to better correctness. Yes, bignums is a good
idea: overflow bugs in this day and age are as bad as C-style buffer
overruns. Why work with a language that allows them when there
are languages that don't?

But why should more regression testing mean less static type checking?
Both are useful. Both catch bugs. Why ditch one for the other?


Marshall
 
P

Pascal Costanza

Marshall said:
I find that an odd conclusion. Given that the cost of bugs is so high
(especially in the cited case) I don't see a good reason for discarding
*anything* that leads to better correctness. Yes, bignums is a good
idea: overflow bugs in this day and age are as bad as C-style buffer
overruns. Why work with a language that allows them when there
are languages that don't?

But why should more regression testing mean less static type checking?
Both are useful. Both catch bugs. Why ditch one for the other?

....because static type systems work by reducing the expressive power of
a language. It can't be any different for a strict static type system.
You can't solve the halting problem in a general-purpose language.

This means that eventually you might need to work around language
restrictions, and this introduces new potential sources for bugs.

(Now you could argue that current sophisticated type systems cover 90%
of all cases and that this is good enough, but then I would ask you for
empirical studies that back this claim. ;)

I think soft typing is a good compromise, because it is a mere add-on to
an otherwise dynamically typed language, and it allows programmers to
override the decisions of the static type system when they know better.


Pascal
 
G

Garry Hodgson

Pascal Bourguignon said:
You're right, I did not answer. I think that what is missing in
classic software, and that ought to be present in AI software, is some
introspective control: having a process checking that the other
processes are live and progressing, and able to act to correct any
infinite loop, break down or dead-lock.

so assume this AI software was running on Ariane 5, and the same
condition occurs. based on the previously referenced design
assumptions, it is told that there's been a hardware failure, and that
numerical calculations can no longer be trusted. how does it cope
with this?
Some hardware may help in
controling this controling software, like on the latest Macintosh:
they automatically restart when the system is hung.

in this case, a restart would cause the same calculations to occur,
and the same failure to be reported.
And purely at the
hardware level, for a real life system, you can't rely on only one
processor.

absolutely right. though, in this case, this wouldn't have helped either.

the fatal error was a process error, and it occurred long before launch.
 
W

William Lovas

...because static type systems work by reducing the expressive power of
a language. It can't be any different for a strict static type system.
You can't solve the halting problem in a general-purpose language.

What do you mean by "reducing the expressive power of the language"? There
are many general purpose statically typed programming languages that are
Turing complete, so it's not a theoretical consideration, as you allude.
This means that eventually you might need to work around language
restrictions, and this introduces new potential sources for bugs.

(Now you could argue that current sophisticated type systems cover 90%
of all cases and that this is good enough, but then I would ask you for
empirical studies that back this claim. ;)

Empirically, i write a lot of O'Caml code, and i never have to write
something in a non-intuitive manner to work around the type system. On the
contrary, every type error the compiler catches in my code indicates code
that *doesn't make sense*. I'd hate to imagine code that doesn't make
sense passing into regression testing. What if i forget to test a
non-sensical condition?

On the flip-side of the coin, i've also written large chunks of Scheme
code, and I *did* find myself making lots of nonsense errors that weren't
caught until run time, which significantly increased development time
and difficulty.

Furthermore, thinking about types during the development process keeps me
honest: i'm much more likely to write code that works if i've spent some
time understanding the problem and the types involved. This sort of
pre-development thinking helps to *eliminate* potential sources for bugs,
not introduce them. Even Scheme advocates encourage this (as in Essentials
of Programming Languages by Friedman, Wand, and Haynes).
I think soft typing is a good compromise, because it is a mere add-on to
an otherwise dynamically typed language, and it allows programmers to
override the decisions of the static type system when they know better.

When do programmers know better? An int is an int and a string is a
string, and nary the twain shall be treated the same. I would rather
``1 + "bar"'' signal an error at compile time than at run time.

Personally, i don't understand all this bally-hoo about "dynamic languages"
being the next great leap. Static typing is a luxury!

William
 
A

Andrew Dalke

Pascal Costanza:
...because static type systems work by reducing the expressive power of
a language. It can't be any different for a strict static type system.
You can't solve the halting problem in a general-purpose language.

This means that eventually you might need to work around language
restrictions, and this introduces new potential sources for bugs.

Given what I know of embedded systems, I can effectively
guarantee you that all the code on the rocket was proven
to halt in not only a finite amount of time but a fixed amount of
time.

So while what you say may be true for a general purpose
language, that appeal to the halting problem doesn't apply given
a hard real time constraint.

Andrew
(e-mail address removed)
 
M

Matthias Blume

Pascal Costanza said:
...because static type systems work by reducing the expressive power
of a language.

It depends a whole lot on what you consider "expressive". In my book,
static type systems (at least some of them) work by increasing the
expressive power of the language because they let me express certain
intended invariants in a way that a compiler can check (and enforce!)
statically, thereby expediting the discovery of problems by shortening
the edit-compile-run-debug cycle.
(Now you could argue that current sophisticated type systems cover 90%
of all cases and that this is good enough, but then I would ask you
for empirical studies that back this claim. ;)

In my own experience they seem to cover at least 99%.

(And where are _your_ empirical studies which show that "working around
language restrictions increases the potential for bugs"?)

Matthias
 

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,777
Messages
2,569,604
Members
45,218
Latest member
JolieDenha

Latest Threads

Top