Extending Python: exception handling
skaller at maxtal.com.au
Wed Aug 25 04:31:52 CEST 1999
At 12:07 24/08/99 -0500, Skip Montanaro wrote:
>Later on, John wrote:
> y = sqrt(x)
> y = x.sqrt()
> Here, the programmer is assuming that if the builtin sqrt function
> defined for floats fails, the object might be a class emulating numbers,
> and try to call a sqrt method. But the Viper compiler might _know_ that
> x is a class instance and that the builtin function 'sqrt' only applies
> to floats, and report an error.
> Even worse, it might _deduce_ that x must be a float, and then get
> confused seeing x.sqrt(), which might require that x is an instance
> (depending on whether sqrt is a method of floats or not).
>Then you're programming in a language other than Python.
I don't quite agree, and I want to explain what I mean by that.
First, there IS no 'precise language' Python. There is a CPython 1.5.2
_defined by the implementation_. Now, I believe that we both
would like to agree that there is a more general language "Python"
which is separate from the implementation -- it is described in
a loose-ish way in the reference manual, although it too contains
far too many implementation details to be considered a specification
of an abstract language.
I have pointed out that even Guido CANNOT extend any version
of Python, since behaviour is well defined for what we might
believe are 'erroneous' constructions. (there are some exceptions
where the manual says that the behaviour is undefined)
The way around this logical conundrum must be to
specify that in SOME cases where an exception is thrown
by CPython 1.5.2, the program is in fact erroneous.
In those cases, then, a change in semantics constitues
an extension: it will break no correct Python programs.
Therefore, in cases like **:
y = sqrt(x)
y = x.sqrt()
where the user is clearly relying on properties of CPython 1.5.2,
the program is in fact NOT correct Python. Therefore, an extension
which changes the behaviour of this code does not break correct
Python programs -- although it _does_ break CPython 1.5.2 reliant
codes. (** actually, this isn't such a good example ..)
In this specific case, the manual would need to say
that 'sqrt' requires a float argument, or an argument that
can be converted to a float. In that case, calling sqrt
on anything else is an error. THEREFORE I can ADD new
semantics in these cases without breaking any correct code.
>If you can
>reasonably determine that x is an instance and not a float, I'd expect the
>compiler to generate the equivalent of simply
> y = x.sqrt()
>if you know that executing the try block will always fail.
This is partly backwards. The way type inference works,
is that it has to look at the USAGE of something to determine
what it's type is.
In order for that to work, I have to assume that the
input program is 'correct' Python. And I don't mean CPython 1.5.2,
I mean 'correct' in the abstract sense in which some code
is 'erroneous', and in that case the behaviour is UNDEFINED:
which is quite different to the well defined behaviour of
throwing an exception.
As I pointed out above, there cannot be an abstract
notion of correct 'Python' without such assumptions.
> If it had been:
> y = sqrt(x)
> except AttributeError:
> y = x.sqrt()
>then you could generate just
> y = sqrt(x)
>when x is a float, because an AttributeError can't occur. (Python currently
>raises an AttributeError if an instance doesn't have a __float__ method.)
Exactly my point. What CPython 1.5.2 does currently is NOT
a specification for Python, otherwise it could not be extended,
CPython 1.5.1 and 1.6 wouldn't be "Python" and neither would
"JPython" be Python.
My intent is that Viper be Python, that is, that it
be an extension of a reasonably large subset of Python.
It will be necessary to exclude some 'Python' programs,
just as JPython does.
In fact, 'Python' will be defined by the common
subset of CPython, JPython and Viper (and any other
implementations), together with some reasonable
interpretation of the language reference.
As an example: I think a ZeroDivideError is
part of Python: the user can catch it, and expect it
to be thrown if division by zero is attempted.
On the other hand, if the user uses an exception
to detect if an object has some type or not,
by 'trying out' some operation that might fail,
then their program is broken -- it isn't Python.
Because one might reasonably extend the semantics
so that the operation succeeds.
Here is an actual working example:
In CPython 1.5.2, this raises an exception.
But in Viper, it prints 2, because integers have
a 'succ' method. Here's another piece of code
I consider broken:
assert type(type(1)) != types.ClassType
because in Viper, the types of standard objects
are classes. And I think that is reasonable,
and 'in the spirit of Python'. In fact, the community
has been demanding better unification of the type
system -- Viper provides it.
For those that are interested, in the current version,
the 'type' of an object can be ANY object: not just a class!
When lookup fails, there is a 'last resort' lookup on the
type of the object.
The types of objects are NOT defined by Viper.
They're defined in Python by a client module!
So you can, for example, define any methods you like
for any type. Here is the current definition of
the type of an integer in the module 'py_types':
def succ(x): return x + 1
def pred(x): return x - 1
The module 'py_types' is imported automatically.
The type of an integer must be called "PyIntType".
Actually: the attribute __typename__ specifies
the name of the type of an object in the module py_types.
For an integers, this is 'builtin' as 'PyIntType'
and cannot be changed.
In addition, the function 'type' is NOT builtin to Viper,
it is, instead, an ordinary python function defined
in the module 'py_types'. The user can replace that
definition with anything they want.
John Skaller email: skaller at maxtal.com.au
snail: 10/1 Toxteth Rd, Glebe NSW 2037, Australia
More information about the Python-list