E08271: a 'register' statement constructs what it registers - call a constructor of the component or service (and of each aspect), not a function, method or delegate

Category: E08xxx Pre-IR checks (flow analysis) · Reported in phase: FULL_RESOLUTION · Classification: REGISTRATION_REQUIRES_CONSTRUCTOR · All error codes

What went wrong

A 'register' statement must CONSTRUCT what it registers. The application builds each registered value itself, in the order the 'register' statements are written, before any program runs - so the registered expression is a constructor call of the component (with 'as') or the service (without 'as'). The same holds for each aspect in 'with aspect of'.

These are rejected: the result of a function ('register makeOther() as Other'), a call through a function delegate, a service made by a function, a dynamic function, and an aspect made by a function ('with aspect of makeTracing()').

A factory can reach an injection that is not registered yet, and whatever it returned would be stored as the component - the application could not check its wiring.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 326 (What is the EK9 equivalent of Spring @Bean and @Configuration?)
  • ek9 -q 228 (Why does registration order matter in EK9 applications?)

The [EXAMPLE] section below shows a component registered from a factory function, then the factory's work moved into the component's constructor (which injects what it needs) and the component registered by a constructor call, after what it injects.

How to fix it

  1. Register a constructor call: 'register Impl() as AbstractType', or 'register SomeService()'.
  2. Move the factory's work into the component's constructor; anything it needs is injected there, and must be registered before it (E08200).
  3. For an aspect, construct it: 'with aspect of Tracing()', where Tracing extends Aspect.

Common causes

  • Spring @Bean / Guice @Provides habit: a factory method that builds the component
  • Wanting to choose an implementation at runtime - define a separate application per configuration
  • Passing a delegate variable that holds a factory function

See also

  • E08271: a registration that is not a constructor call (this error)
  • E50020: the constructed type is not a component (with 'as') or not a service (without 'as')
  • E50030: the constructed component is not the 'as' type, or an aspect is not an Aspect
  • E08200: a registration injects something registered after it
  • E08210: an injection reachable from a program is not registered

Examples

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

Fails with E08271: a 'register' statement constructs what it registers - call a constructor of the component or service (and of each aspect), not a function, method or delegate

#!ek9
defines module error.e08271.bad

  defines function

    makeOther()
      <- rtn as Other: Labelled()

  defines component

    Svc as abstract
      name() as abstract
        <- rtn as String?

    SvcImpl is Svc
      override name()
        <- rtn as String: "service"

    Other as abstract
      tag() as abstract
        <- rtn as String?

    Labelled is Other
      label as String?

      Labelled()
        svc as Svc!
        label: svc.name()

      override tag()
        <- rtn as String: label

      default operator ?

  defines application

    App
      register SvcImpl() as Svc
      register makeOther() as Other

Fixed: Register a constructor call of the component

#!ek9
defines module error.e08271.good

  defines component

    Svc as abstract
      name() as abstract
        <- rtn as String?

    SvcImpl is Svc
      override name()
        <- rtn as String: "service"

    Other as abstract
      tag() as abstract
        <- rtn as String?

    Labelled is Other
      label as String?

      Labelled()
        svc as Svc!
        label: svc.name()

      override tag()
        <- rtn as String: label

      default operator ?

  defines application

    App
      register SvcImpl() as Svc
      register Labelled() as Other