When two entrants derive from a base that closed the self-referential comparison, what mismatch does that limit still allow?
answer
- the loop closes at one level only
- everything below inherits that closure
- unrelated families out, relatives still in
- strict form rejects the subtypes themselves
- loosened to itself-or-a-type-above
basics
~20 sSiblings still compare freely. The limit is satisfied once, at the level where the recursion was closed, and every type below that level inherits a comparison typed against the closing type - so two unrelated subtypes of it pair up with no complaint.
solid answer
~40 sThe self-referential limit rules out unrelated families, not related types. Once a base closes the loop by declaring itself as its own comparison partner, every subtype inherits a comparison whose parameter is that base, so two sibling subtypes can be compared with nothing flagged. The strict form has a second, opposite consequence: a subtype is not itself a legal type argument, because it is comparable with the base rather than with itself, so a ladder over that subtype will not even compile. Teams resolve this by loosening the limit to "comparable with itself or with some type above it", which admits the subtypes - and formally accepts the sibling comparison as the price.
code
pseudocode · 14 linestype Entrant is Ranked<Entrant>: # the loop closes here
function beats(other of type Entrant) returns boolean
type Sprinter derives from Entrant # inherits beats(other of type Entrant)
type Swimmer derives from Entrant # inherits the same comparison
aSprinter.beats(aSwimmer) # accepted: Swimmer is an Entrant
# strict limit: E must be Ranked<E>
seedLadder(list of Entrant) # accepted
seedLadder(list of Sprinter) # rejected: Sprinter is Ranked<Entrant>
# loosened limit: E must be Ranked<E or something above E>
seedLadder(list of Sprinter) # now accepted - and so is a mixed fieldgo deeper
Take away one fact: the limit keeps out types from an unrelated family, not every type you would not want compared. Do not describe it as making mismatches impossible.
Explain by substitution. Show that a subtype inheriting the base's comparison is comparable with the base, and follow that through to both consequences - siblings pair up, and the subtype is not a legal argument under the strict form.
This is the question where a real system shows. Name the loosened limit, say what it concedes, and describe the defence you leave in the comparison body because the signature no longer carries the guarantee.
Decide whether self-comparability is the right contract at all. An explicit comparison passed alongside the values removes the whole shape and handles several orderings, at the cost of a wider surface.
## The two-sided consequence of closing the loop A self-referential limit is checked by substitution: a candidate type is admitted if that type's comparison names that same type. The awkward cases appear as soon as a family has more than one level, because the recursion is closed **at exactly one level** and everything below inherits the closure rather than making its own. Say a base entrant type declares that it is comparable with the base entrant type. Two subtypes derive from it and neither redeclares the comparison. Two separate things now follow, and they point in opposite directions: 1. **Siblings compare.** Each subtype inherits a comparison whose parameter type is the base. Handing one subtype a value of the other subtype satisfies that parameter, so the comparison is accepted with nothing reported. The ladder will cheerfully seed a mixed field. 2. **Neither subtype is a legal argument.** Under the strict limit, a subtype is comparable *with the base*, not *with itself*. Substituting it fails, so a ladder declared over the strict limit refuses the subtype outright - the code will not compile even though the intent is obviously sound. The second consequence is the one that bites in practice, and the standard response to it is what makes the first consequence permanent. ## The loosened limit The usual fix is to weaken the limit from *comparable with itself* to *comparable with itself or with some type above it*. That admits every subtype, because being comparable with the base satisfies "some type above it". It is the form most real libraries publish, and a candidate who has used this shape in anger will name it. | Limit form | Base accepted as argument | Subtype accepted as argument | Sibling comparison | |---|---|---|---| | Comparable with itself (strict) | Yes | No - it is comparable with the base | Not reachable through the ladder | | Comparable with itself or a type above it | Yes | Yes | Accepted, with no report | So the honest summary is a trade, not a guarantee: the strict form is precise but rejects legitimate subtypes; the loosened form is usable but concedes that types in one family can be mixed. ## What the limit actually promises Stating the promise carefully is the whole SENIOR answer: - **It promises** that the argument's comparison is typed against a type the argument itself is - so a member of one family can never be handed to a comparison belonging to an unrelated family. - **It does not promise** that the comparison's parameter is the *most-derived* type, and therefore does not separate siblings. - **It leaves nothing behind at run time.** It is a compile-time narrowing; nothing re-checks the pairing when the comparison executes. A candidate who claims this shape makes cross-type comparison impossible has overstated it by exactly one step, and that overstatement is what an interviewer is usually probing for. ## Living with it Three responses, with their costs: 1. **Close the loop at each leaf.** Each subtype redeclares the comparison against itself. The siblings are now genuinely separated, but the base can no longer be used as a common comparable type, collections of mixed entrants lose their element type, and every new subtype must remember to do it. 2. **Accept it and defend at run time.** Keep the loosened limit and have the comparison itself reject a partner it does not recognise, failing loudly instead of returning a meaningless ordering. This is what most libraries do; the failure is at least immediate and attributable. 3. **Make the ordering explicit instead.** Pass a separate comparison object alongside the entrants rather than requiring the entrants to be self-comparable at all. This side-steps the whole shape and is often the better design when more than one ordering is meaningful. ## The reviewing habit When you meet such a signature in review, ask two questions in this order: *at which level is the recursion closed?* and *is any type below that level used as a type argument?* If the answer to the second is yes, the limit in force is the loosened one and mixed comparison is possible - so the run-time defence in option 2 above is not optional paranoia, it is the part that replaces the guarantee everyone assumed they had.
- Why does the strict form reject a subtype that is obviously comparable?Because the check is substitution, not intuition. The subtype's inherited comparison is typed against the base, so substituting the subtype into "comparable with itself" produces a statement that does not hold. The intent is sound and the limit still says no, which is why the loosened form - comparable with itself or with some type above it - exists at all.
- What do you lose by closing the loop separately in every leaf type?The common comparable base. Once each leaf declares a comparison against itself, there is no single type that describes "any entrant comparable with its own kind", so a mixed collection loses a usable element type and shared code that wanted to accept any entrant has to be parameterized further. It also becomes a rule every future subtype author must know.
- Given the residual hole, what should the comparison body itself do?Fail loudly on a partner it does not recognise rather than producing an ordering from whatever members it can reach. A meaningless comparison corrupts a sort silently and is discovered far from its cause; an immediate, attributable failure at the point of the mismatch is the practical replacement for the guarantee the signature does not actually give.
saying these in an interview costs you the question
- Claims the limit makes any cross-type comparison impossible
- Thinks each subtype automatically re-closes the recursion on itself
- Believes a run-time check re-verifies the pairing when the comparison runs
- Says the strict form admits subtypes of the type that closed the loop
- Treats the loosened form as identical in strength to the strict one