# Pattern: Two-Trait Hierarchy
### Scope
- **Purpose**: Document why `interval_adapter` splits the interval interface into two traits — a base trait for all intervals and an extended trait for bounded ones only.
- **Responsibility**: Problem statement, solution structure, applicability criteria, and consequences of the split.
- **In Scope**: Trait hierarchy design, the type-safety argument, and the tradeoff accepted.
- **Out of Scope**: Canonical type design (→ `pattern/002`); trait signatures (→ `api/001`); unbounded interval feature (→ `feature/002`).
### Cross-References
| doc | [api/001_interval_traits.md](../api/001_interval_traits.md) | Trait signatures for both layers |
| doc | [feature/002_non_iterable_intervals.md](../feature/002_non_iterable_intervals.md) | Feature built on the non-iterable-interval trait |
| doc | [invariant/001_integer_endpoints_only.md](../invariant/001_integer_endpoints_only.md) | Additional constraint enforced at the trait level |
| doc | [pattern/002_canonical_interval_type.md](002_canonical_interval_type.md) | Complementary pattern governing the concrete type layer |
### Problem
Standard range types include both bounded (half-open and closed) and unbounded (fully-open, open-ended, and upper-bounded) variants. A generic function that accepts any range type must either:
1. Accept only bounded ranges (losing the ability to describe unbounded intervals), or
2. Accept all ranges — including unbounded ones — and risk creating infinite iteration loops.
A single trait covering all range types cannot expose iteration without silently allowing infinite loops over fully-open or open-ended ranges.
### Solution
Split the interface into two traits in a strict hierarchy: a base trait covers all interval types including unbounded ones and exposes only bound-query operations; an extended trait adds an iteration requirement and is implemented only by types that also support the iteration protocol — bounded ranges only.
- The base trait is implemented for all range types, including unbounded ones.
- The extended trait is implemented only for types that also support the iteration protocol — bounded ranges only.
- Fully-open, open-ended, and upper-bounded range types never implement the extended trait.
The compiler enforces the split: a function parameterized on the extended trait cannot be called with a fully-open range argument. The error appears at the call site, not inside the function body.
### Applicability
Apply this pattern when designing an abstraction over a heterogeneous collection of types where:
- Some members can participate in an operation (iteration) and some cannot.
- Using a single trait with a blanket implementation would silently allow unsafe or infinite behavior.
- Compile-time type safety is preferred over runtime guards.
### Consequences
**Benefits:**
- Compiler prevents passing unbounded intervals to functions that iterate — no infinite loops at runtime.
- Functions that only query bounds can accept both bounded and unbounded intervals uniformly.
- The hierarchy is extensible: a third trait (e.g., a bidirectional interval trait) could extend the extended trait.
**Tradeoff:**
- Callers must choose between the base and extended trait when writing generic functions.
- The split adds conceptual complexity for users unfamiliar with the rationale.