Friday, January 07, 2005

"where" keyword and Python as pseudo-code

There has been much discussion lately about what is now a series of blogs by Guido van Rossum (see Optional Static Typing -- Stop the Flames! for the latest discussion) about optional static typing in Python and related extensions to the language. A recent post
comp.lang.python
by Andrey Tatarinov about using where as a new keyword in the absence of anonymous functions gave me some ideas about how such a new keyword could be useful in rendering pythonic the addition of optional static typing. However, I do not use where in the same context as Tatarinov.

Python is often described as executable pseudo-code. Therefore, rather than explaining in words my ideas, I will let the code (examples taken from Python's BDFL blogs) speak for itself. Note that the amount of code versus type declarations is not really representative of real life in the following examples. I also deliberately did not add blank lines, which could have made this more readable.

1. Optional typing.

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

2. Optional typing with pre- and post- conditions.

def gcd(a, b):
where:
a: int, b: int
a > 0, b > 0
return c where:
c: int
c > 0
c <= a and c <= b
while a:
a, b = b%a, a
return b

3. Interfaces

interface I:
def foo(x):
where:
x: int
x > 0
return r where:
r: str

class C(object; I): # derived from object; implements I
def foo(self, x):
return str(x)

4. More interfaces

interface I1(I2, I3):
def foo(a: t1, b: t2):
where:
a: t1, b: t2
return c where:
c: t3

class C(C1, C2; I1): # derived from C1, C2; implements I1
def foo(a, b):
return a+b

5. Formerly anonymous functions.

list_of_powers = [ powers(x) for x in my_list]:
def powers(x):
return (x, x*x, x*x*x)

6. Formerly anonymous functions with typing information

list_of_powers = [ powers(x) for x in my_list]:
def powers(x):
where:
x: int
return (a, b, c) where:
a: int, b: int, c: int
return (x, x*x, x*x*x)

7. Other optional typing style, meant to be equivalent to above

def type_safe_gcd(a, b):
where:
a: int, b: int
return c where:
c: int

def gcd(a, b):
where type_safe_gcd(a, b);
while a:
a, b = b%a, a
return b

If you "grok" the above, I have made my point. If not, feel free to comment!


10 comments:

André said...

Please note that Blogger keeps messing up with spacing; I tried to indent using 4 spaces everywhere but, when I "publish" the page, the indention gets messed-up.

Anonymous said...

A fabulous idea. I really like it!

Anonymous said...

It's a great idea ..
it's the way to go !!
it has an optionnal form, and a powerful feature !
it's pythonic !
it's THE way !

Anonymous said...

I like it a lot. It lumps object type with other pre-conditions which makes it more flexible and empahsizes the "optional" part. I assume that you could specify pre-conditions without specifying type. Is that your intention?

André said...

Of course, one could have pre- and post- conditions without *optional* static typing. While I did not show any example of this in this post (nor on the following post) it is clearly allowed by this syntax.

Anonymous said...

Yes for the idea, but:

- you show only a way to specify signature variables.
All other locals seems not to be cared of.
Or perhaps,I assume you want to declare them all in your where stuff (!!)

- I can't see the interet of your anonymous functions.
What's the advantage of your example compared to
def powers(x):return x*x

mylist=[powers(x) for x....]

- for the sub-class, I'd rather mix classes and interfaces :
class C(C1,C2,I)
Since interface are already declared with a special statement, no need to differentiate them *again*


--OPQ

Edward Robertson said...

Rather than have to specify a new name "c" when you want to describe the return type, what about using the arrow notation? e.g.:

def gcd(a, b):
where:
a: int, b: int
gcd -> int

I realize that the arrow syntax would be new, but Guido used it in his writings on optional static typing.

Anonymous said...

Case 1 looks a lot like a transcript of what Psyco does behind the scenes. I'd also expect the transcript of a mythical type inference mechanism to look pretty similar. It's therefore quite a convenient notation, but possibly unnecessary as a language construct given a decent enough compiler infrastructure.

André said...

Regarding the comment about using gcd -> int to specify return value. Suppose I have a function that returns multiple parameters; I might want to be able to eventually do something like the following:

return ((x, y, z), state, colour, fname) where:
x: float, y: float, z: float # final position
state: int # moving or at rest
colour: type(Colour) # user-defined
fname: str # temporary filename used to store data

(note: spacing information might be lost by blogger.com in above code.)

i.e. add additional information as required. Furthermore, in my opinion, the notation for type information should be the same whether it is for input or output.

Anonymous said...

I was just thinking how cool it'd be to have a keyword like this. Great minds...