<< Previous | Next >>

If t' ≤ t then Set(t') ≤ Set(t)

(or reply to: A bug in the UML 2.0 OCL spec?)

Notice how this is the same in java, and is one of the most fundamental problems with it:

Get an array of strings, downcast it to an array of objects, then put in a widget (downcasted to an object) ... you'll get (on runtime!) an ArrayStoreException; the object can not be put in your array of objects.

ArrayStoreException

Object x[] = new String[3];
x[0] = new Integer(0); // throws the runtime exception: ArrayStoreException

This makes java check the real type of objects whenever you put them in arrays. Which makes arrays a lot slower then they could be (runtime type-checking).

Then compare how OCaml handles upcasting / downcasting (coercions) ... it basically does not allow you to easily upcast objects. (section 3.12 Using coercions) So you cannot interpret a downcasted array as the previous type anymore, nor can you interpret any element in that array as being of the previous type.

(If you really want to do this in OCaml, you need to wrap your objects in a section 1.7 symbolic type. Or include this symbolic type in your ancestor object, so you can still benefit from the polymorphism directly (e.g. have an array of widgets, and call their draw method). Then unpack the original object from the symbolic type.

Notice that this is ment by the remark: "Be aware that subtyping and inheritance are not related." in 3.12 Using coercions, and notice that this idiom makes it hard to extend the object model with more elements without adding them to the symbolic type, but this symbolic type could be a feature of the OCaml language)

on:

If t' ≤ t then Set(t') ≤ Set(t)

this is true, as you can put a t in a Set(t) and in a Set(t') ... but you are working from the other end:

if t' ≤ t and t' ≤ b then Set(t) <- b (incorrect!)

or

if t' ≤ t then b <- Set(t'); b is_type t (incorrect!)

... but that is not true of-course, though:

if t' ≤ t and t' ≤ b then Set(t') <- b

or: you can take a bag of apples, interpret them as a bag of fruit, add a banana to the bag of fruit ... this is fine, just don't interpret the bag of fruit as a bag of apples anymore. That was why you downcasted it in the first place.

Sure in languages like C++ and Java this doesn't work well: somewhere something might still reference the list of fruit as a list of apples. But in OCaml, the downcast and adding would not invalidate any place still referencing the list of apples, because the list didn't mutate, instead, a new list was created that contains the banana (as a list of fruit) which is perfectly what you want.

This is functional programming where there are no side effects vs imperative programming where there are side effects and the logic doesn't map to that model. (e.g. something might be referencing the array of apples which now also include an banana, so is no longer an array of apples, but still an array of fruit)

Java choose to solve this by checking on runtime if you are putting a banana in a bag of fruits which is really a bag of apples ... and you don't even want to thing about what happens in C++

Why keep this mathematically correct statement in the language at all? Java doesn't really, but it pretends to have it, because you can have the mathematical equivalent of adding two arrays. Just as long as the receiving array is declared as an array of the lowest type.

(small disclaimer: I might have made some mistakes here, this is not a easy subject for a blog you write just like that, corrections are welcome of-course)

Last modified: 2007-11-19 20:15 GMT