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.