Where is type inference not supported in generic type and function bodies?

← Generics · Ref: Q646

Inside a generic type or function body, EK9 does not support type inference for local variables at all (E06070). Every local declared with '<-' needs an explicit type, even when the value does not depend on T (for example 'counter as Integer: 0', not 'counter <- 0').

INFERENCE LIMITATION

Inside a generic definition the compiler rejects every '<-' declaration, whether or not the type involves T. Declare locals with an explicit type: 'counter as Integer: 0', 'copy as T: T(item)'.

EXPLICIT RETURN TYPES

Generic functions must declare return types explicitly:

  defines function
    transform() of type (S, T) as open
      -> source as S
      <- target as T: source? <- T() else T()

FIELD DECLARATIONS

Fields in generic classes must use the type parameter explicitly:

  Container of type T
    item as T: T()

WHEN INFERENCE WORKS

Type inference works at the CALL SITE, not inside the generic body:

  intContainer <- Container(42)  // inference works here

But inside Container's methods, T must be used explicitly.

See Q642 for constructor inference at call site. See Q648 for function constraints. See Q194 for generic class basics.

Example

defines module qa.genericsdeep.inferencelimits

  defines function

    <?-
      Correct: explicit types used throughout the generic body.
      Return type explicitly declared as T.
    -?>
    identity() of type T as open
      -> item as T
      <- rtn as T: T(item)

    <?-
      Correct: multi-param generic with explicit return type.
      Both S and T used with explicit type annotations.
    -?>
    converter() of type (S, T) as open
      -> source as S
      <- target as T?
      require source?
      target: T()

  defines class

    <?-
      Correct: all fields and method return types use T explicitly.
      No reliance on type inference inside the generic body.
    -?>
    Stack of type T
      items as List of T?

      Stack()
        items :=? List() of T

      Stack()
        -> initial as T
        items: List() of T
        items += initial

      push()
        -> item as T
        if not items?
          items: List() of T
        items += item

      count() as pure
        <- rtn as Integer: 0
        if items?
          rtn: length items

      default operator ?

  defines program

    InferenceLimitsDemo()
      stdout <- Stdout()

      // === INFERENCE AT CALL SITE (works) ===

      result <- identity(42)
      stdout.println(`Identity: ${result}`)

      // === EXPLICIT TYPE AT CALL SITE ===

      stack <- Stack("first")
      stack.push("second")
      stack.push("third")
      stdout.println(`Stack count: ${stack.count()}`)

Common mistakes

E50060 — Integer has no toString() method in EK9. Use the $ prefix operator or string interpolation. See ek9 -h E50060 for details.

Incorrect:

stdout.println(stack.count().toString())

Correct:

stdout.println(`Stack count: ${stack.count()}`)
Other ways to ask this
  • What is E06070 type inference not supported in generic context?
  • When must I use explicit types inside generic definitions?
  • Why cannot I use type inference inside a generic class body?

Coming from another language?

Java: type inference works inside generics (var keyword, diamond). C++: auto deduction inside templates. Rust: type inference inside generic functions with compiler assistance. Go: type inference limited inside generics too. Kotlin: type inference works inside generic bodies. EK9: no '<-' inference inside generic bodies (E06070), every local needs an explicit type.

Keywords: explicit, inference, function, parameter, limitation, body, generic, type, context, E06070, type-parameter