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

Greg Stein gstein@lyra.org
Mon, 3 Jan 2000 15:58:04 -0800 (PST)


On Mon, 3 Jan 2000, Paul Prescod wrote:
> Greg Stein wrote:
> > 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.
> 
> Dividing the language is inevitable. The question is whether to do it
> "up front" or subtly through cryptic error messages.

You are presuming that the alternative to "up front" is "cryptic error
messages". Again: this is FUD.

I do not believe that smooth integration necessarily leads to cryptic
error messages. Not at all.

I am certainly biased, but I believe that my syntax suggestions and
run-time and compile-time models are quite clean and natural-feelings
extensions for Python. And I see them as *extensions* rather than a
distinct wart glued into the existing Python.

> > > 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."
> 
> I don't want the type-checker flagging valid code with warnings (unless
> you turn -Wall) and I don't want it silently ignoring code like the
> above because it doesn't understand it.

I agree. I've been presuming that a number of warnings will only be
flagged with -Wall. Whether my explanation on what happens with your
example truly issues a warning or not... fine-tuning.

And I never said that it would ignore the above code. I think the
type-checker will be well-aware of what is happening and can provide
several options for us to choose from in its response.

Your syntax with a "typedef" statement attempts to avoid the problem by
restricting the syntax to an arbitrary subset of Python. My proposal
allows people to use similar, limited syntax *or* to use complex stuff for
complex, runtime operations.

> You are setting up three levels of conformance:
> 
> 1. Checked at compile time.
> 2. Seems like it *might* be checked at compile time but you get a
> warning telling you that it doesn't.
> 3. Checked at runtime.
> 
> I vote to remove the middle option by clearly flagging which things fall
> into the former and latter categories.

Fine. We can declare your example "illegal". I was simply trying to state
that it *can* be legal. Look at my words, quoted above. I was attempting
to move it to #3 -- runtime only. Even though we move it there, the
type-checker can tell that the user may have thought it fell into #1, so
it can issue a warning.

But hey: this is what Guido calls, to paraphase, "appropriate error
messages." Some fine-tuning is all.

It does not invalidate my original point: there is no need to separate
compile-time and run-time syntax/semantics.

> Also, what do you do about forward declarations? They are not a problem
> with my syntax.

I have said this repeatedly. I will repeat one more time:

==> "incomplete" classes and interfaces may be declared

Effectively, this enters the name into the namespace for usage, but the
actual interface is not yet known. As an example, we do the following in C
code:

struct foo_object {
  struct foo_private * priv;
  struct foo_object * next;
};

struct foo_private {
  struct foo_object * owner;
  int data;
};

These are mutually recursive structures. To model the same thing in
Python, we do the following:

decl incomplete class foo_private
decl incomplete class foo_object

class foo_object:
  decl member priv: foo_private
  decl member next: foo_object

class foo_private:
  decl member owner: foo_object
  decl member data: Int


Technically, the "decl incomplete class foo_object" could possibly be
omitted because the "class foo_object" will automatically create that,
then fill in the final interface at the end of the classdef.

In the above scenario, all names are available and their semantics are
well-defined at compile-time and run-time. The human-level semantics are
well-understood, as we have analogies in other languages.

In this example, there is no separate, distinct namespace. We continue to
use the standard Python triplet of namespaces (local, global, builtin).
Everything looks and operates quite normally.

The PyClassObject would modified to allow for an update to its bases and
methods (at the end of the classdef); an incomplete class would raise
errors if somebody attempts to instantiate one.

> > 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.
> 
> The partitioning is not avoidable. Some objects are evaluated at 12:35
> when the static type checker runs. Other objects are evaluated at 12:27
> when the code starts to interpret. You can hide that and then issue
> warning messages but all you have done is *hide it*. I think "be
> explicit" is more Pythonic.

I don't believe anything has been hidden. Instead, I am trying to use
standard Python language, conventions, syntax, and idiom as a leverage
point for a new type declaration/checking system.

Introducing new sub-languages through a single "decl" statement, or adding
a new namespace, are both items that I believe are too divergent. I also
believe they are wholly unnecessary and have provided explicit solutions
to avoid them.

Yes, "decl" is needed, but it shouldn't be a trap door, catch-all solution
that some people want to view it as.

> > 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 don't think word-smithing will help. The problem is that you've taken
> an unavoidable, temporal distinction, papered it over, and then
> re-introduced it through warning messages. That is unavoidably
> confusing.

Oh, bunk. I was attempting to make it actually *work* for the user. In
your model, it would just punt. You are creating artificial limitations,
and you're doing it through a partitioned syntax. Neither is desirable.

If somebody says:

  MyComplicatedType = doSomething()

  def foo(x: MyComplicatedType):
    ...

Why should we prevent them? I say, let them do it, but tell them they
aren't going to get compile-time assistance with the construct.

Keep the syntax clean. Provide flexibility. Let the developer know if they
have done something suspicious.

Cheers,
-g

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