Front page | perl.perl6.language |
Postings from April 2003
Re: Static typing with Interfaces
Thread Previous
|
Thread Next
From:
Austin Hastings
Date:
April 17, 2003 14:35
Subject:
Re: Static typing with Interfaces
Message ID:
20030417213536.39609.qmail@web12304.mail.yahoo.com
--- Luke Palmer <fibonaci@babylonia.flatirons.org> wrote:
> =head1 Static typing with Interfaces
>
> In Perl 6, the drive to add strict static typing seems like a bad
> decision,
> according to this article:
>
> http://ootips.org/static-typing.html
The article is pretty biased, it seems to me, but it does make some
good points.
> What they're saying is true, but I don't think the attack on static
> typing is well-founded. Rather, it's the specific methods of static
> typing used by Java and some parts of C++ that deserve criticism.
>
> This document describes a method of static typing that may be able to
> provide
> the run-time guarantees like those of Java and C++, without affecting
> too much
> the versatility of the language. The type system of Apocalypse 6 is
> used,
> and this just proposes a method of type checking to go along with it.
>
> =head2 Why check at all?
>
> It's obvious why types in general are needed: multimethod dispatch,
> interfaces to other typed languages (like C), and all the stuff
> mentioned in A6. But is static type checking really useful?
>
> C++ checks types because it needs to. It needs to assure the memory
> it gets is actually pointing to an object of the proper size with
> the proper things at the proper offsets. Java checks types because
> it's pedantic. But a dynamically typed language like Perl doesn't
> have any reason to.
>
> But it's a good idea from a couple perspectives. It helps catch
> certain bugs, it helps out the optimizer, it's good documentation,
> and it can be useful in generic programming.
Not to mention, as pointed out in A6, that two diffent objectmethods,
or two different multis, may provide different context to their args.
Not knowing what the context of your call should be is likely to
produce some REALLY slow code.
> Enough convincing. On to the argument.
>
> =head2 The idea of an Interface (and how Java missed the point)
>
> Early(er) in the childhood of software design, the OO guys came up
> with the idea that you could use interchangable object
> interchangably. So many a software designer came up with pages of
> inheritance heirarchies to solve their problem. And static typing
> made sure no object that wasn't derived from the kind expected was
> actually used. And life was good.
>
> Then entered reuse and maintainence. People started to complain that
> they wanted to use I<their> object in someone else's code without
> deriving from their stupid class. And thus the idea of an interface
> was born. "As long as you assure me, by deriving from my special
> assuring class, that you can do I<these> things, I'll use you in my
> code."
>
> That turned out to be a stupid idea too. Or, rather, a stupid way to
> manifest a good idea. Whenever you wanted to make a function that
> required a new interface, you required anyone that used it to tell
> us explicitly, with an C<implements> declaration, that it's
> compatible.
The point here being that having to explicitly modify a class
declaration to retroactively add an C<implements> directive to detail
things you may ALREADY have methods for is pretty lame.
OTOH, declaring C<implements> is a good piece of static typechecking in
itself, especially for those "dusty corner" methods that only get
called under weird circumstances.
>
> In the meantime, C++ nailed it (at compile-time) without even
> realizing it. Using the C++ "template" feature, you could say that
> you wanted a certain interface without having the client knowing at
> all that you wanted it. If their object provided the interface, the
> code would compile, and otherwise it wouldn't. Brilliant.
You have *NO* idea how hearing someone call C++ templates "Brilliant"
makes the hairs on my neck stand up. But I take your point.
>
> =head2 Where Perl might go
>
> In the world of C++ outside of templates, it still requires explict
> heirarchy compatibility, which leads to lots of casts and over-
> specifications everywhere. But Perl is devoid of the implementation
> restrictions of C++, so it needn't be so strict about heirarchy
> conformance.
>
> Why not just unify the idea of type and interface for the type
> checking phase? That is, if it acts like a Foo, it is a Foo. And
> it doesn't need to say it acts like a Foo, the compiler can figure
> that out. It's just like Perl 5's type checking, except at
> compile-time when it's actually useful.
>
> The following generic code:
>
> class HasNodeList {
> method nodelist() returns List { ... }
> }
> sub traverse(HasNodeList $root, Code $code) {
> $code.($root);
> traverse($_, $code) for $root.nodelist
> }
>
> Would Just Work, for whatever class of $root, so long as it has a
> nodelist() method that returns some kind of list (or something that
> I<behaves> like a list). If $root's class doesn't have such a
> function, it would be caught at compile time.
So here you advocate declaring "interface" types to specify the
required bits of external objects, right?
(Obviously when referencing classes that are explicitly enumerated in
your source code you'll have full access to those things.)
>
> =head2 Fitting with other paradigms
>
> There are, of course, times when this doesn't work so smoothly.
> Certain interfaces can't be determined at compile-time. These
> include both objects whose methods may dynamically change, and those
> classes which delegate to an object of varying type.
>
> These I call "dynamic" types. Their behavior is too complex to be
> analyzed at compile-time, so they're just deemed with the "universal
> interface" (one that supports everything, with caveats) in the type
> checking phase. When run time rolls around, they may be checked on
> the spot or just run, in accordance with a pragma.
>
> If an object C<AUTOLOAD>s or specifies itself with an C<is dynamic>
> trait, it is considered dynamic. If an object derives from or
> delegates to an object of a dynamic class, it is also dynamic. The
> default polymorphic scalar is a dynamic type, because it delegates
> to its stored reference if it has one.
>
> When a dynamic object is assigned to a statically-typed variable,
> type checking just accepts it. At run time, the I<assignment> is
> checked for type compatibility, reducing bugs (and increasing
> efficiency) later on. This also applies to binding.
So going back to (was it John's) example:
class Vehicle;
class Car is Vehicle;
class MotorCycle;
class ParkingLot is Array of Vehicle {
method park(Vehicle $v) returns Int {...}
}
my @lot = ParkingLot.new(100);
my $vette = Car.new(type => Corvette);
my $space = @lot.park($vette);
{ my GangMember&CrackHead&Male $crook; $crook.StealRandomCar(@lot); }
{ given $newcomer { @lot.park(.wheels); } }
$vette = @lot[$space];
give $driver {
$CART.UnlockViaRemoteControl();
when Gentleman { $CART.OpenDoor($girlfriend); }
when Redneck { say("Get in, bitch!"); }
}
The problem is that you're potentially losing data again, which means
you're giving up the static typing bits. But if $vette winds up being a
Motorcycle, kaboom.
Perhaps the right balance here is for there to be a collection of
classes associated with each object (including classes). When the
compiler looks up the method or multi being called, it records the
class used to confirm the validity of the invocation (or undef). Then
if the object is associated with the same class as the
method-call-lookup, there's a fast "go ahead". Otherwise, there's a
full-up easter-egg hunt for the method.
This "granular class" idea has a lot to it. More thought required.
=Austin
Thread Previous
|
Thread Next