Squeak SmalltalkJoker Squeak Smalltalk : Philosophy : prevnext Strong Weak Static Dynamic Typing

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.