[Types-sig] parameterization and syntax
Greg Stein
gstein@lyra.org
Mon, 3 Jan 2000 19:25:15 -0800 (PST)
In going over some of this stuff, I've been thinking more on
parameterization so that I can nail down a concrete syntax proposal.
Here are the items that I believe we'd like to parameterize:
* classes
* interfaces
* function signatures
* builtin types (such as List)
Classes may be parameterizable by virtue of parameterized interfaces.
Builtin types (such as List) could also be parameterized via interfaces.
To constrast: non-function variables/attributes are not parameterizable in
isolation, but may be parameterized via a parameterized interface.
SIMPLIFY
--------
To reduce the problem set, let's start with a few simplifications. First:
a class definition and an interface declaration are nearly identical in
syntax, so the two should also share parameterization syntax. For example:
class Foo(Base1, Base2):
decl member a: Int
def b(x: Int):
pass
interface Foo(Base1, Base2):
decl member a: Int
def b(x: Int):
"doc string only"
Second simplifying assumption: an interface can be associated with builtin
types. Interfaces will be expressive enough to provide for the
parameterization of things like List, Dict, and Tuple.
RESULT
------
We are now concerned with parameterizing interfaces and functions.
INTERFACES
----------
The syntax that has been introduced seems to work well. Notably:
interface (_X, _Y) Foo(Bases):
decl member a: _X
decl member b: _Y
My only comment here is that usage places the _X and _Y *after* the name
Foo. For example:
def bar(x: Foo(Int, String)):
...
I think that I would like to suggest a little borrowing from the C++
template syntax:
interface Foo<_X, _Y>(Bases):
...
def bar(x: Foo<Int, String>):
...
This makes it very clear that we are not performing a function invocation
when the concrete interface is constructed (in the bar() definition). This
also allows us to place the _X and _Y parameters after the name "Foo" and
to even use the same syntax between declaration and usage.
[ note that we shouldn't say: interface Foo(_X,_Y)(Bases) due to grammar
problems. it may be possible, but rather ugly grammar-wise. ]
There is conceivably a parseing issue with things like:
x = y ! SomeType<Int>
When the '<' is seen, it is unknown whether it is part of the typedecl, or
part of the outer expression. It would be quite easy for the parser to
make a call, but it is also arguable on whether that ambiguity should even
be left in (note: the Python grammar *does* have some ambiguities that
Guido resolves at compile-time).
An alternative to the < > markers are braces:
interface Foo{_X,_Y}(Bases):
...
def bar(x: Foo{Int, String}):
...
There are no ambiguities here because braces are not operators like
parentheses or angle-brackets.
[ I just realized that: x = y ! SomeType(Int) has similar binding
precedence issues. is (y!SomeType)(Int) or y!(SomeType(Int)) ]
Also, braces are never allowed to occur just after a dotted_name, meaning
that "dotted_name { typedecl-list }" can always be correctly parsed.
OPEN ISSUE: parenthese/brackets/braces.
OPEN ISSUE: placement of parameters in the "interface" line
Operation of type-parameters: in the above examples, we have declared _X
and _Y to be type-parameters that will be bound to a particular type. The
operational model is that the names are in scope for the duration of the
"interface" suite (this applies to "class", too, remember). Any type
declarator within the scope may use these names. A "parameter-reference
type declarator" will be constructed (at compile/run-time) which holds the
type-parameter name and type-parameter index. When the interface is made
concrete, the reference evaluates to the concrete type declarator passed.
FUNCTIONS
=========
Functions have a number of type declarators associated with them: argument
types and a return type. Being type declarators, they can use any
type-parameter name that is placed into scope through a parameterized
interface.
However, a function may need to be parameterized *outside* of an
interface, or to use additional type-parameters within a parameterized
interface. Specifically, consider the following function:
def Add(x: _X, y: _X) -> _X:
return x + y
The question here is, how do we introduce the parameter name _X?
==> we don't want to use a notation similar to interfaces because we
won't be creating a concrete form of the function -- we're just saying
(in this example) that its argument types must match and the return
type will match that.
==> we have to declare _X somehow so that we can distinguish between a
NameError and an intended parameterization.
Note: a type-parameter name must appear at least twice, to establish a
relationship among the function's type declarators. Something like:
def whatever(x: _X)->None: ...
does not make any "rational" sense.
==> we could introduce a token, such as "param":
def Add(x: param _X, y: param _X) -> param _X:
This is a bit wordy, but works. If we want to cut down on the words,
then where does the token go? The first occurrence? That creates a
weird "the first one is special" rule that doesn't normally occur in
Python.
==> we could go ahead and use braces:
def Add{_X}(x: _X, y: _X) -> _X:
and just live with "but you aren't supposed to call the function by
saying Add{Int}(x,y)".
==> other ideas? I'm out
Note that it will also be important to mix parameterized interfaces and
functions:
interface MySequence{_X}:
def getitem(x: _X{_Y}) -> _Y:
"doc"
f = MyClass() ! MySequence{List}
l = something() ! List{Int}
z = f.getitem(l) ! Int
OPEN ISSUE: how do we declare that a name is a type-parameter in a
function definition/declaration?
Cheers,
-g
--
Greg Stein, http://www.lyra.org/