Steven D'Aprano a écrit :
On Tue, 03 Feb 2009 12:09:46 +0100, Bruno Desthuilliers wrote:

I love Python, and I'm greedy and want it all: I want a dynamic,
easy-to- use language *and* a compiler that can protect me from myself
That's not what compilers are for.

So you say.

While it's quite clear that a compiler for a statically typed language can catch at least some type errors, this is still not what a compiler is for. A compiler is here to compile you source code into a suitable representation for the execution environment. And FWIW, some compilers are rather weaker than Python when it comes to "protecting you from yourself".

and bad data.
I definitly fail to see how a compiler could protect you from *runtime*
issues ???

I'm surprised you can't. Any time a compiler prevents an error by detecting it at compile-time (say, attempting to store a 96-bit float into the memory space allocated for a 32-bit int),

Oh, you meant "type error". But this is a programming error, not "bad data". "bad data" is something you get (usually from the outside world) at runtime only. Well, the way I understand it, at least.


Perhaps what you are thinking is that I meant compilers can protect you from "all" runtime issues. That would be silly. Although, in principle, we can get close, for some definition of "close".


Here's a toy function, and a specification:

def inverse(x):
    return 1.0/x

Specification: input must be a float, output must be a float.

Is that function correct? No, because it will raise an exception if x==0.0. Python's compiler is dumb and will allow you to write incorrect functions, but a smart compiler could look at that function and see that it was incorrect. The fix would then be to change the code, or the specification, or both.

Ok, let's see. What happens if you pass 0.0 to the function as-is ? Yes, indeed, you get a runtime error. Now what happens if you add error handling to the function itself ? What will the error handler do ? Yes, raise a runtime error. So far, I don't see any gain here. No, no, don't answer yet. Sure, what you need here is a compiler that can inspect the whole code source using this function to make sure it is always properly invoked. Mmm, wait - will this work with shared libs ? I'm afraid not. Ok, so you have to change the specification to : "input must be a non-zero-float", and use the type system to define non-zero-float type.

Smart compilers turn some runtime errors into compile-time errors.

Indeed.

The earlier you catch the error, the easier it is to fix it.

Indeed.

Now, that's a toy example. Languages like Ada make correctness proofs, well, perhaps not easy, but merely difficult compared to impossible for languages like Python.

Indeed. And that's fine : if we want maximum type safety and as-possibly-proved code, we know which tool to use. Now good luck doing web development, system admin, app scripting and quite a lot of other things (well, perhaps more that 80% of ordinary application programming) in Ada.

Can you understand that not everybody needs maximum type safety / correctness proof etc for all his code ? That all this "security" stuff comes with a price that may not be worth paying ?

To bring it back to private/public attributes, side-effects make correctness proofs difficult. Modifications of attributes are side-
effects.

Then use a functional language !-)

When attributes are subject to being modified by arbitrary code, it is harder to reason about the correctness of the code:

Indeed. Not the right tool, obviously. OTHO, it makes it way easier to use as glue between different, sometimes not highly compatible components.

(snip)

There are two usual approaches to dealing with that problem which are available to Python programmers:

(1) Paranoia. Make the developer responsible for checking everything all the time:


Paranoiacs (also known as control-freaks) are not happy using Python. They usually prefer Ada or, at least, Java.

    def invert(self):
        x = self.x
        if isinstance(x, float) and x:
            return 1.0/self.x
        else:
            raise MyError('post-condition x a non-zero float violated')


The correct solution is indeed to make x a property with a sanity check in the setter - in which case you'll have a useful traceback - or as a read-only property, or even better to just declare it as implementation (naming it _x).


(2) Hope the error never occurs, and if it does, let the caller deal with it.

If the attribute is either a read-only property or an implementation attribute, then the guy that messed with it is responsible for the possible consequences. That's the contract, yes.

Hopefully you aren't your own caller.

I'm usually the first user of my own libs, and my coworkers comes immediatly after. IOW, I eat my own dogfood, thanks.

That's often the Python approach.

As I explained above, the "Python approach" is actually a bit different.

In your example code, you explicitely declared x as a *public* attribute. Which is not wise - whatever the language - if you have to maintain invariants on this attribute. Make it either an implementation attribute or a read-only property.

(snip usual stuff about nuclear disasters, x-ray-overdosed patients etc)

There is a third strategy, sadly not available to Python programmers: prevention by catching potential errors at compile-time. (Python does catch some errors at compile-time, but only syntax errors.)

Yes. Because *everything* else happens at runtime. Remember, even "def" and "class" are executable statements.

If x can only be modified in a few places, (that is, it is effectively hidden or private) then it is much easier to reason about program correctness, avoid bugs, and debug those bugs which do occur.

Would you then say that Ruby has "real privates" attributes ?


No, this is not a panacea which will prevent every imaginable bug.

Indeed.

Nor is it a replacement for unit-testing (which also does not prevent every imaginable bug).

Neither.

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to