The Artima Developer Community
Sponsored Link

Weblogs Forum
Adding Optional Static Typing to Python

49 replies on 4 pages. Most recent reply: Jul 22, 2010 10:29 AM by Dmitry Dvoinikov

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 49 replies on 4 pages [ « | 1 2 3 4 | » ]
Serge Boyko

Posts: 1
Nickname: seriozhcka
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 23, 2004 1:37 PM
Reply to this message Reply
Advertisement
Hi there,
how about this notation?


def gcd(a: int, b: int):
while a:
a, b = b%a, a
return b: int



Seems more coherent to me...
-S

Manuel M Garcia

Posts: 1
Nickname: manuelg
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 23, 2004 1:37 PM
Reply to this message Reply
I want to tell Python how I intend to use my variables, not type check.

def gcd(a: int, b: int) -> int:
while a:
a, b = b%a, a
return b

would work the same as:

def gcd(a, b):
a = int(a)
b = int(b)
while a:
a, b = int(b%a), int(a)
return int(b)

(we are filtering every assignment to a,b through an 'int', at the function call, and also every assignment in the function body)

Of course, we would expect most of the calls to 'int' to melt away, as the operations we are performing are well defined with respect to type

Happily, we would get the appropriate exceptions when calling with anything other than 'int'

===========
Duck Typing
===========

with respect to enforcing 'duck-typing', we could provide a callable 'duck_something'

def foo(f: duck_StringIO) -> str:
f.write("hello")
return f.getvalue()

would be the same as

def foo(f):
f = duck_StringIO(f)
f.write("hello")
return str(f.getvalue())

as before, a clever optimizer would recognize that getvalue was defined like this

def getvalue(self) -> str:

the upshot is that most of these 'type-enforcing' calls at assignments would melt away

==========
coercetype
==========

Container Types, I would avoid type-variables, because I don't understand them as well as I probably should

def min(a: T, b: T) -> T:
if a < b:
return a
else:
return b

instead

def min(a, b):
enforce return: coercetype(a, b)
if a < b:
return a
else:
return b

all returns would be filtered through the result of 'coercetype'

if we wanted to enforce type on a temp variable:

def min(a, b):
T = coercetype(a, b)
enforce temp: T
enforce return: T
...

===============
container types
===============

def int_sort(L: Sequence of int) -> int:

def list_of_int_sort(L: list of int) -> int:

the 'of' keyword is so that we avoid list(int)

===========
overloading
===========

'overloaded' looks for 'TypeError', moves on to the next signature
I assume will have an intellegent dispatch logic, so the minimum
number of signatures are tried

@overloaded
def min(a: iterable):
T = coercetype(*a)
enforce return: T
...

@overloaded
def min(x, y, *rest):
T = coercetype(x, y)
enforce rest: list of T
# rest = (list of T)(rest)
# and all other assignments to rest in the body of the function
# also use (list of T) to 'filter' and enforce type
enforce return: T
...

Phillip J. Eby

Posts: 28
Nickname: pje
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 23, 2004 7:15 PM
Reply to this message Reply
> Philip Eby already proposes decorators to declare types of
> arguments : something that could sounds like
> @args(IString, IInteger)
> @return(IString)
> def x(y,z):
> return ""

Don't know where you got that idea. I am in favor of optional type declarations, but I'd prefer Guido's syntax to using decorators. My real interests in type declarations are:

1. Compact documentation/declaration of code's intent

2. Support for automatic adaptation (so you don't have to do a separate conversion/adaptation step in the body)

3. Easier interfacing/compiling to other languages such as C, C++, Java, C#, etc. etc. This would allow tools like Pyrex to use ordinary Python syntax, instead of a variant of Python syntax.

For these purposes, full parametric type assertions aren't really needed, nor is a "complete" type system, IMO. One could allow types to be full expressions, and implement simple parametric types using subscripting. E.g. 'list[int]' could return a new list subclass that accepted ints.

This could increase compiler complexity quite a bit, if your goal was to have static type checking, but I'm really not interested in static type checking, just type declarations and runtime adaptation.

Daniel F Moisset

Posts: 2
Nickname: dmoisset
Registered: Dec, 2004

Some suggestions/random ideas Posted: Dec 23, 2004 7:50 PM
Reply to this message Reply
I am an experienced Python programmer and Eiffel programmer; both are my favorite language (depending on the kind of application). I'm also very interested in typing systems, so i can give you some information on other issues I know

My Eiffel experience has showed me some other nice benefits of typing:

- it serves as documentation ("does method foo takes a Bar object, or a string with the Bar's id?")

- it works very nicely with Design by contract

The Eiffel type system is not perfect (sometimes I miss some features from Haskell like type-inference, or duck typing a la Python). But it has some nice ideas worth considering

1) conformance vs. inheritance: Eiffel agrees on your point about not making int inherit from float inherit from complex. In Eiffel, you have a relation between types called "conformance". "T1 conforms to T2" means that you can give an object of type T1 to anyone who wants a T2. So, if you have def routine(arg: T2), you can call it with a T1 as an argument.

The conformance relation is defined by several factors. if T2 inherits T1, T2 conforms to T1. For several "builtin" types, the relation is explicitely given (INTEGER conforms to REAL, with no inheritance relationship between them). You can also define "conversions": for example you can say that an INTEGER can be converted to a STRING with its method "to_string", which makes INTEGER conform to STRING. (To avoid ambiguity, there's a rule saying that A can inherit B or convert to B, but not both).

What you need to define is not an inheritance relationship (you already have one, don't break it :) ), but a conformance relationship

2) covariance

This is a problem in Eiffel, Java, and I don't know a good solution for it. Suppose you have:


def addmore(l: list(float)):
l.extend(1.5)

def problem(l: list(int)):
addmore(l)
# now l is no longer a list(int)!


If you look at each instruction/function alone, this looks ok. addmore extends a list of floats, nothing unusual. the function problem passes a list(int) to a function that wants a list(float). looks ok. but it breaks.

in Java, and some Eiffel implementations, it causes a runtime exception inside addmore. Other Eiffel implementations add some restrictions, forbidding some cases that could be allowed. This problem should be considered.

Guido van van Rossum

Posts: 359
Nickname: guido
Registered: Apr, 2003

Re: Adding Optional Static Typing to Python Posted: Dec 23, 2004 8:11 PM
Reply to this message Reply
Many excellent comments. I expect that the best solution will involve type inferencing, interfaces, and optional type declarations. I am now packing for a week-long trip to a place with no internet; I'll try to write a sequel in January.

Daniel F Moisset

Posts: 2
Nickname: dmoisset
Registered: Dec, 2004

Some suggestions/random ideas (part 2) Posted: Dec 23, 2004 8:26 PM
Reply to this message Reply
Sorry for the broken post, I had to change computers...

3) Generic classes

Something you can do in Eiffel (and Haskell, and, in some way, C++) is stuff like this:


def class myContainer[T]:

def add_item (i: T):
# yadda yadda
def get_item (index) -> T:
# yadda yadda


This is what you're doing with your 'type variables' and 'container types'

so you can later say def f(cont: myContainer[T]). It's the same which you are doing with list(T), but extended to any kind of class/interface. You can of course have multiple generic arguments

4) Constrained genericity

When using genericity, you can put restrictions on types. For example def class Foo[T -> StringIO], where you say that type Foo[T] is defined only when T conforms to StringIO (note that I say "conforms" and not "inherits"). You can put multiple restrictions def class Foo[T -> {StringIO, NonMutable}]. Or even mutual dependencies, def class Foo[T -> Comparable, S -> Sequence[T], R -> S,T].

That kind of restrictions (this goes outside Eiffel) could even be useful outside genericity. You might want to declare functions as:


def binarysearch(item: T -> {Comparable}, seq: Sequence(T))


Note that if you take this ideas to the extreme, you finish with duck typing: define a type having only each possible method name+signature, and then you have each declaration of parameter as restricted to the types that provide the needed operations. For example you would have gcd taking a paramter restricted by the supertype "types with % operator taking a parameter of this same type" and the supertype "types that can be bool tested".

5) Typing and inheritance.

A whole new world of problem arises when mixing typing with inheritance. When you redefine a method on a descendant class, can you change the argument/result types? how? A safe bet (Java and C++ do this) is to forbid type changing. Other choice is going down on the conformance Tree (actually a DAG) but it has some problems (see covariance). Another safe bet from the theoretical standpoint, but not very useful in practice, is allowing result types to go down, and argument types to go up. It must be considered very carefully

Tom Cocagne

Posts: 1
Nickname: rakis
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 23, 2004 10:01 PM
Reply to this message Reply
To throw another log on the fire of the apparently popular idea of incorporating interfaces into the standard language, here's another half-formed idea that might avoid language modifications:

Assuming a built-in std_interfaces:

-----------------------------------

from std_interfaces import *

class IOutStream( TypeSafeInterface ):

def write(self, data):
pass

write.tsi_args = (IStream, (IString, IBuffer))
write.tsi_returns = (Int, None)


def write_lines(self, lines):
pass

write_lines.tsi_args = (IStream, ISequence(IString))
write_lines.tsi_returns = (Int, None)


def close(self):
pass

close.tsi_args = IStream
close.tsi_returns = None
close.excepts = (IOError, OSError)

Simon Percivall

Posts: 4
Nickname: percivall
Registered: Jul, 2003

Re: Adding Optional Static Typing to Python Posted: Dec 24, 2004 12:21 AM
Reply to this message Reply
A yes, please vote for the optional static typing, especially its syntax, as long as it doesn't actually enforce anything.

As it's proposed, it's great documentation. Optimization seems a secondary goal, and interface constriction would be more a bother than a help, especially if formal Interfaces are introduced.

Ka-Ping Yee

Posts: 24
Nickname: ping
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 24, 2004 1:53 AM
Reply to this message Reply
I came across this article via Lambda the Ultimate, which i read from time to time. Here are some random not-so-insightful thoughts.

I'll echo what was said in the comment above by has about focusing more attention on mechanisms for inference and code safety than on type declaration syntax. In addition to the risk of making the language messier, there is the meta-risk of starting a lengthy and tiresome syntax battle just by raising the issue.

Documenting and verifying preconditions is significant for security, as another comment mentioned. I wanted to note here that dynamic preconditions are often valuable and necessary for security, not just static ones. Other examples posted here, such as this:
    def divide(a, b):
# preconditions
assert isinstance(a, Number)
assert isinstance(b, Number)
assert b != 0

# body
return a/b

mention only static preconditions. But it's useful to also specify a precondition that can only be checked at runtime, such as this example of a method on a bank account:
    def decrement(self, amount):
# preconditions
assert self.balance >= amount

# body
self.balance -= amount

In E, the latter condition would be written in the function signature using a parameterized interval:
    def decr(amount :(0..balance)) :void {
balance -= amount
}

Being able to see the interval right there in the function signature is very helpful both as documentation for callers and when performing a security audit.

Mike Meyer

Posts: 2
Nickname: mwm
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 24, 2004 3:29 AM
Reply to this message Reply
My first exposure to OO programming was with the language CLU. It didn't use the terminology of OO programming, but all the pieces were in place.

What makes it stand out at this time is that it had static duck typing. You'd declare that a module accepted paremeters of a type, and what operations that type was requierd to support. This is sort of like interfaces, except that modules that don't need the full interface can accept types that only implement the parts it needs.

The gcd example would look like:

def gcd(a, b: T) -> T:
T has __mod__(T, T), __bool__(T)
while a:
a, b = b % a, a
retirm b

Michael Hudson

Posts: 8
Nickname: mwh
Registered: Jul, 2003

Re: Adding Optional Static Typing to Python Posted: Dec 24, 2004 4:02 AM
Reply to this message Reply
Guido, when you write your followup can you please explain which problems you are attempting to to solve here?

It's kind of pointless to poke holes in your argument until then :)

rahul

Posts: 2
Nickname: rahulgarg
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 24, 2004 5:26 AM
Reply to this message Reply
Hi.
Well i am a newbie to python and maybe not qualified enough to make a comment on proposals to changes in python.My previous programming experience has been in Java and C.

But anyway here goes:

I think instead what should be done is:

1.def gcd(a,b) expects (int,int):
Here the function is not enforcing type checking. The compiler should only generate warning when someone passes something which is not an int and should not generate an error.Maybe the person calling the function knows what he is doing and wants to pass the unexpected type.

2.Another possibility is to let the function decide if the type is not what it is expecting. Maybe the function recvs some kind of flag which tells it that the type passed is not what it was expecting. So it can throw an error if it is really critical.

3.In my post i am not stressing on adding 'expects' as keyword or something. Only that type should not be enforced and 'expects' makes this clear.

Rahul Garg

Nick Coghlan

Posts: 13
Nickname: ncoghlan
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 24, 2004 6:53 AM
Reply to this message Reply
I put some thoughts together, and posted them at the link below. They mainly look at tying Guido's article into PEP 246, and then investigates extending PEP 246 to make it more useful for static type-check based optimisations.

http://boredomandlaziness.skystorm.net/2004/12/type-checking-in-python.html

Christian Tismer

Posts: 2
Nickname: stackless
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 24, 2004 7:50 PM
Reply to this message Reply
> A simple decorator should suffice, shouldn't it?
>
>

> @static_types("a int, b int -> int")
> def gcd(a, b):
> while a:
> a, b = b%a, a
> return b
>

>
> This solution can evolve independently of the Python core.


Yes, I think it should suffice, and it puts some little more
sense into those decorators which really made me consider to
leave the language, after all.
At least I like this better than to add optional type
annotations to the syntax.

Despite of that, I haven't yet seen an example of Python
code that really needed type annotation. The types in
the article were always guessable in a way that the code
didn't make sense unless the right class of types was
inserted. Maybe there exist examples where this is not
obvious.

My claim is just: if you read the code, you know what kind
of types it should be. Why should a code analysis be less
intelligent than an average human reader?

I think it is possible and easy to add type annotation
to Python, but it is also an evidence of incapacity.

regards -- chris

Larry Bugbee

Posts: 13
Nickname: bugbee
Registered: Dec, 2004

Re: Adding Optional Static Typing to Python Posted: Dec 24, 2004 11:35 PM
Reply to this message Reply
Please keep it simple! I like the idea, especially the intangible benefit of documented arguments and results. ...but simplicity must be the order of the day. I'll leave it to those closest to the issues, but please consider...

Arguably Python's greatest calling card is it's simplicity making Python easy to learn and easy to maintain. Overloading, for example, could add so much complexity before getting it right that confusion would be the ultimate winner. Not good. And there are other examples.

Speed improvements would be nice and if the 80-20 rule were followed, complexity and reability should not be problems.

That said, it seems like a good idea.

Larry

Flat View: This topic has 49 replies on 4 pages [ « | 1  2  3  4 | » ]
Topic: Have Generics Killed Java? Previous Topic   Next Topic Topic: Reviewable, Runnable Specifications Ensure Software Quality

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use