[Types-sig] A lurker's comment

lannert@uni-duesseldorf.de lannert@uni-duesseldorf.de
Thu, 16 Dec 1999 20:05:24 +0100 (MET)


"types-sig-admin@python.org" wrote:
  [Apologies first. Although being subscribed to the digest only, I hardly
   manage to follow the current volume of this list. Is there a life beyond
   work, Types-SIG and a minimum of sleep?? The discussion may well be
   past the points I'm addressing at the time of this writing ...]

> Paul Prescod wrote:
> > 
> > Greg Stein wrote:
> > >
> > > I stated a preference for allowing this information to reside in the same
> > > file as the implementation. i.e. I don't want to maintain two files.
> > 
> > The nice thing about having separate files is that it becomes instantly
> > clear what is "interesting" to the compiler. We have no backwards
> > compatibility constraints. We have no questions about what variable are
> > "in scope" and "available". It's just plain simpler.

Please, don't introduce separate spec files. It's OK for a quick hack while
doing a proof of concept, but not for actual use. A C[+-]* program that
consists of .c, .h, .cpp and some other files usually resides in a directory
of its own, but when it's compiled, it usually collapses into just one file
that you can freely move around. I'm already not too happy with a Python
program that needs a few special-purpose modules to accompany it wherever
it goes.

> > There is also something deeply elegant and useful about a separation of
> > interface from implementation.
> 
> It can be helpful, but that doesn't mean it needs to be in a separate
> file. :)

Seconded!

Wouldn't it be a Pythonic solution to regard a restricted namespace as a
"restricted dictionary" which can (a) refuse to accept new items once
it is declared closed (or frozen or fixated), and (b) refuse to accept
values for certain keys unless these values are compatible with a (list
of) type/class/interface spec(s)? (I guess Chris T. had something similar
in mind; hadn't you?)

  d = RestrictedDict()
  d.declare_type("i", IntType)
  d.declare_type("j", (IntType, NoneType))
  d["i"] = 5
  d["j"] = None
  d["i"] = None # raises TypeError
  d.fixate()
  d["spam"] = "foo" # raises KeyError

Modules, classes, and instances can offer this sort of __dict__, providing
type and name safety; for a function's locals() it has to be simulated.

If there is an unambiguous syntax for these restrictions, a compiler can
use them for (OPT):

  def count: IntType  # == __dict__.declare_type("count", IntType)
  def finally         # == __dict__.fixate()

(Or whatever syntax there will be.) Of course the variable declarations
should be performed at definition/compile time; as for "global"s,
the variable must not be used before the declaration.

Anyway, I'd like to have something as open to inspection and manipulation
as Python's __dict__s etc. to achieve type and name safety. (Even a
d.unfixate() would be nice for testing a program with the interpreter.)
And if I knew how to do the declarations right, I'd help the compiler to
implement my count=count+1 as a simple machine code integer increment.

[Tim Peters, iirc:]
> Types form a
> lattice, in which "unknown" is the top element, and the basic rule of type
> checking is that the binding
> 
>     lhs = rhs
> 
> is OK iff
> 
>     type(lhs) >= type(rhs)
> 
> where ">=" is wrt the partial ordering defined by the type lattice (or, in
> English <wink>, only "widening" bindings are OK; like assigning an int to a
> real, or a subclass to a base class etc, but not their converses).

This would also be valid for alternative types:
(NoneType, IntType) >= (IntType,).

An assignment with lhs: (IntType,), rhs: (NoneType, IntType) should not be
rejected by the interpreter if rhs happens to be an Int, but by the
compiler.

Finally, while I'm just bothering you anyway, an irrelevant opinion on the
difficulty of declaring lists of tuples of (int, string, someclass, ...):
Wouldn't a simplistic approach, which leaves the ultimate responsibility
to the user, suffice for the time being? We can't _prove_ the correctness
of a program (yet; did I miss something?), but we can help a human to
avoid the most frequent errors.

  class MyListType(ListType): pass # suppose we have types as classes ...
  def ml: MyListType
  def al: ListType
  
  ml = MyListType([1, 2, "many"])  # OK
  al = ml                          # OK
  ml = al                          # rejected by the compiler
  ml = MyListType(al)              # OK (it's up to myself to do it right,
                                   #     I proved my awareness)

  Detlef