[Python-ideas] non-Pre-PEP: Syntactic replacement of built-in types with user-defined callables (Take 2... fewer newlines!)
Taro
taroso at gmail.com
Mon Jan 28 07:01:19 CET 2008
Hi all... again,
Built-in types such as float, string, or list are first-class citizens
in Python sourcefiles, having syntactic support:
myfloat = 1.0
mystring = "my string"
mylist = [1,2,4,8]
mydict = {1:"a", 2:"b", 3:"c"}
myset = {1,2,3}
User-defined classes are second-class citizens, requiring data to be
manually converted from a type:
mydecimal = Decimal("1.00000000000000001")
myrope = Rope("my rope")
myblist = BList([1,2,4,8])
myordereddict = OrderedDict((1,"a"), (2, "b"), (3, "c"))
myfrozenset = frozenset([1,2,3])
If there's only one or two conversions needed in a file, then such
conversion is not particularly burdensome, but if one wants to
consistently use (say) decimals throughout a file then the ability to
use a literal syntax makes for prettier source.
Some languages have open types, allowing the addition of methods to
built-in types. This is not considered desired behaviour for Python,
since modifications made in one module can potentially affect code in
other modules.
A typedef is syntactic sugar to allow user-defined replacements to be
treated as first-class citizens in code. They affect only the module
in which they appear and do not modify the original type. To be
typedeffable, something must be a builtin type, have a
constant/syntactic representation, and be callable. Hence,
typedeffable types would be limited to complex, dict, float, int,
?object?, list, slice, set, string, and tuple. No modification is
made to __builtins__ or types, so conversion and/or reference to the
original type is still possible.
The syntax for a typedef is:
from MODULE typedef ADAPTOR as TYPE
OR
typedef BUILTINADAPTOR as TYPE
Syntactic constants of a given type are then wrapped with a call:
ADAPTOR(SOURCELITERAL)
where SOURCELITERAL is the string that appears in the sourcecode
eg:
from decimal typedef Decimal as float
i = 1.000000000000000000000000000001
translates as:
from decimal import Decimal as float
i = Decimal("1.000000000000000000000000000001")
Syntactic collections of a given type are always provided with a list
of objects, eg:
from decimal typedef Decimal as float
from blist typedef BList as list
b = [1.1, 4.2]
translates as:
from decimal import Decimal as float
from blist import BList as list
b = Blist([Decimal("1.1"), Decimal("4.2")])
and
from collections typedef OrderedDict as dict
d = {1:"a", 2:"b", 3:"c"}
as:
from collections import OrderedDict as dict
d = OrderedDict([(1,"a"), (2,"b"), (3,"c")])
A typedef appears at the start of a module immediately after any
__future__ imports. As no adaptors can be defined in a module before
a typedef and typedefs are in no way a forward declaration, "typedef
ADAPTOR as TYPE" only works for builtins, since to do otherwise would
lead to one of two unpalatable options; either:
a/ definition of adaptors would have to be allowed pre-typedef, which
would allow them to be buried in code, making them far easier to miss;
or
b/ adaptors would be defined after the typedef, which means that you'd
have to handle:
typedef float as float
def float():
pass
or:
typedef MyObject as object
class MyObject(object):
pass
or:
typedef myfloat as float
x = 1.1
def myfloat():
pass
It is true that if a valid typedef is made, the type can be redefined
within the module; but the "consenting adults" rule applies -- it's
possible to redefine str and float multiple times within a module as
well, but that isn't recommended either (and the typedef at the top of
the module at least indicates that non-standard behaviour is to be
expected)
It is a SyntaxError to typedef the same type more than once:
from decimal typedef Decimal as float
from types typedef FloatType as float #SyntaxError("Type 'float'
already redefined.")
Spelling: "typedef" is prettier than "pragma", and less likely to be
in use than "use" but its behaviour is somewhat different from C's
typedef, so perhaps another term may be preferred.
Theoretical Performance Differences: Since a typedef is purely
syntactic sugar, and all tranformational work would be done at
compilation, running code should be no slower (there should be no new
opcodes necessary) and by default no faster that performing manual
conversion (though they may assist an optimisation). Unless
optimisation magic is available, performance-critical code should be
careful when typedeffing not to use typedeffed literals in inner
loops.
I know it's considered polite to provide code, but any implementation
would have to be in C, so please accept this extremely fake dummy
implementation which in no way resembles the way things really work as
a poor substitute:
typedefs = { FLOATTYPE: None,
INTTYPE: None,
LISTTYPE: None,
DICTTYPE: None,
... }
while True
line = lines.next()
type, module, adaptor = parsetypedef(line)
if type is None:
break
if typedefs[type] is not None:
raise SyntaxError("Typedef redef")
typedefs[type] = adaptor
if module is not None:
emit_bytecode_for_import_from(type, module, adaptor)
else:
emit_bytecode_for_assignment(type, adaptor)
parse([line] + lines)
...
def emit_float(...):
if typedefs[FLOATTYPE] is not None:
emit_constant(typedefs[FLOATTYPE][0], stringliteral)
else:
... # standard behaviour
def emit_list(...):
if typedefs[LISTTYPE] is not None:
emit(LOADGLOBAL, typedefs[LISTTYPE])
# standard behaviour
if typedefs[LISTTYPE] is not None:
emit(CALL_FUNCTION)
# standard behaviour
All rights are assigned to the Python Software Foundation
More information about the Python-ideas
mailing list