245 lines
8.4 KiB
Plaintext
245 lines
8.4 KiB
Plaintext
PEP: 3117
|
||
Title: Postfix type declarations
|
||
Version: $Revision$
|
||
Last-Modified: $Date$
|
||
Author: Georg Brandl <georg@python.org>
|
||
Status: Rejected
|
||
Type: Standards Track
|
||
Content-Type: text/x-rst
|
||
Created: 01-Apr-2007
|
||
Python-Version: 3.0
|
||
Post-History:
|
||
|
||
|
||
Abstract
|
||
========
|
||
|
||
This PEP proposes the addition of a postfix type declaration syntax to
|
||
Python. It also specifies a new ``typedef`` statement which is used to create
|
||
new mappings between types and declarators.
|
||
|
||
Its acceptance will greatly enhance the Python user experience as well as
|
||
eliminate one of the warts that deter users of other programming languages from
|
||
switching to Python.
|
||
|
||
|
||
Rationale
|
||
=========
|
||
|
||
Python has long suffered from the lack of explicit type declarations. Being one
|
||
of the few aspects in which the language deviates from its Zen, this wart has
|
||
sparked many a discussion between Python heretics and members of the PSU (for
|
||
a few examples, see [EX1]_, [EX2]_ or [EX3]_), and it also made it a large-scale
|
||
enterprise success unlikely.
|
||
|
||
However, if one wants to put an end to this misery, a decent Pythonic syntax
|
||
must be found. In almost all languages that have them, type declarations lack
|
||
this quality: they are verbose, often needing *multiple words* for a single
|
||
type, or they are hard to comprehend (e.g., a certain language uses completely
|
||
unrelated [#]_ adjectives like ``dim`` for type declaration).
|
||
|
||
Therefore, this PEP combines the move to type declarations with another bold
|
||
move that will once again prove that Python is not only future-proof but
|
||
future-embracing: the introduction of Unicode characters as an integral
|
||
constituent of source code.
|
||
|
||
Unicode makes it possible to express much more with much less characters, which
|
||
is in accordance with the Zen ("Readability counts.") [ZEN]_. Additionally, it
|
||
eliminates the need for a separate type declaration statement, and last but not
|
||
least, it makes Python measure up to Perl 6, which already uses Unicode for its
|
||
operators. [#]_
|
||
|
||
|
||
Specification
|
||
=============
|
||
|
||
When the type declaration mode is in operation, the grammar is changed so that
|
||
each ``NAME`` must consist of two parts: a name and a type declarator, which is
|
||
exactly one Unicode character.
|
||
|
||
The declarator uniquely specifies the type of the name, and if it occurs on the
|
||
left hand side of an expression, this type is enforced: an ``InquisitionError``
|
||
exception is raised if the returned type doesn't match the declared type. [#]_
|
||
|
||
Also, function call result types have to be specified. If the result of the call
|
||
does not have the declared type, an ``InquisitionError`` is raised. Caution: the
|
||
declarator for the result should not be confused with the declarator for the
|
||
function object (see the example below).
|
||
|
||
Type declarators after names that are only read, not assigned to, are not strictly
|
||
necessary but enforced anyway (see the Python Zen: "Explicit is better than
|
||
implicit.").
|
||
|
||
The mapping between types and declarators is not static. It can be completely
|
||
customized by the programmer, but for convenience there are some predefined
|
||
mappings for some built-in types:
|
||
|
||
========================= ===================================================
|
||
Type Declarator
|
||
========================= ===================================================
|
||
``object`` <20> (REPLACEMENT CHARACTER)
|
||
``int`` ℕ (DOUBLE-STRUCK CAPITAL N)
|
||
``float`` ℮ (ESTIMATED SYMBOL)
|
||
``bool`` ✓ (CHECK MARK)
|
||
``complex`` ℂ (DOUBLE-STRUCK CAPITAL C)
|
||
``str`` ✎ (LOWER RIGHT PENCIL)
|
||
``unicode`` ✒ (BLACK NIB)
|
||
``tuple`` ⒯ (PARENTHESIZED LATIN SMALL LETTER T)
|
||
``list`` ♨ (HOT SPRINGS)
|
||
``dict`` ⧟ (DOUBLE-ENDED MULTIMAP)
|
||
``set`` ∅ (EMPTY SET) (*Note:* this is also for full sets)
|
||
``frozenset`` ☃ (SNOWMAN)
|
||
``datetime`` ⌚ (WATCH)
|
||
``function`` ƛ (LATIN SMALL LETTER LAMBDA WITH STROKE)
|
||
``generator`` ⚛ (ATOM SYMBOL)
|
||
``Exception`` ⌁ (ELECTRIC ARROW)
|
||
========================= ===================================================
|
||
|
||
The declarator for the ``None`` type is a zero-width space.
|
||
|
||
These characters should be obvious and easy to remember and type for every
|
||
programmer.
|
||
|
||
|
||
Unicode replacement units
|
||
=========================
|
||
|
||
Since even in our modern, globalized world there are still some old-fashioned
|
||
rebels who can't or don't want to use Unicode in their source code, and since
|
||
Python is a forgiving language, a fallback is provided for those:
|
||
|
||
Instead of the single Unicode character, they can type ``name${UNICODE NAME OF
|
||
THE DECLARATOR}$``. For example, these two function definitions are equivalent::
|
||
|
||
def fooƛ(xℂ):
|
||
return None
|
||
|
||
and ::
|
||
|
||
def foo${LATIN SMALL LETTER LAMBDA WITH STROKE}$(x${DOUBLE-STRUCK CAPITAL C}$):
|
||
return None${ZERO WIDTH NO-BREAK SPACE}$
|
||
|
||
This is still easy to read and makes the full power of type-annotated Python
|
||
available to ASCII believers.
|
||
|
||
|
||
The ``typedef`` statement
|
||
=========================
|
||
|
||
The mapping between types and declarators can be extended with this new statement.
|
||
|
||
The syntax is as follows::
|
||
|
||
typedef_stmt ::= "typedef" expr DECLARATOR
|
||
|
||
where ``expr`` resolves to a type object. For convenience, the ``typedef`` statement
|
||
can also be mixed with the ``class`` statement for new classes, like so::
|
||
|
||
typedef class Foo☺(object<63>):
|
||
pass
|
||
|
||
|
||
Example
|
||
=======
|
||
|
||
This is the standard ``os.path.normpath`` function, converted to type declaration
|
||
syntax::
|
||
|
||
def normpathƛ(path✎)✎:
|
||
"""Normalize path, eliminating double slashes, etc."""
|
||
if path✎ == '':
|
||
return '.'
|
||
initial_slashes✓ = path✎.startswithƛ('/')✓
|
||
# POSIX allows one or two initial slashes, but treats three or more
|
||
# as single slash.
|
||
if (initial_slashes✓ and
|
||
path✎.startswithƛ('//')✓ and not path✎.startswithƛ('///')✓)✓:
|
||
initial_slashesℕ = 2
|
||
comps♨ = path✎.splitƛ('/')♨
|
||
new_comps♨ = []♨
|
||
for comp✎ in comps♨:
|
||
if comp✎ in ('', '.')⒯:
|
||
continue
|
||
if (comp✎ != '..' or (not initial_slashesℕ and not new_comps♨)✓ or
|
||
(new_comps♨ and new_comps♨[-1]✎ == '..')✓)✓:
|
||
new_comps♨.appendƛ(comp✎)
|
||
elif new_comps♨:
|
||
new_comps♨.popƛ()✎
|
||
comps♨ = new_comps♨
|
||
path✎ = '/'.join(comps♨)✎
|
||
if initial_slashesℕ:
|
||
path✎ = '/'*initial_slashesℕ + path✎
|
||
return path✎ or '.'
|
||
|
||
As you can clearly see, the type declarations add expressiveness, while at the
|
||
same time they make the code look much more professional.
|
||
|
||
|
||
Compatibility issues
|
||
====================
|
||
|
||
To enable type declaration mode, one has to write::
|
||
|
||
from __future__ import type_declarations
|
||
|
||
which enables Unicode parsing of the source [#]_, makes ``typedef`` a keyword
|
||
and enforces correct types for all assignments and function calls.
|
||
|
||
|
||
Rejection
|
||
=========
|
||
|
||
After careful considering, much soul-searching, gnashing of teeth and rending
|
||
of garments, it has been decided to reject this PEP.
|
||
|
||
|
||
References
|
||
==========
|
||
|
||
|
||
.. [EX1] https://mail.python.org/pipermail/python-list/2003-June/210588.html
|
||
|
||
.. [EX2] https://mail.python.org/pipermail/python-list/2000-May/034685.html
|
||
|
||
.. [EX3] http://groups.google.com/group/comp.lang.python/browse_frm/thread/6ae8c6add913635a/de40d4ffe9bd4304?lnk=gst&q=type+declarations&rnum=6
|
||
|
||
.. [#] Though, if you know the language in question, it may not be *that* unrelated.
|
||
|
||
.. [ZEN] http://www.python.org/dev/peps/pep-0020/
|
||
|
||
.. [#] Well, it would, if there was a Perl 6.
|
||
|
||
.. [#] Since the name ``TypeError`` is already in use, this name has been chosen
|
||
for obvious reasons.
|
||
|
||
.. [#] The encoding in which the code is written is read from a standard coding
|
||
cookie. There will also be an autodetection mechanism, invoked by ``from
|
||
__future__ import encoding_hell``.
|
||
|
||
|
||
Acknowledgements
|
||
================
|
||
|
||
Many thanks go to Armin Ronacher, Alexander Schremmer and Marek Kubica who helped
|
||
find the most suitable and mnemonic declarator for built-in types.
|
||
|
||
Thanks also to the Unicode Consortium for including all those useful characters
|
||
in the Unicode standard.
|
||
|
||
|
||
Copyright
|
||
=========
|
||
|
||
This document has been placed in the public domain.
|
||
|
||
|
||
|
||
..
|
||
Local Variables:
|
||
coding: utf-8
|
||
mode: indented-text
|
||
indent-tabs-mode: nil
|
||
sentence-end-double-space: t
|
||
fill-column: 70
|
||
End:
|