LogoThe Eiffel Compiler / Interpreter (tecomp)

doc/papers/lang/catcall frozen variant

Discussion of Bertrand Meyer's last paper to the catcall problem

In 2008 Bertrand Meyer and Emmanuel Stapf wrote a paper with the title "The world is covariant: Is it safe?". In that paper they described a proposal to sove the catcall problem.

This proposal will be analyzed in the following.

1. The catcall problem

Eiffel allows polymorphic attach (i.e. ) and covariant redefinition of arguments.

Polymorphic attach:

   attaching an object of type DT to an entity of type T, if
   T is a conformant ancestor of DT

Covariant redefinition:

   class T feature f(u:U) .. end
   class DT inherit T redefine f end feature f(u: DU) ... end  -- (1)

Polymorphic attach and covariant redefinition alone are not a problem, but in combination they can provoke catcalls.

   t: T
   u: U
   create {DT} t.make(...)  -- (2)
   create u
   -- (3)
   t.f(u)  -- t has an object of type DT attached and expects an
           -- argument of type DU, but gets an argument
           -- of type U

To speak precisely, a catcall happens if 3 things come together:

    1. covariant redefinition with conformant inheritance
    2. polymorphic attach
    3. polymorphic feature call

Unfortunately as opposed to the primitive example above, the 3 things can happen in very remote parts of the software. We can have 3 different authors for the 3 different constructs, where each of them has written perfect Eiffel and in combination they delivered an invalid program.

Any solution which solves the catcall problem must avoid that these 3 things come together.

2. The proposoal

The proposed solution attacks combinations point 2 and 3, the polymorphic attach and the polymorphic feature call.

A type modifier "frozen" is introduced into the language in order to declare

  x: frozen T

The proposed rules:

  1. An attachment x:=y with x:frozen T is invalid, if the
  type of y is a conformant proper descendant of T (i.e. you
  cannot attach polymorphically to frozen T).
  2. A call x.f(a) with x: T not frozen, must be valid when
  x is given any descendant of type T.

Note: The second rule has been given explicitely. I have derived the first one from the examples given.

Viewed from top level the proposal is rigid in the sense that it catches all possible catcalls. It does not allow that the above causes 1+2+3 come together, therefore no catcall can escape.

The problems: Due to its rigidity the rules catch a lot more than catcalls i.e. they are pessimistic (especially rule 2). They are global.

The following sections give an analysis of the consequences.

3. The frozen type rule

The intent of the rule is clear. If you declare an entity of type frozen T, you want to be sure that no polymorphic attach happens and that you therefore can call all features of T without any risk of a catcall.

Although the rule seems to be local at first glance, it is not. In primitive scenarios it seems to be local

  x: frozen T
  y: DT   -- a conformant proper descendant of T
  x := y  -- invalid

However consider the following case:

  f ( y: T )
      x: frozen T
      x := y  -- valid or invalid?

Is the statement x:=y valid or invalid in that context?

3.1 Locally valid

If it is locally valid, then you need global analysis to check the validity again, because the argument y can be attached to an object of type DT. And that requires a calculation of the dynamic type set of y.

If global analysis signals a problem, what to do?

I mean: What to do, if the SW package is big and the author of the polymorphic attach (e.g. the caller of f) does not understand the code of f and the author of f is not available because it is in a library package which the caller of f does not want to modify?

It is always the same problems with invalidity uncovered by global analysis. In a complicated context, you don't know how to correct it.

3.2 Locally invalid

That is the rigid interpretation of rule one. An object of type T has to be attached to entities of type frozen T from its creation.

  x: frozen T
  create x.make(...)

With that interpretation of rule 1 you don't need any global picture to spot an invalidity. Entities of type frozen T cannot "communicate" with entities of type T (without the frozen mark). They are a closed "subworld" within an Eiffel system.

I do not yet know, whether that restriction would push a lot of meaningful code into invalidity. But if yes, a similar construct as object test like

  if {x: frozen T} exp then

might be necessary to remedy the situation.

The proposal seem to say: "If you want to be sure to be able to call any feature of a type T, then declare the corresponding entity of type frozen T.

The crux: I always want to be able to call all features of type T on an entity declared to be of type T. If not, what is the type good for?

Therefore I assume that only in very limited cases an author will declare an entity of type frozen T, because you cannot do a lot of OO with it.

4. The polymorphic feature call rule

This rule is terribly pessimistic. It is even more pessimistic than the dynamic type set calculation. It states that x.f(a) with x:T not frozen must be valid for any descendant of the type T.

Any descendant! Regardless whether the descendant is in the dynamic typeset of x or not.

The problems:

5. Summary

The proposal works in the sense that it catches all possible catcalls.

The critique: It won't scale for big systems.

The proposed solution is not complete. It gives rules for a compiler on how to flag potential catcalls. But it does not give an answer to the question "And what to do with it?".


Feel free to comment on that topic at

 Local Variables: 
 mode: outline
 coding: iso-latin-1
 fill-column: 60
 outline-regexp: "=\\(=\\)*"
Table of contents

- 1. The catcall problem

- 2. The proposoal

- 3. The frozen type rule

- 3.1 Locally valid

- 3.2 Locally invalid

- 4. The polymorphic feature call rule

- 5. Summary

- Discussion