The Artima Developer Community
Sponsored Link

Weblogs Forum
Optional Static Typing -- Stop the Flames!

79 replies on 6 pages. Most recent reply: Feb 4, 2008 9:52 AM by Wolfgang Lipp

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 79 replies on 6 pages [ « | 1 2 3 4 5 6 | » ]
Daniel Fackrell

Posts: 1
Nickname: intchanter
Registered: Jan, 2005

Re: No syntax debate, please Posted: Jan 7, 2005 10:24 AM
Reply to this message Reply
Advertisement
> Most developers use a prefix notation when declaring a
> variable of a certain type. Why not us this as part of the
> language, as indention scoping is? I don't know how this
> would effect function signatures and such, but just
> ponder:
>
> iSomeVariable: int
> or
> def gcd(a: int, b: int) -> int:
>
> vs
>
> def int_gcd(int_a, int_b)
> or
> int_SomeVariable
>
> Obviously int_[code] could be anything ...
> [code]i_
... but I didn't want to promote a cryptic
> syntax.
>
> Just musings from a Python outsider looking in.

I've been following threads in several locations about this, but have been dismayed that what seems to me to be an "obvious way to do it" hasn't yet been mentioned. Maybe I just missed it?

def foo(int(a, b), str(c), list(d)):

This has the benefits that any type or class could be specified, it doesn't mean anything in the language yet (tested on 2.3.4 and 2.4), reads more like natural English (I don't yet know Dutch ;), evokes a similarity to a factory call, and can reduce duplication of type specifiers.

My ignorance:
- implementation
- a way to extend to specifying return types
- extending to more complex specification (though this could be worked around by using these throughout a class)

I do apologize for mentioning syntax, and I promise not to debate it or even post on this article again unless asked.

Nick Coghlan

Posts: 13
Nickname: ncoghlan
Registered: Dec, 2004

Re: No syntax debate, please Posted: Jan 7, 2005 10:26 AM
Reply to this message Reply
> But since 'as' keeps being proposed: the problem with
> using 'as' is that Python already uses 'as' with a very
> different meaning: like in SQL, "import X as Y" is a local
> renaming of X to Y.

I've been a proponent of 'as' for adaptation myself, but recently realised what you describe here: that it conflicts with the 'as for name binding' that import statements already use, and Python 3.0 except clauses are expected to use. (I realised this in the context of a discussion where someone suggested a *third* distinct meaning for 'as')

So, Guido's syntax it is for now, since picking an adaptation syntax is well into the future. I still dislike colons in expressions, though :)

Alex Martelli

Posts: 3
Nickname: aleax
Registered: Jan, 2005

Re: Optional Static Typing -- Stop the Flames! Posted: Jan 7, 2005 10:40 AM
Reply to this message Reply
" +1 " doesn't even come _close_ to describing my delight in this proposal, Guido -- it's just wonderful. As you asked, I'm not even _looking_ at the specific syntax sugar (if I were, I might notice that the colon is even more overloaded in Python than the keyword 'as' -- so, fortunately, I'm not;-); I'm focusing on the semantics.

One great aspect of this proposal is that, with slightly clumsier syntax to be sure, we can start experimenting with it right now, just about -- using a decorator to stand for declarations of arguments, as you suggested, and maybe a metaclass plus some kind of marker to make interfaces, e.g.:

class MetaInterface(type):
def __new__(mcl, cnam, cbas, cdic):
if cbas and cbas[0] is not Interface:
""" this is a class which implements interfaces, deal with that """
else:
""" this is an interface, deal with that """

class Interface:
__metaclass__ = MetaInterface

so, instead of the future syntax:

interface IFoo(IBar, IBaz): ...

we'd have for these experiments to code:

class IFoo(Interface, IBar, IBaz): ...

with Interface as the first base class. A few simple conventions such as these would let us assemble a "coalition of the willing" to do the hard work of sketching out "interfaces" (I'd rather call them "protocols" or "concepts" if they include syntax and semantics and pragmatics -- but if they're just syntax, i.e. method names and signatures, "interfaces" is just fine, I think).

Do you think I should include these issues in the long-promised, not-yet-delivered rewrite of PEP 246? Having 'adapt' work with ``interfaces'' only would make a big difference, etc, etc...


Alex

Stephen Birch

Posts: 3
Nickname: sgbirch
Registered: Jan, 2005

Re: Optional Static Typing -- Stop the Flames! Posted: Jan 7, 2005 10:42 AM
Reply to this message Reply
Although I have been watching python with great interest for about 5 years now, my corp has only just started to actually use it.

I have to say that the inability to optionally specify type in function calls has been one of the biggest show-stoppers.

Thank you so much for starting down the path of optional static typing. Python just keeps getting better.

Steve

D H

Posts: 13
Nickname: dug
Registered: Jul, 2003

Re: No syntax debate, please Posted: Jan 7, 2005 12:38 PM
Reply to this message Reply
> But since 'as' keeps being proposed:

Actually I see Paul Prescod proposed using "as" over 5 years ago when you were debating adding static typing to Python 1.6: http://www.prescod.net/pytypes/#AEN231

And you and Greg Stein gave the same response against "as" here:
http://www.python.org/~guido/types-sig.html
http://www.lyra.org/greg/python/typesys/type-proposal.html

Sorry, I wasn't aware there was already a debate about this back then.

Here is Greg Stein's argument against "as":

"""
I having been maintaining that the word "as" is the wrong semantic for declarations or the type-assert operator. For example, consider the following code fragments:

def foo(x as Int):
...
foo("123")

z = "123"
y = z as Int


In the first example, does the "as" mean that "123" will be converted to an integer and then bound to x? In the second example, will z be converted to an integer before its assignment to y?

I propose the use of a colon for all declarations. For the type-assert operator, I propose the exclamation point. If a word is required (by Guido :-) for the type-assert operator, then "isa" is closest to the proper semantic.
"""

So his proposal is for this syntax:

def foo(x : Int):
...
foo("123")

z = "123"
y = z ! Int #or y = z isa Int


(the answer to both his hypothetical questions is yes, but they would throw an error because you cannot cast a string to an integer. "isa" is used in other languages to check the type of an object at runtime "if x isa string: ...")

Blake Winton

Posts: 4
Nickname: bwinton
Registered: Jan, 2005

Re: implements vs "subclassing" Posted: Jan 7, 2005 1:02 PM
Reply to this message Reply
Fisrt, a syntax suggestion, cause I can't resist. Following the:
def foo( a, b ) -> int:
pass

idea, how about:
class MyClass( object ) -> IMyInterface1, IMyInterface2:
pass

You'll already need to reserve the "->" for return value typing, and the syntax is invalid today. (Also, the interfaces are kind of like the type of the class's constructor... Okay, so that's a bit of a stretch.)

Second, for me at least, the scariest thing about your first two posts were their complexity. Python feels very simple to use currently, and the things you were talking about are inherently complicated. There are a lot of twisty edge cases, and if you start trying to think about and plan for all of them, there is a very large chance of vastly complicating the language. I think you should be allowed to think about them, and even post some thoughts on them, but surely you can understand my worries. This post, on the other hand, seemed to get much closer to the simplicity I've come to know and love (perhaps only because it ignored the edge cases).

Ka-Ping Yee

Posts: 24
Nickname: ping
Registered: Dec, 2004

Re: No syntax debate, please. Posted: Jan 7, 2005 1:02 PM
Reply to this message Reply
Sigh. I guess this goes to show you can't avoid a syntax debate no matter what you do.

Ka-Ping Yee

Posts: 24
Nickname: ping
Registered: Dec, 2004

Re: Optional Static Typing -- Stop the Flames! Posted: Jan 7, 2005 1:29 PM
Reply to this message Reply
I like this proposal a lot. The concepts make a lot of sense to me.

1. Argument types and return types: Perfect. Those are exactly the semantics i'd want.

One question: could you clarify what specifying types for *args and **kwds would mean? I must admit this possibility didn't even occur to me. If a function signature says

def foo(a: int, b: str, *c: int, **d: str):

does that mean c will be a tuple of ints, i.e. each non-keyword argument after the second will be adapted to an int? And does it mean all the values in the dictionary d will be adapted to strings?

2. Attribute declarations: Clever! Also something i hadn't considered, but i think it's a good idea. It will be very handy to have attributes that obey known protocols.

3. Interfaces: Looks great to me. The issue of standardizing protocols isn't addressed, though, and i think it's worth looking at (see my next post).

4. Design by contract: I think your suggestion is workable, but i agree with you that it shouldn't become part of Python until something better is found. Let me just make a couple of points: you described a dichotomy between statement-based and expression-based contracts, with the two options being too cumbersome and too limited respectively.

I don't think it's really an exclusive choice. With a statement-based design, you must define contracts in separate blocks, so the contracts are cumbersome no matter what. But with an expression-based design, you have the option of writing your contracts in expressions or statements — you can always put a block of statements in a reusable function and call it. So using expressions doesn't prevent you from writing more complex guards.

You do have a point that guards don't handle conditions involving multiple arguments well, in terms of violating TOOWTDI. On the other hand, i think this flexibility is a small matter (about the same magnitude as Python letting you put the arguments in any order you want instead of enforcing that the arguments have to be in alphabetical order), and it's outweighed by the simplicity advantage of using one mechanism for both adaptation and contracts instead of introducing a whole separate mechanism for contracts.

Oh, and about defining True.__adapt__(x) to return x — i don't buy your claim that anyone would "just use True while waiting for something better". Huh?

Part of my brain still longs for an even slightly more general mechanism — a single mechanism allowing aspect-oriented programming, for which adaptation and contracts are both special cases. Nothing beautiful has come to mind yet, though.

Ka-Ping Yee

Posts: 24
Nickname: ping
Registered: Dec, 2004

Providing standard protocols Posted: Jan 7, 2005 2:07 PM
Reply to this message Reply
In my wild and crazy fantasies, i wish that Python could do either or both of these things:

1. To implement a class that provides the complete mapping protocol, i only have to write 3-4 basic methods. When i derive my class from a standard Mapping interface, the rest of the methods get filled in for me.

2. I'm writing a function that expects a mapping as one of its arguments. Even if the value passed to my function only implements part of the standard Mapping protocol, i can get the complete protocol filled in by declaring that the argument is to be adapted to a Mapping.

I think that making it effortless to provide standard protocols would be more than just a convenience; it would also make programs more reliable and less error-prone. What i'm not so sure about is the best way to do this.

Strategy 1 (inheritance) is pretty straightforward to use and easy from a language design standpoint: just allow code in interfaces. I'm not sure whether Guido has made a decision about this, but i'm assuming since his post didn't mention it that he's not supporting this idea yet. The inheritance strategy has the advantage of clear semantics, but the disadvantage that you have to rely on your caller to supply an object derived from the correct interface.

Strategy 2 (adaptation) is more flexible from the called function's point of view, because the argument doesn't have to derive from any particular interface. The argument just has to provide implementations for the methods that interface requires (the "basic" methods or what C++ would call the "pure virtual methods"), and then everything will work. The disadvantage is that it's a little more complicated to define a mechanism that will take care of this.

Here's one strawman idea for how to do strategy 2. Suppose that if you define an interface without explicitly writing out its __adapt__ method, it comes with a standard adaptor that does this:
def __adapt__(iface, value):
if implements(value, iface):
return value
if implements(value, pure_virtual_part_of(iface)):
return iface(value)
raise AdaptationError

This example assumes that it's possible to flag which methods on the interface are "pure virtual". If we allow code in interfaces, then these would be the methods with no code (or we could allow a token such as "..." to indicate that a method is intentionally declared with a missing body). The line that says pure_virtual_part_of would look at just that part of the interface and make sure that these methods have been implemented; instantiating the interface then fills in the rest of the methods.

I'm not assuming that this is the only or the best way to do this, but it seems to me that combining these two strategies would give us a lot of flexibility, convenience, and resilience.

More generally, it seems to me that one way or another we have to decide what the default adaptor should do. If we try to adapt an object to an interface and the object implements all the methods in the interface even though its class doesn't declare the interface, should the adaptor still fail?

These ideas also led me to a couple of other questions:

Q1. How should interfaces indicate that they require their implementors to implement certain methods?

Q2. Should a missing implementation for a method cause an error when the class that claims to implement an interface is compiled, when the class is instantiated, when an instance of the class is adapted to the interface, or only when a client of the instance tries to call the missing method? Or at some other time?

Jayson Vantuyl

Posts: 7
Nickname: kagato
Registered: Jan, 2005

Re: Optional Static Typing -- Stop the Flames! Posted: Jan 7, 2005 2:59 PM
Reply to this message Reply
Things That Are Good

I can handle adapt as a function name.

I also like the separate interface syntax well enough to not complain.

Questionables

I would caution against using a colon for type specifiers. The colon is threatening to become Python's curly brace. If there are 18 colons on a line, it will become difficult to read.

What do types on *args and **kwargs mean? Will the internally used tuple and dict be adapted to that type? That's the only thing I can think of that even comes close to being intuitive...

Is it good to evaluate the type expressions at define-time? Could using namespacing to evaluate these save us the later trouble of doing parameterized types?

Serious Issues

I like the interface idea. However, could type specifiers only accept interfaces or types, not classes?

The reasoning goes like this, you can't duck-type integers or strings. They are what they are. This is what types do, it specifies which data is core data--it's primitive. Classes are constructed to implement interfaces of one type or another. This is why could completely hose duck-typing.

If we type based on core-data, then only a certain type or subtype is acceptable, since you can't "imitate" an integer or a string--only subtype them to adjust behavior or add methods. However, when we specify classes, the case is not so clear-cut. I will now describe my worst nightmare.

Take the situation where I have to use a framework that does this:


def file_mangler(x: myFileHandleClass):
__...


Now assume myFileHandleClass does something unacceptable in one of the base __init__'s, (or worse, in __new__). For example, let's say I'm going to put network support into this framework. Before I could implement my own class and duck-type it to behave like the myFileHandleClass.

With non-Pythonic type specifiers, I have the problem of having to either implement a subclass of a broken class or the other coder must have the foresight to use an interface, not a class, in the type specifier. Now, I will get an exception, because my duck-typed class cannot be adapted to myFileHandleClass. There is no good way that an adapt function can predict the duck-typing. This MUST be for Python to remain "My Python". Again, it should not be possible to specify that only a certain CLASS may be passed in.

My gut reaction, then, is to only allow types or interfaces to be specified. In most other Statically Typable languages, classes can be specified here. As far as I am concerned, this is what hamstrings their ability to duck-type. A class is not a type, it shouldn't be treated as one. A class has behavior, that's what's important, and that's what an interface indicates.

Also, it is important to support specifying interfaces post-hoc.

Without either of the above, other programmers mistakes limit my ability to duck-type and clever-glue-class my way out of those mistakes.

Actually, Should interfaces actually be special types? I understand that they wouldn't be useful for actually instantiating data, but perhaps that's what we're looking for. It would be especially useful for bridging the gap created with lists and dicts. Right now, we don't insist that things passed around actually be subclassed from list or dict. We simply duck-type list and dict methods.

This actually has an elegance to it. Our adapt implementation, assuming it attempts to duck-type interfaces, will need to match interface signatures to object signatures. Special cases will be needed for internal interfaces. Additionally, there are some interfaces that are more nebulous than just being an internal type primitive (generators/iterators). If we stash the signature information in the built-in types and have the interface syntax generate abstract types as interfaces, then the adapt information (and adaptation registry) could be encapsulated entirely in the existing type structures.

Look at the utility of the following example:


from types import types.IteratorType
# Note, this type is not actually instatiated, it just exists to integrate interfaces into the base language

interface RestartableIterator(types.IteratorType):
__def restart():
____...

class randomNumberGenerator implements RestartableIterator:
__...

def encrypt(r: types.IteratorType):
__...


This example means what it says and says what it means. No wierd inheritance acrobatics involved to trick it into accepting our class.

Summary

Again, I cannot stress enough, enforcing a certain CLASS is folly. Enforcing a certain TYPE is not (with some informational types provided, such as IteratorType, NumberType). Making interfaces generate types, retrofitting built-in types, making a few informational types, and allowing post-hoc interface specification will make this a feature with real utility.

Self-Criticism

You probably have a different feeling for types than I do. It might be there is an impedence mismatch between what you envision a type is and what I do.

Arnold deVos

Posts: 18
Nickname: arnoldd
Registered: Dec, 2002

Switching adaption on and off Posted: Jan 7, 2005 3:34 PM
Reply to this message Reply
I think people will quickly adopt any standard notation for function signatures despite quibbles about the details and fears that it would spoil the language. Reason: people are already inventing such notations and using them in docstrings. It is hard to accurately describe your function to your users in plain english.

Driving dynamic adaption from these declarations makes python more dynamic. The topic should be "Optional Dynamic Features" instead of "Optional Static Typing"!

My question: how can we turn off adaption when it is not wanted but leave it on in places where it is (and where the semantics of the program depend on it)?

Perhaps a per-module switch or some kind?

I know one can always redefine adapt() globally - but that might break some modules that use it. One can always opt out of the adaption protocol by defining a __conform__ method that raises the appropriate exception - but it should be possible to opt out with zero runtime overhead.

Guido van van Rossum

Posts: 359
Nickname: guido
Registered: Apr, 2003

Re: Switching adaption on and off Posted: Jan 7, 2005 3:51 PM
Reply to this message Reply
Arnold de Vos: "how can we turn off adaption when it is not wanted but leave it on in places where it is (and where the semantics of the program depend on it)?"

By not using the optional syntax for declaring argument interfaces:

def foo(a, b):
...

will not invoke adaptation for its arguments.

I don't think it would be very Pythonic to have a way to let you use the notation while disabling its intended semantics.

Phillip J. Eby

Posts: 28
Nickname: pje
Registered: Dec, 2004

Re: Optional Static Typing -- Stop the Flames! Posted: Jan 7, 2005 3:55 PM
Reply to this message Reply
> I like the interface idea. However, could type specifiers
> only accept interfaces or types, not classes?
>
> ...snip...
>
> Take the situation where I have to use a framework that
> does this:
>
>
> def file_mangler(x: myFileHandleClass):
> __...
>

>
> Now assume myFileHandleClass does something unacceptable
> in one of the base __init__'s, (or worse, in __new__).
> For example, let's say I'm going to put network support
> t into this framework. Before I could implement my own
> class and duck-type it to behave like the
> myFileHandleClass.
>
> With non-Pythonic type specifiers, I have the problem of
> having to either implement a subclass of a broken class or
> the other coder must have the foresight to use an
> interface, not a class, in the type specifier. Now, I
> will get an exception, because my duck-typed class cannot
> be adapted to myFileHandleClass.

Try this:

class DuckType(object):
def __conform__(self,protocol):
return self


And then inherit from it. Your DuckType subclasses will happily claim they support any type, even concrete types. For extra credit, expand the definition of __conform__ to actually check for matching public signatures, and only return self if they match.

Alternatively, the default __adapt__ of class objects could do the same thing, i.e. check for conformance of the class' signatures and then cache the result for future reference.


> Again, I cannot stress enough, enforcing a certain CLASS
> is folly. Enforcing a certain TYPE is not (with some
> informational types provided, such as IteratorType,
> NumberType). Making interfaces generate types,
> retrofitting built-in types, making a few informational
> types, and allowing post-hoc interface specification will
> make this a feature with real utility.

Allow me to rephrase/redirect: instead of forbidding classes to be used as types, simply treat a class as an *implied* type based on signature. This is actually pretty expensive computationally, but it might be easier to get people to refrain from using concrete classes instead of interfaces if you can convince them it's bad for performance. :)

Arnold deVos

Posts: 18
Nickname: arnoldd
Registered: Dec, 2002

Re: Switching adaption on and off Posted: Jan 7, 2005 3:56 PM
Reply to this message Reply
>
> I don't think it would be very Pythonic to have a way to
> let you use the notation while disabling its intended
> semantics.

No way to use the notation just for documentation, PyChecker and other static analysis without getting the runtime adaption semantics?

Guido van van Rossum

Posts: 359
Nickname: guido
Registered: Apr, 2003

Re: Serious Issues Posted: Jan 7, 2005 5:13 PM
Reply to this message Reply
Jayson Vantuyl: "There is no good way that an adapt function can predict the duck-typing."

I think PEP 246 actually has a provision for this. Your class that duck-types myFileHandleClass can implement an __adapt__() method that returns self when asked to adapt to myFileHandleClass.

Even if both myFileHandleClass and the class that duck-types it are 3rd party classes that you can't modify, I believe PEP 246 has a provision whereby you can tell adapt how to adapt one to the other by modifying a global table.

Flat View: This topic has 79 replies on 6 pages [ « | 1  2  3  4  5  6 | » ]
Topic: Optional Static Typing -- Stop the Flames! Previous Topic   Next Topic Topic: Setting Up for Emulation

Sponsored Links



Google
  Web Artima.com   

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