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. >> 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), it has prevented a runtime error. 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. Smart compilers turn some runtime errors into compile-time errors. The earlier you catch the error, the easier it is to fix it. 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. To bring it back to private/public attributes, side-effects make correctness proofs difficult. Modifications of attributes are side- effects. When attributes are subject to being modified by arbitrary code, it is harder to reason about the correctness of the code: class Inverter(object): def __init__(self, x): # Pre-condition: x is always a float if x: self.x = x else: raise ValueError("x must not be zero") # Post-condition: if self.x exists, it is a non-zero float def invert(self): return 1.0/self.x Is method invert correct? No, it is not, because the invariant that self.x is non-zero may have been broken by some arbitrary piece of code elsewhere. Our ability to reason about the correctness of the code is weakened significantly. Sticking an underscore in front of x does not help: there's nothing to stop some arbitrary function elsewhere changing _x to zero. 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: 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') You can use decorators to decrease the amount of boilerplate, but you have to remember to use the decorators; or you can use a metaclass to automatically apply decorators, but that's hard and not very flexible. Whichever way you do it, you're still responsible, and whatever way, you still have to pay the performance penalty. This also turns an unexpected exception into an expected exception, but is otherwise rather useless. It doesn't tell you when the post-condition was violated, or by what function, and that's the information you need in order to fix the bug. (2) Hope the error never occurs, and if it does, let the caller deal with it. Hopefully you aren't your own caller. That's often the Python approach. I've already written about the glorious freedom such an approach gives the developer. I'm not being sarcastic: even if you are your own caller, you get to put off worrying about a bug which might never happen. This is a great strategy when the worst thing that a bug will do is display an icon in the wrong position. Fix it in the next release. But that's not a viable strategy when the consequences of a bug might include giving the patient 20,000 rads of radiation instead of 200: http://www.ccnr.org/fatal_dose.html 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.) 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. No, this is not a panacea which will prevent every imaginable bug. Nor is it a replacement for unit-testing (which also does not prevent every imaginable bug). It is a compliment to unit-testing. Is it subject to over-use or misuse? Of course it is. So are properties, and decorators, and metaclasses, and OO, and Singletons, and, well, everything. Let's not close our eyes to *either* the costs or the benefits, and let's not limit what Python might become in the future. Python 3 has type annotations and Guido is encouraging people to experiment with type systems. This was unthinkable a year or two ago. What might Python 4000 bring? -- Steven -- http://mail.python.org/mailman/listinfo/python-list