justPassingThrough: anObject <X def> ^<X> ^anObject
Each time we send this message, the type system will define X to be the type of whatever you pass in as the argument anObject. This way, the return type will automatically change for each send, to the type of the argument you pass in. This adds a huge amount of flexibility to the type system.
Here is a really important example of this construct. Suppose we have a dictionary that maps keys of type K to values of type V; its type would be <Dictionary[K,V]>, according to the way the generic class Dictionary is defined. Here is how we could write the signature of the #at:ifAbsent: method:
at: key <K> ifAbsent: failBlock <[^FAILVAL def]> ^<V|FAILVAL>
This says the method takes a key of type K, and a fail block that yields some type we will call FAILVAL that can vary depending on the block we pass in, and the method returns either a value of type V or the result of executing the failBlock, which is of type FAILVAL.
at: key <K> ifAbsent: failBlock<[^FAILVAL]> ^<V|FAILVAL> {{where FAILVAL is returnType of #value message of arg 1}}
Just read this almost literally: for each send of #at:ifAbsent:, FAILVAL will be defined to be the return type of the #value message of whatever block we pass in for argument 1. That's another way of figuring out what type the block produces. You can do much more sophisticated things with inference clauses. The great thing about them is that although you have to think about them when you write the method, they do all the work automatically when you call the method, and let you write very sophisticated type signatures. Normally users don't pay any attention to them, but they are critical in allowing the libraries to support all the flexible ways Smalltalk methods uses objects.
getMasterSize: d <Dictionary[Str,Str]> ^<Int> ^(d at: 'master' ifAbsent: [^0]) size
Our #at:ifAbsent: method from before returns either the dictionary value type (<Str>), or the return type of the fail block. But the fail block doesn't ever produce a value, because it aborts the expression and make the method directly return 0 (which matches the method return type, so that's ok). So the failblock's return type is <BottomType>, so the #at:ifAbsent: message returns <Str|BottomType>. The type system ignores the BottomType, so we get <Str>, which supports the size message, which returns an <Int>, which matches the method return type, so the method is typesafe.
This shows all the type system features we've seen working together on the kind of code Smalltalkers write all the time. Note that when you are writing the method above, the typing looks really simple; all the hard part is done in the libraries when defining the signatures for these very powerful methods like #at:ifAbsent:.