I've been reading a lot of discussions of strong/weak typing versus
static/dynamic typing.
I've seen a number of statements that assert that Smalltalk, while
dynamically typed, is also strongly typed.
Dynamic, I get. Typed, I get. But strongly typed? Am I mistaken in
thinking there isn't really any type-checking at all? As long as an
object can accept the right messages, it will work, regardless of the
context. That is, if I had a routine with two parameters, "a" and "b",
and it expects both to be an integer:
c := a plus: b
If a and b happen to be strings, this code will still work. Or if
they're ingredients to a recipe or...whatever, as long as they have a
"plus" method.
Is this true? Anyone have opinions on this? I mean, I guess it's just
a label in the long run, but--well, I work a lot with Pascal so
strong, static-typing, compile-time checking is the norm for me.
--
Weak vs. strong typing is not primarily about type-*checking* but
rather about whether an object (reference) uniquely belongs to a type
or not. 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! 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...
... 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!
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? 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.
[*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 ;-)
[*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.
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? 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.
[*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 ;-)
[*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.