[Types-sig] Static typing: Towards closure?
Martijn Faassen
faassen@vet.uu.nl
Thu, 20 Jan 2000 23:16:25 +0100
[snip interesting proposal doc]
A few points that I didn't see discussed by Guido in his proposal document.
They range from a syntactic nit to a discussion on interface semantics,
so please bear with me. :)
I) Beware of overloading ':' too much. The colon currently I think has three
meanings already, luckily fairly easily distinguishable by the eye:
* Block introducer
class Foo:
...
def Bar:
...
* slice syntax
a[1:10]
* dictionary syntax
a = {"foo" : "bar"}
I don't think the type annotation use is that easy on the eyeballs, though:
decl foo: integer
It's too much like
def foo: integer
Of course the latter doesn't mean anything, but it's still too visually
similar to block introducer use to me. We may therefore want to use something
other than the colon for type annotations. Just to be contrary again,
I'll again advocate the use of 'the' (inspired by Tim Peters):
decl foo the integer
def foo(a the string, b the Bar) -> integer:
pass
II) The stubbing problem.
Currently, Python allows this:
def foo(a, b):
pass
Nobody complains, we can run and test our code. This is good for rapid
prototyping.
However:
def foo(a, b) -> integer:
pass
will presumabily cause the typechecker to barf. I'm not quite sure if this
is really bad, but I just wanted to bring it to people's attention.
III)
My previous point implies in my mind an important principle:
When adding type checking to Python, the speed of developing in Python should
*not* go away. If static type checking makes us slow down too much, we
will have failed.
This splits into two requirements:
* We want to keep Python as it is today. We want to avoid the chance that
static types run amok, subtly or non-subtly pushing people to add them
everywhere, as this could slow down development a lot.
* We want statically checked Python to be as much as possible like Python
today in regards to quick development time. Ideally Python with type
annotations should not be that much slower to develop in than Python without.
My next point is influenced by this important principle as well.
IV) interfaces
As I remarked earlier on this list, perhaps it's useful to think of all types as
interfaces. The type of an instance is the interface type defined by
its class. The interfaces form an inheritance tree of the form of a
DAG.
A class always implies at least one interface; that of the same name as the
classes name.
[If two classess exist in a namespace with the same name, this is only
valid if their interfaces are structurally identical (or perhaps use
of them is only valid if only the structurally identical part of the
interface is used).]
Interfaces can be implied:
class Foo implements Alpha:
decl greeting: integer
def hey(a: integer)->integer:
...
def hi(a: string)->string:
...
def hoi(a: float)->float:
...
class Bar implements Alpha:
decl greeting: integer
decl specialgreeting: integer
def hey(a: integer)->integer:
...
def hi(a: string)->string:
...
Alpha is now deduced to be the following interface:
interface Alpha:
decl greeting: integer
def hey(a: integer)->integer:
...
def hi(a: string)->string:
...
Note that you don't need to write this down anywhere! It's implicit
(until explicitized by the developer).
The consequences:
* no need to explicitly code up interfaces, so existing Python code is
more easily brought into a typecheckable form. You could for instance
say each FileObject type class implements the interface FileObject.
* no need for access specifiers; private members are implied as they
usually won't appear in both classes, and are thus not part of the
interface.
* interfaces are easily shrunken if necessary, by declaring a special class
with the maximum interface.
* interfaces can always be made explicit (and then any class that states it
conforms to it but doesn't is in error), so that a minimal interface can
be specified.
These consequences may be advantages during development; interfaces (or types)
can be quickly and conveniently created and used, and they are always
_right_; we're not bothered by interface compliance errors as the interfaces
are implied.
This is good as early in development we may still be prototyping, and
therefore we may not be clear on the exact structure of the interfaces
yet. We don't want to go back and forth to change the interface definition
all the time as we discover new methods are needed, or some should be removed.
We only have to edit the actual classes implementing the interface.
As the code solidifies we may want to do more checking, so we
can start to explicitize the interfaces. This is similar to the pattern we're
hoping to accomplish by adding static typechecking -- it should be possible
to code in the dynamic way and then tighten up code by adding type annotations
later.
I posted earlier to the list on this idea, but either nobody read it,
nobody understood it or everybody thought it was obviously a bad idea. I'd
like to be informed on which is the right explanation. :)
Regards,
Martijn