Smalltalk = strongly typed?
Blake
blake at kingdomrpg.com
Thu Oct 14 11:15:44 UTC 2004
On Thu, 14 Oct 2004 00:09:06 -0700, Andreas Raab <andreas.raab at gmx.de>
wrote:
> Hi Blake,
Thanks for responding Andreas!
> Weak vs. strong typing is not primarily about type-*checking* but rather
> about whether an object (reference) uniquely belongs to a type or not.
Well, okay, but what's the purpose of having an object reference uniquely
belong to a type if that never comes into play? OK, so foo uniquely
belongs to class bar, but bar is never referenced, since foo responds to
bie, bix and fee? I mean, at that point, what does "strongly tpyed" even
mean.
Maybe what I'm talking about really is best called explicit typing, which
I see as having the following benefits: The programmer explicitly states
what he's using; the programmer explicitly states what he wants; the
compiler can verify that what he's using matches what he wants. (This
really is neither static nor strong, necessarily.)
> To give an example, in C[++] (which is statically but weakly typed) I
> can do things like:
>
> ((Foo*)0)->size()
>
> e.g., take an integer, "cast it" into a Foo* and invoke methods on it.
> The weird thing is that those methods (as long as they aren't virtual)
> may even work!
That kind of thing used to be possible in Borland Pascal (5.5-7.0).
Actually, it's still possible because the old object model is still there,
but you'd have to work at it. (A static method in a class model that
allows for static objects. It's understandable why it works, even if
there's never a valid reason to do it.)
> In Smalltalk there is no such thing, you cannot trick anyone into
> interpreting a SmallInteger object as being of type Foo. It just won't
> work. That said...
Well, sort of, right? If Foo and SmallInteger receive the same
messages--oh, wait, okay, so, the point is that, yes, it could be used in
the same place as FOO but could never be made to call FOO's methods.
<lightbulb goes on>
> ... your interpretation is entirely correct - if a Foo understands the
> set of messages that a SmallInteger understands it will work
> interchangeably with SmallIntegers. But while that may be true it still
> remains a Foo!
I've got it! "The rain in Spain stays mainly in the plain."
> You are precisely right, except the "it's just a label" part. For the
> advocates of static type checking the ability to coerce between types is
> both a blessing and a curse. It's a blessing because without the ability
> highly generic data types (like lists) are almost impossible to
> implement yet, at the same time, it completely destroys the illusion
> that static type checking would make your program "type-safe" in any
> meaningful way[*1]. In other words, how can you possibly tell whether:
>
> Foo *myFoo = (Foo*)list.getNext();
>
> will answer a valid Foo or not?
Well, Delphi handles it this way:
if Foo is TList then myFoo := TList(myFoo).GetNext;
or, if you prefer:
myFoo := (myFoo as TList).GetNext;
'course, you're not doing squat statically at this point. Arguably, you're
getting the benefits of static type-checking when possible and dynamic
when not.
But MAN, it makes for some UGLY-ASS code.
And, I hope everyone understands, that I'm not dissing ST or saying
Delphi is superior. In some Delphi communities--well, okay, probably every
programming community I've ever been in--I've been known to post things
that garner the occasional response of "Well, if Smalltalk is so wonderful
why don't you go use that?"
(I use a lot of programming languages. About the only job I bever took
that I occasionally regret turning down is one for IBM, who was using
Smalltalk in the '94 winter Olympics.)
> Even worse, how can you say whether the coercion operation is even valid
> (e.g., "understood") by the element you are retrieving from the list?
> Note that the meta-point here lies in acknowledging that only the object
> (receiver) can really decide which messages it (pretends to) understand.
> As such any "external assumption" about the object is prone to fail and
> gives raise to all of the "niceties" of the modern computer world,
> including buffer overruns (hey, the compiler said it'd be okay!) etc.
Well, that goes back to C/++, though, where buffer overruns are a way of
life but, for some reason, LINT isn't.<s>
> [*1] Or at least I haven't seen any such meaningful interpretation. It
> is true (and I won't deny it) that static types help you avoid a few
> mistakes by sending objects messages that they don't understand but in
> reality[*2], if that happens it means you haven't tested your code. But
> then at least you know what you're up against contrary to the situation
> where you have a foo cast into a Bar and wonder how it could have
> possibly gotten into that state. I have debugged programs where this has
> happened and I take a few DNUs every day single over that (in particular
> considering that I can fix them right in the debugger ;-)
Well, I've been reading people's arguments that type CHECKING can be done
without static typing, and it seems reasonable (if way more complex). It's
more of a "hey, you sure about this" kind of checking than a "I won't
compile it" deal, but maybe that's enough.
It seems to me that a compiler could build ad hoc interface requirements
and check methods against the parameters passed to them. Except for this:
> [*2] The ONE good reason for static types is auto-completion btw. This
> really sucks without adequate type-information but I'm still thinking
> that a decent type-inferencer could do a perfectly good job here.
OK, thinking out loud here, but, when using an object, there are two
possibilities: Either you created it locally, and therefore you know what
type it is from that, or it was passed as a parameter--and you should know
how it is going to be used. The local situation is trivial:
i = 1 'okay, you know i is an integer, and can auto-complete for integer
methods
For a routine, you could say something like:
myRoutine: t1#Integer
Then you could auto-complete, though you're really more interested in an
interface than a specific inheritance. No reason to require Integer, if
you're really okay with just Magnitude--and no reason to require all of
Magnitude, if you just want "<=".
Sort of a catch-22: You need to know what you want in order to specify
the interfaces, but you want to specify the interface in order to allow
the environment to help you fill in the interfaces.<s>
I suppose one possibility would be for you to put in the base type you
had in mind (like integer) and then suggest an ad hoc interface when you
finished the routine:
myRoutine: t1#Integer(supports <=, +)
I kind of like that, though not the punctuation of it.<s>
I see the evolution of languages and environments needing to move toward
respect for the distinctions between design-time and compile-time and
interface interaction. I'm not sure the Smalltalk approach of "it's all
design time--or run-time, whatever"<s> is optimal.
===Blake===
More information about the Squeak-dev
mailing list
|