E11054: method chain traverses too many types - extract a function or use a pipeline

Category: E11xxx Code quality and IR analysis · Reported in phase: PRE_IR_CHECKS · Classification: EXCESSIVE_TYPE_TRAVERSAL · All error codes

What went wrong

This method chain crosses more than 3 type boundaries. Each dot that returns a different type from its receiver creates a structural dependency on an intermediate type. Chains like 'order.getCustomer().getAddress().getCity()' couple this code to Order, Customer, Address, and String simultaneously.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 731 (What is the type traversal limit and why does EK9 enforce...)

Rationale

  • Law of Demeter (Lieberherr 1987): "Only talk to your immediate friends"
  • Guo et al. (University of Zurich, WCRE 2011): across five Eclipse sub-projects, Law of Demeter violations correlated with bug-proneness
  • Each type transition adds a structural dependency that must be maintained when any intermediate type changes
  • Limit: 3 type transitions (same-type chains like String.trim().upperCase() do not count as transitions)

Detection details

  • Counts type transitions, not raw dots in the chain
  • Same-type chains (String.trim().upperCase()) count as 0 transitions
  • Constrained types and their base types are treated as the same type (e.g., EmailAddress constrained from String calling trim() is not a transition)
  • The start type is the receiver; each subsequent call that returns a different type increments the counter
  • Threshold: more than 3 type transitions triggers the error

How to fix it

  1. Identify which intermediate types the chain traverses
  2. Extract a function that encapsulates the multi-step traversal
  3. Or add a direct accessor to the immediate type that hides the depth
  4. Consider using stream pipelines where each stage handles one type

Common causes

  • Deep object graph traversal (order -> customer -> address -> city)
  • Accessing nested configuration (config.getDatabase().getPool().getMaxSize())
  • Navigating tree structures through multiple node types
  • Reporting code that pulls data from disparate domain objects

See also

  • E11054: excessive type traversal (this error)
  • E11016: excessive module coupling (module-level structural quality)
  • E11014: low cohesion (related structural quality metric)

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E11054: method chain traverses too many types - extract a function or use a pipeline

#!ek9
defines module error.e11054.bad

  defines class

    Location
      name as String: "Leeds"
      getName()
        <- rtn as String: name
      default operator ?

    Address
      location as Location: Location()
      getLocation()
        <- rtn as Location: location
      default operator ?

    Customer
      address as Address: Address()
      getAddress()
        <- rtn as Address: address
      default operator ?

    Order
      customer as Customer: Customer()
      getCustomer()
        <- rtn as Customer: customer
      default operator ?

  defines function

    getDeepValue()
      -> order as Order
      <- city as String: String()
      city: order.getCustomer().getAddress().getLocation().getName()

Fixed: Ask each type for what it knows - give Customer a method for the city

#!ek9
defines module error.e11054.good

  defines class

    Location
      name as String: "Leeds"
      getName()
        <- rtn as String: name
      default operator ?

    Address
      location as Location: Location()
      city()
        <- rtn as String: location.getName()
      default operator ?

    Customer
      address as Address: Address()
      city()
        <- rtn as String: address.city()
      default operator ?

    Order
      customer as Customer: Customer()
      getCustomer()
        <- rtn as Customer: customer
      default operator ?

  defines function

    getCustomerCity()
      -> order as Order
      <- city as String: order.getCustomer().city()

Questions and answers

These Q&A entries show E11054 and its fix: