Chair of Software Engineering Object-Oriented Software Construction Bertrand Meyer Lesson 21 Last update: 25 May 2004 Type issues, covariance and catcalls
Chair of Software Engineering Programming in the Large, What’s special about this lecture Mix of: Fundamental concepts Unsolved issues Recent proposal
Chair of Software Engineering Programming in the Large, Flexibility vs. safety Expressive power (no “bondage and discipline” language) Protection against crashes and attacks
Chair of Software Engineering Programming in the Large, Typing approaches Pascal x := y Only if types of x and y are identical! Smalltalk x.f always permitted, not static type check! At execution time: “ Message not understood ”
Chair of Software Engineering Programming in the Large, Like Pascal in basic forms But polymorphism allows more flexible assignment rule: x := y permitted if type of y “conforms to” type of x Typed O-O languages
Chair of Software Engineering Programming in the Large, Basic type rules in typed O-O languages Eiffel, Java,.NET... In every assignment x := y (or argument passing), type of y conforms to type of x. In every feature call x.f(…) (qualified), f is an exported feature of the class of x. C++: “A little bit typed” (Casts)
Chair of Software Engineering Programming in the Large, Terminology Typed vs untyped languages More accurate: Statically typed, vs Dynamically typed
Chair of Software Engineering Programming in the Large, Typing is always pessimistic Invalid in Pascal: var n: INTEGER n := 0.0 if 0 > 1 then begin n := 0.0 end
Chair of Software Engineering Programming in the Large, The basic goal A programming language is statically typed if its definition includes a set of type rules, guaranteeing that no execution of a valid program will ever produce a run-time type failure. A type rule is a validity constraint involving the types associated with program elements. A validity constraint for a programming language is a boolean condition applicable to any syntactically legal program text in the language.
Chair of Software Engineering Programming in the Large, Precise definitions An object-oriented program is class-level-valid if it satisfies the following properties: In every assignment x := y (or argument passing), type of y conforms to type of x. In every feature call x.f(…) (qualified), f is an exported feature of the class of x. Without catcalls, any class-level-valid program would be type-safe!
Chair of Software Engineering Programming in the Large, The issue: flexibility vs safety Find a type system that: Supports covariance and genericity Permits full static type checking Stated differently: Disprove the “Pierre America conjecture” that one can have at most two of polymorphic substitution, covariance, and static type checking Fix the “holes” in Eiffel’s type system
Chair of Software Engineering Programming in the Large, A typical covariance situation
Chair of Software Engineering Programming in the Large, Original class class BOAT feature captain: SKIPPER -- Skipper assigned to this boat sail (c: SKIPPER) is -- Appoint c as captain of this boat. require c /= Void do captain := c ensure captain = c end
Chair of Software Engineering Programming in the Large, Original class (simplified) class BOAT feature captain: SKIPPER sail (c: SKIPPER) is do captain := c end ARRAYED_ BOAT STACK CAT captain sail(...) sail(...) ARRAYED_ SKIPPER STACK CAT_ SKIPPER captain
Chair of Software Engineering Programming in the Large, Heir class class CAT inherit BOAT redefine captain, sail end feature captain: CAT_SKIPPER sail (c: CAT_SKIPPER) is do captain := c end
Chair of Software Engineering Programming in the Large, Original class, using anchored type class BOAT feature captain: SKIPPER sail (c: like captain) is do captain := c end No explicit redefinition necessary for sail in CAT (see next)
Chair of Software Engineering Programming in the Large, Heir class class CAT inherit BOAT redefine captain end feature captain: CAT_SKIPPER end
Chair of Software Engineering Programming in the Large, Static typing vs. dynamic binding x.f Static typing: ensures that there is AT LEAST ONE FEATURE Dynamic binding: ensures that it is THE RIGHT FEATURE
Chair of Software Engineering Programming in the Large, Catcalls boat1: BOAT; cat1: CAT skipper1: SKIPPER; cat_skipper1: CAT_SKIPPER... boat1 := cat1... boat1.sail (skipper1) The captain of this catamaran is now a plain SKIPPER ! Might try to do captain.repair_second_hull (see next) captain (SKIPPER) (CAT) “Ticking bomb”
Chair of Software Engineering Programming in the Large, Catcalls class BOAT feature do_maintenance is do -- Something here. end feature do_maintenance is do captain.repair_second_hull end class CAT inherit BOAT redefine do_maintenance end
Chair of Software Engineering Programming in the Large, Catcall situation ARRAYED_ BOAT STACK CAT captain sail(...) sail(...) repair_second_hull ARRAYED_ SKIPPER STACK CAT_ SKIPPER captain
Chair of Software Engineering Programming in the Large, Consequences of a catcall Crash? Security attack?
Chair of Software Engineering Programming in the Large, Catcalls: source #2 — Genericity skipper_list: LIST [SKIPPER] cat_skipper_list: LIST [CAT_SKIPPER]... skipper_list := cat_skipper_list skipper_list.extend (skipper1) cat_skipper1 := cat_skipper_list.last
Chair of Software Engineering Programming in the Large, Catcalls: source #3 — Descendant hiding class RECTANGLE inherit POLYGON export {NONE} add_vertex end... end poly1 := rect1... poly1.add_vertex (...)
Chair of Software Engineering Programming in the Large, CAT Changed Availability or Type
Chair of Software Engineering Programming in the Large, Pierre America, 1990 At most two of: Polymorphic substitution Covariance Static type checking
Chair of Software Engineering Programming in the Large, Previous solutions: novariance C++, Java,.NET... Eliminates the problem (obviously) Forces programmers to do the conversions themselves May result in brittle code
Chair of Software Engineering Programming in the Large, Previous solutions: contravariance Results specialized, arguments generalized Solves the problem Only problem: doesn’t match practical needs The world seems to be covariant.
Chair of Software Engineering Programming in the Large, Previous solutions: catch catcalls Generate code to check arguments Cause exception if wrong type Avoids the security issue Disadvantages: Performance penalty in all cases Forces issue on client! No easy way to force client to handle exception Exception too drastic
Chair of Software Engineering Programming in the Large, Previous solutions: Overloading Consider that redefinition creates a new variant of the routine but doesn’t obliterate the previous one. Name resolution combines dynamic binding and overloading. Semantic rules can be devised (Giuseppe Castagna, ENS Paris) Doesn’t seem compatible with goals of O-O programming
Chair of Software Engineering Programming in the Large, Previous solutions: system-level validity Considering all assignments, compute dynamic type set (DTS) of any variable x. If there is an assignment x := y, or a corresponding argument passing, all elements of DTS of y are also in the DTS of x. No attempt at control flow analysis Fixpoint algorithm Helps with optimization Disadvantages: Pessimistic Not incremental Difficulty of giving precise diagnostics
Chair of Software Engineering Programming in the Large, Previous solutions: class-level validity If there is an assignment x := y and y is of a different type from x, or (recursively) y is polymorphic, consider x polymorphic. Any formal routine argument is polymorphic Disallow x. r (...) if x is polymorphic and r is a CAT routine. Local information, all incremental Disadvantages: Pessimistic Difficulty of giving precise diagnostics
Chair of Software Engineering Programming in the Large, Recent Eiffel developments Tuples Expanded inheritance
Chair of Software Engineering Programming in the Large, Tuple types and tuples TUPLE [X, Y, Z] Denotes sequences of at least three elements, first of type X, second of type Y, third of type Z Individual tuple: [x1, y1, z1] Conformance relations TUPLE [X, Y, Z] TUPLE [X, Y]
Chair of Software Engineering Programming in the Large, Expanded inheritance class C inherit A expanded B feature... end No polymorphism permitted: a1 := c1 -- OK b1 := c1 -- Not permitted
Chair of Software Engineering Programming in the Large, The new solution (1) Covariance etc. OK with expanded inheritance Covariance also OK with non-exported features x.f(a) -- Qualified f(a) -- Unqualified
Chair of Software Engineering Programming in the Large, The new solution (2) Allow covariant redefinition even with polymorphism! Replacement (“recast”) must be provided Also applies to generic case
Chair of Software Engineering Programming in the Large, Covariant cats class CAT inherit BOAT redefine captain, sail end feature captain: CAT_SKIPPER sail (c: CAT_SKIPPER) is recast trained_as_cat_skipper do captain := c end
Chair of Software Engineering Programming in the Large, A recast function trained_as_cat_skipper (s: SKIPPER): CAT_SKIPPER is -- Version of skipper s reborn as cat skipper require exists: s /= Void do create Result.train_skipper (s) end
Chair of Software Engineering Programming in the Large, In CAT_SKIPPER class CAT_SKIPPER inherit SKIPPER create make_from_skipper feature -- Initialization train_skipper (s: SKIPPER) is require s /= Void s.trained_as_cat_skipper do … end
Chair of Software Engineering Programming in the Large, The latest… No more recast function Use exceptions instead sail (c: CAT_SKIPPER) is do captain := c rescue … Can use c … end
Chair of Software Engineering Programming in the Large, The multi-argument case r (x: TYPE1 ; y: TYPE2; z: TYPE3) is recast transform do... end with transform (x: OLD1 ; y: OLD2; z: OLD3): TUPLE [TYPE1, TYPE2, TYPE3] is do... end
Chair of Software Engineering Programming in the Large, The generic case In C [G], if there is a routine r (x: G) it must have a recast clause!
Chair of Software Engineering Programming in the Large, Possible criticism Creates a copy What if LIST of catamarans
Chair of Software Engineering Programming in the Large, The descendant hiding case Expanded inheritance works, of course More flexibility: still under discussion
Chair of Software Engineering Programming in the Large, More work Implement Analyze benefits and disadvantages further Study real software Formalize through a mathematical model, prove Solve descendant hiding issue
Chair of Software Engineering Programming in the Large, Some tentative conclusions Flexibility can be reconciled with safety No need to choose between anarchy and “bondage and discipline” Static typing is practical Language design is needed Language design is fun Committees work! Don’t sail a catamaran unless you know how to