[Types-sig] Re: syntax and compile/run -time

Greg Stein gstein@lyra.org
Sun, 2 Jan 2000 16:26:23 -0800 (PST)


On Fri, 31 Dec 1999, Paul Prescod wrote:
> Greg Stein wrote:
> > ...
> > > And I put decl
> > > and typedecl at the front instead of making them operators because I
> > > agree with Tim Peters that we are designing a sub-language that needs to
> > > be understood as being separate by virtue of being evaluated BEFORE the
> > > code is executed.
> > 
> > I disagree. Making a "sub-language" will simply serve to create something
> > that is not integrated with Python. I see no reason to separate anything
> > that is happening here -- that is a poor requirement/direction to take.
> 
> Compile time stuff is inherently separate because it is *compile time
> stuff*.

So? The type checker uses a lot of runtime-like stuff to do its work.

  x = y + z

That is arguably runtime, but the type checker will be handling it at
compile time -- it has to understand "y" and "z" and what happens when you
use the "+" operator on them, to produce a result for assignment to "x".

Dividing the language into pieces is simply dividing it. I see no rational
reason for doing so, but many reasons to keep everything clean and
integrated.

> It follows different import rules, it is evaluated in a
> different namespace, and so forth.

The import rules are barely different. It looks for an interface file and
reads that, but there isn't any reason it couldn't also find/read/parse a
module file to extract an interface.

As I've attempted to explain in other notes, I don't believe a different
namespace is required.

> This, for instance, is not legal:
> 
> a = doSomething()
> b = typedef a

It certainly can be legal. But the type-checker would say "I can't help
you here."

Note: you don't need the typedef in the above expression. The typedef
operator simply serves to provide access to a typedecl object (and its
particular construction syntax). Generally, you would only need a typedef
for things such as:

  IntOrString = typedef Int or String
  ListOfInt = typedef [Int]

It is also helpful to note that the type-checker has no problem with the
above statements.

You wouldn't need a typedef for things like:

  IntAlias = Int
  MyClassAlias = MyClass
  MyIfcAlias = MyInterface
  String = type("")

In each of these cases, the LHS is assigned an object that can be used in
type declarators later in the program. A name in a type declarator can
refer to a base type, a class, an interface, or another type declarator.

Type declarator objects are usually only used for complex typedecls --
alternates such as IntOrString, or parameterized (abstract or concrete)
typedecls.

Even with the above statements, the type checker can easily track them and
use the names later on (again: it has to track these kinds of things just
as part of its normal job).

> Python programmers need to understand these sorts of things. The decl
> syntax and "gpydl" semantics makes it very clear that these declarations
> are *separate* and are evaluated in a different time in a different
> execution context with a different namespace.

I believe this distinction is unnecessary and is on the wrong track. I do
not believe there is a requirement to create a partitioning of the
language and its semantics. It is much better to leverage the existing
semantics of Python than to create a whole new set.

There are very few constructs that a person will attempt, for which we
cannot track the implied type information. We can easily identify those
situations and warn the user that we cannot provide a compile-time check
for them.

Specifically, in your example: if "a" or "b" was used in a later typedecl:

  def foo(x: a)->None:
    ...

We Could issue a warning that might read, "The type information
represented by <a> is not available at compile time; compile-time checking
is not enabled for parameter <x> in the function <foo>, nor for calls to
<foo>."
[ well, some suitable word-smithing is needed there :-) ... but you get
  the idea. ]

I believe the only real time that we can't tell is when people try to use
type information pulled from indexing, a function call, or from an
attribute of a class instance. In these cases, we won't know the value, so
we cannot perform the requisite checks later on.

However: I don't believe that will happen. Most uses are going to be very
clear -- one of the major reasons you and other have been pointing out is
the documentation aspect of type declarators. If people get funny with
their declarators, then it ruins the doc aspect.

Even if it does happen, then a simply warning is all that is required.

And hey... maybe the *are* doing something complex at runtime with the
objects and we should not disallow that construct.

> > The typedef unary operator allows a Python programmer to manipulate type
> > declarator objects. That will be important for things such as an IDE, a
> > debugger, or some more sophisticated analysis tools.
> 
> This is a completely orthogonal issue. There is no syntax in Python for
> a traceback or frame object but IDEs can work with traceback and frame
> objects. Classes are not created by a unary operator and assignment but
> they are still runtime-available objects.

The operator is needed to distinguish things like:

  a = Int or String
  b = typedef Int or String

Both of the above are valid Python statements, but they have entirely
different semantics.

My point is that I think we want a typedef operator because we want to
expression the notion of creating a typedecl object and assigning that to
a variable. We don't generate tracebacks and frames out of the blue -- the
interpreter gives us those in particular cases. Type declarators are quite
different. Classes and functions are different: they have an associated
suite, so an assignment does not make sense for them.

A typedecl can be used within an expression or assigned to an object.
Using the "typedef" unary operator makes more sense, than to have a
side-effect from a "decl typedef" statement.

The use of an operator also avoids the artificial distinction that you are
trying to draw between compile-time and run-time syntax. Those two are so
intertwined that a distinction ought to be avoided.

Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/