Lo, on Tuesday, January 1, dman did write: > On Mon, Dec 31, 2001 at 09:27:36PM -0500, William T Wilson wrote: > > | Casting you can't really get away from nor do you really need to. In fact > | the more strongly typed the language is, the more casting you have to do. > > This statement is incorrect.
Agreed. > The strength and staticness of typing are two independent properties. Also agreed. However, I think that the flexibility of a type system is more important than its `strength' for removing the need for casts. When do people use typecasts in C/C++? In my experience, they tend to happen in the following situations: 1) conversions between data types (char and int, int and float, etc.) 2) downcasts in a class hierarchy, as in Java. (Note that `upcasts' aren't really casts: if B is a subclass of A, then an instance of B *is* an instance of A, no cast needed.) 3) Accessing external data in a callback function. Callbacks in C and C++ often take a void* parameter that points to data supplied by the programmer when he registers the callback; to access this data, it is necessary to cast it back to the original type. How do more flexible type systems handle these? 1) There are conversion functions. Scheme does this with, e.g., char->integer and integer->char, or exact->inexact and inexact->exact. (The latter two convert between integers and floating point numbers.) In many cases, this is what C's casts and C++'s static_cast really do; they just use a typecast notation. Many programmers tend to follow this trend when writing their own conversions, although I usually don't---implicit conversions worry me. 2) As several people have pointed out, too many downcasts indicate a design flaw. They are, however, useful in some situations, so (I believe) many advanced languages with a static type system support these. However, as in Java, there's a run-time check that goes with it. C++'s dynamic_cast gets this right. 3) There are a variety of strategies here. My personal favorite is to use first-class closures to implement callbacks; the external data is no longer supplied as a parameter but is simply available to the callback function due to normal scoping rules. If you're using a language without closures, like C++ or Java, you can hack it up: class Closure { public: virtual void invoke(void) = 0; }; Subclass this, implement invoke(), and supply a reference to the subclass instance as your callback function. The data normally recognized by the void* is now private data members in the callback instance; no cast necessary. Alternatively, supply the callback system with enough smarts to handle types. Instead of void register_callback(void (* cb)(void *), void *data); write something like template<typename T> void register_callback(void (* cb)(T), T data); although most advanced languages typically use a more concise mechanism for this sort of polymorphism. With the exception of downcasts, casting is generally only necessary if your type system isn't very expressive. > I've heard that haskell has an even better type system, but I haven't > had time to learn it yet. Most of my experience with advanced programming languages has been in Scheme, which (despite what many people believe) *is* strongly-typed and type-safe, but it checks all types at runtime rather than compile time. As a result, I'm not really up on languages with compile-time type verifications systems, though I've been meaning to investigate them for a while now. At any rate: I've heard that ML's type system is easier to learn than Haskell's; you may want to start there first. Richard