I've been looking around Objective-C a bit, and I would like to turn
over an old rock.

Remember when I suggested that (static) type equivalence should be
based on interface equivalence?  Objective-C seems to feel the same
way.  It calls an interface a I<protocol>, and specifying it is
optional.  This maps nicely into Perl's distinction between typed an
untyped variables.

In Objective-C:

    id untyped = somefunction();
    id<Foo> typed = otherfunction();

If you send a message to C<typed> which isn't in the C<Foo> protocol
definition, you get warnings.  Depending on the implementation, that
assignment might be dynamically interface-checked.

Objective-C had one difference from my proposal, which, upon reading
it, I like.  There is a distinction between a real object and a
protocol.  You can supply the name of a class, and the object has to
(well, as much as anything I<has to>) be a real object of that class.
If you supply the name of a protocol, its interface just has to
conform.

Objective-C makes a lexical distinction on the user side, which I
don't think is so Perlish.

So, here's my new proposal.

    my Type $x = func();
    $x.meth;

First, C<Type> is allowed to be both an interface (my name for
I<protocol>) and a class, even at the same time.  When checking, the
interface takes precedence, but that can be overridden with some
trait.

At compile time, if func() has a visibly declared return value, it is
checked against C<Type>.  If C<Type> is an interface, then func()'s
return type is checked to make sure that it supports all methods that
C<Type> declares.  If C<Type> is a real class and func() is a real
class, they are checked for family relationship.  Finally, if C<Type>
is a real class and func() is an interface, checking is deferred until
runtime  (with a warning under some pragmatic setting).

Then, still at compile time, C<Type> is checked to make sure it
provides C<meth>.

At runtime on the first statement, no checking needs to be done if
C<Type> is an interface (unless func() didn't explicate a return
type).  If C<Type> is real, a family relationship test is done.

And that's all.  It's very lenient, but it catches most of the errors
any Java-like type system[1] would catch, hopefully without getting in
the way.  

And to make type checking even less explicit, there might be a feature
which would scan (as much as possible) the body of a function and
generate interfaces for the variables in the signature based on how
you use them.  That would be neat.

Luke

[1] It would be totally cool to use a Haskell- or ML-style type
inference system, but those things just don't work in procedural
languages.

Reply via email to