[Types-sig] namespaces

Greg Stein gstein@lyra.org
Fri, 21 Jan 2000 07:43:54 -0800 (PST)


On Fri, 21 Jan 2000, Guido van Rossum wrote:
> [Greg Stein]
> > '!' can *definitely* invoke a compile-time check:
> > 
> >   x = 5
> >   y = x!string  # Boom!
> > 
> > The operator also provides information to the type-checker.
> 
> Greg, you've used this phrase before, and I wonder if we're on the
> same sheet here.

We are. I just keep hearing that "it only does runtime" or whatever, and
respond to that. When I first raised the issue of the operator, nobody
seemed to understand me when I described the compile-time aspects, so it
was being dismissed as not-useful in a static checker. After I while, I
think that I found the right way to explain its behavior, but I still have
that reflex built in :-)

>...
> Well, we won't have to guarantee that we will figure it out for
> arbitrary expressions.

I do agree with this. I have to :-) because I think we should be able to
do things like:

  MyType = some_function()

  def foo(x: MyType):
    ...

> We can make isinstance an operator (reserved
> word) and require that the left argument is a variable (maybe a dotted
> name) and the right argument is a type expression evaluatable at
> compile time.  Or we can say that the type checker only uses
> the information conveyed by isinstance if these conditions are met.

It is this last part that has me squeamish. "If you do this and that, and
also this, then you get the benefit."

Yes, I understand that we can definitely use the isinstance() pattern (as
an operator or remaining as a function) to correctly disambiguate. But I'm
just a bit uncomfortable with the rules that would need to be followed.
The "magic" that I was referring to.

If you actually alter the syntax to enforce something like:

  isinstance_expr: 'isinstance' '(' dotted_name ',' typedecl ')'

Then we would avoid "Wrong Forms" and people wouldn't have to remember
particular rules (to me, it is easier to remember syntax than to remember
syntax plus a set of rules for using that syntax)

> > Moreover, it is difficult to "temporarily" record "int" for "x"
> > within the if: block (up to that point, we think x is int|str).
> 
> This seems to be an argument based on your current idea of how a
> typechecker should be implemented.  I don't think it's difficult at
> all (and besides, you said difficult, not impossible).

Just "difficult"... yes. I could certainly build it, and it might even be
reasonably easy because I want to correctly handle:

  # no declarations
  if whatever:
    a = 1
  else:
    a = '1'
  # merge the types, label 'a' as int|str

In this sense, we are tracking separate type-sets for each suite. Labeling
"x" as an "int" for a branch might simply be altering the "initial"
type-set for each branch.

>...
> >   if isinstance(x, int):
> >     print "an int: %d" % (x!int)
> >   elif isinstance(x, string):
> >     print "a string: %s" % `x!string`
> >   elif isinstance(x, None):
> >     print "nothing"
> >   else:
> >     print "don't know"
> 
> My problem with this is that in this version each type check is
> executed twice: first the isinstance() call, then the ! operator.

Agreed.

> Because (in your world :-)

:-)

> the code generator isn't smart enough to
> understand the meaning of the isinstance() call, it won't know that in
> x!int, x is always an int, so it will insert another runtime type
> check.

Correct. However, a smart one *would* know and could omit the runtime
check. That option is not foreclosed by my proposal.

If you believe that it is "not hard" to do the type handling, then I'll be
happy with that... it helps to support my proposal :-)  We would end up
with just one check per branch; just like the typecase-statement proposal.

>...
> >     if isinstance(x, int):
> >       return x!int + 5
> >     # this could be an else: or omitted (because of the preceding return)
> >     if isinstance(x, str):
> >       return x!str + 'hi'
> 
> Same counter-argument: each type check will be coded twice in the
> bytecode.

Right.

> (Small nit: you alternatingly demonstrate ! as having a very low
> priority and having a very high priority.  Please make up your mind.)

Just call me feeble :-)

For some reason, I brain-farted and thought the '+' would terminate the
typedecl and create an implied left-grouping. But no... instead we get
x!(int + 5) and the '+' is invalid in a typedecl.

> > Specifically, I think we should use if/elif/else statements rather than a
> > typecase. In combination with the '!' operator, we successfully pass the
> > type-check step without errors.
> > 
> > It is true that isinstance() is a bit more wordy, but I doubt the above
> > code patterns will be all that common. (can somebody supply some
> > examples?)
> 
> Grep the standard library for 'type('; I found about 150 occurrences.
> Most of these are in tests that should be translated to using
> isinstance().  Quite a few are for functions or constructors taking
> either a file or a filename, e.g.:

Hrm. Good point.

>...
> > Therefore, I'd rather avoid introducing the typecase syntax -- not enough
> > benefit for the cost of the additional syntax.
> 
> Well, new syntax is new syntax.  To me, the cost of adding ! seems
> just as high as the cost of adding typecase.

Hunh. A single, new operator seemed simpler to me than the typecase
statement, its branches, etc.

> But the real difference of opinion is our judgement about which will
> be more common.  I expect that checked modules using unchecked modules
> is rare, and disambiguating union types is common; you expect the
> reverse.  Only time will tell...

True.

The condition I had in mind is using a third-party library. If I sent you
my httplib-in-development, then I probably don't have type annotations in
it yet. However, you're going to drop it into your app to test.

Grabbing modules from here and there, they may not be checked. But yah...
you could also argue they would have checks added to them before release.
But what about developers that don't care to do so?

Cheers,
-g

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