What is the type traversal limit and why does EK9 enforce it?
← Code Quality · Ref: Q731
EK9 limits method chains to 3 type transitions. Each time a dot-chain crosses from one type to another, it counts as a transition. Exceeding 3 triggers E11054.
IMPORTANT DISTINCTION
Same-type method chains do NOT count as transitions:
name.trim().upperCase().lowerCase() // 0 transitions (all String)
Only CROSS-TYPE chains count:
dept.getManager().getAddress().getCity() // 3 transitions
WHY 3 TRANSITIONS MAXIMUM
This enforces the Law of Demeter (principle of least knowledge):
1. Structural coupling: long chains couple your code to the internal structure of distant objects
2. Fragile chains: changing any intermediate type breaks all callers
3. Testability: mocking a 4-deep chain requires 4 mock objects
4. Encapsulation violation: reaching through objects exposes internal structure
HOW TO FIX
Add a convenience method that encapsulates the traversal:
// Before: dept.getManager().getAddress().getRegion().getCity() // After: dept.getManagerCity() // The Department class adds: getManagerCity() = getManager().getAddress().getRegion().getCity()
THIS EXAMPLE
The method chain below crosses exactly 3 type boundaries (Department to Manager to Address to String). The Address class also has a getRegion() method returning a Region object. Adding that extra traversal step to reach the city via region would push from 3 to 4 transitions, triggering E11054.
See Q310 for code quality overview. See Q696 for complexity limits. See Q322 for quality enforcement.
Example
defines module qa.codequality.typetraversalboundary defines class <?- Geographic region containing a city name. -?> Region regionCity <- String() Region() -> regionCity as String this.regionCity: regionCity getCity() as pure <- rtn as String: regionCity default operator ? <?- Address that belongs to a geographic region. Has both getCity() for direct access and getRegion() for region traversal. -?> Address cityLabel <- String() addressRegion <- Region() Address() -> cityLabel as String addressRegion as Region this.cityLabel: cityLabel this.addressRegion: addressRegion getCity() as pure <- rtn as String: cityLabel getRegion() as pure <- rtn as Region: addressRegion default operator ? <?- Manager type that holds an address. -?> Manager managerName <- String() homeAddress <- Address() Manager() -> managerName as String homeAddress as Address this.managerName: managerName this.homeAddress: homeAddress getName() as pure <- rtn as String: managerName getAddress() as pure <- rtn as Address: homeAddress default operator ? <?- Department type that holds a manager. Method chains from Department cross 3 type boundaries maximum. -?> Department deptName <- String() deptManager <- Manager() Department() -> deptName as String deptManager as Manager this.deptName: deptName this.deptManager: deptManager getName() as pure <- rtn as String: deptName getManager() as pure <- rtn as Manager: deptManager default operator ? defines program TypeTraversalBoundaryDemo() stdout <- Stdout() addressRegion <- Region("Springfield") homeAddress <- Address(cityLabel: "Springfield", addressRegion: addressRegion) deptManager <- Manager(managerName: "Alice", homeAddress: homeAddress) department <- Department(deptName: "Engineering", deptManager: deptManager) //3 type transitions: Department -> Manager -> Address -> String //This is exactly at the boundary managerCity <- department.getManager().getAddress().getCity() stdout.println(managerCity) //Same-type chains: 0 transitions (all String) processed <- managerCity.trim().upperCase() stdout.println(processed) //2 transitions: Department -> Manager -> String (within limit) managerLabel <- department.getManager().getName() stdout.println(managerLabel)
Common mistakes
E11054 — Adding an extra type transition (Address to Region to String instead of Address to String) pushes the chain from 3 to 4 type transitions, exceeding the limit. Encapsulate the traversal in a convenience method. See ek9 -h E11054 for details.
Incorrect:
managerCity <- department.getManager().getAddress().getRegion().getCity()
Correct:
managerCity <- department.getManager().getAddress().getCity()
Other ways to ask this
- What triggers E11054 excessive type traversal?
- How many type transitions are allowed in a method chain?
- What is the Law of Demeter in EK9?
- Why does EK9 limit dot-chain depth?
Coming from another language?
Java: no type traversal limit (Demeter checked by optional tools). Python: no limit. C++: no limit. Rust: method chains common, no cross-type limit. Go: no limit (short method chains by convention). EK9: 3-transition hard limit enforced at compile time.
Keywords: type, limit, transition, traversal, Demeter, E11054, coupling, dot, clean-code, quality, encapsulation, boundary, chain