First, some data types to work with:
data Red; Green; Blue nicknamed Color data Colorful(color : Color) data Box(size : Int) data Fragile
Hopefully this should be getting familiar by now. We start with three stateless traits, the union of which is nicknamed
Color. We then introduce two stateful traits, followed by a third trait to mark fragility. Now let's create some objects:
a = Colorful Box where color = Red size = 27 b = Fragile b b2 : Box = b c = Fragile b c2 = Fragile b2 d = Colorful c where color = Blue
The first assignment just creates a composite object, as I described in the last post. Easy.
The second assignment (the one to
b) creates a new object. It has to, because (as I'll elaborate in the upcoming function resolution post) the object's runtime type is maintained even if the object is "upcasted" later, as happens with
b2. We need some way of knowing that
ais not, and the easiest (onlyest?) way to do that is by creating a new object.
Fragilehas no state, which means all
Fragileobjects are identical. In fact, internally,
Fragilecould be just a metadata marker on the object. That means that when we re-add the
c = Fragile b, we can actually reuse the same
bobject. This optimization can be done at compile time.
c2assignment is also a no-op, but only at runtime. Even though the compile-time type of
Fragile, at runtime we can see that the
b2object is already
Fragile, and so we simply return it.
Contrast that to the
dassignment, which also re-sets the state of one of the component traits. In this case, we do need to create a new object in the general case: objects are immutable, so we can't change the state of
c, but we definitely need to store the fact that we now have a blue box where we used to have a red one. (If the new color happens to be the same as the old one, we reuse the object in principle; I'm not sure if this check would be cheaper than unconditionally creating a new object.)
Implicit in all of the above is that there is not a way to check for referential equality — that is, that the programmer won't ever care (or know) if the runtime reuses an object. I think this is a good idea even without these optimizations, so I'm going to throw that into the Effes "spec." Truth be told, I've been assuming it all along.
Incidentally, since the compile-time type of
Fragile Colorful Box, we could have just written
d = Colorful c(without the
whereclause). This would say that we want to change none of the fields, in which case the whole thing is a no-op at compile-time (as
Colorfulhad more than one field (maybe it has an
alpha : Float), we could have used this syntax to change only one of the fields. This would obviously still create a new object.
On the other hand, if we'd written
d2 = Colorful c2, then we'd have to provide the field values.
c2has a compile-time type of
Fragile Box, and the compiler would require that if we add color to this box, we need to specify which color. The fact that
c2already has a color at runtime is irrelevant; the compiler will require that the program specific values for all
Colorfields (in this case, just the one), and the runtime will create a new object that overwrites these fields.