E11070: nested call in throw - extract intermediate variable for the exception
What went wrong
A throw statement was given a nested call expression of the form `throw factoryFn(arg)(otherArg)`. EK9 only allows the throw statement to take either a constructor call or a previously-declared variable. Higher-order factory chains that produce an Exception must be materialised into a named variable first, then thrown.
Worked examples
Compilable examples related to this error:
- ek9 -q 1303 (Why does EK9 reject a call nested inside a throw expression?)
Rationale
`throw` is a control-flow boundary. The exception object that flows across that boundary is the most important value in the surrounding code path — it determines which catch will run, what message the operator will see, and what gets logged. Hiding that object inside a nested factory chain conflates two distinct concerns:
- SELECTING / CONSTRUCTING the exception
- THROWING the exception
When these run together on a single line, as in 'throw getExceptionFactory("std")("oops")', a reader cannot tell at a glance:
- what type of exception is actually thrown
- which factory was selected and why
- what arguments were passed to which call
- whether the inner call has any side effects
Forcing extraction makes every one of those concerns visible - the factory selection, the exception construction and the throw site each on a line of their own:
factory <- getExceptionFactory("std") ex <- factory("oops") throw ex
Each line is independently understandable. The exception type is named in its declaration. Debugging can break on the construction or on the throw separately. Logging or enrichment can be inserted between construction and throw without restructuring the code.
This rule pairs with E11069 (excessive call chain): both express the same principle that values flowing through more than one call layer should pass through a named intermediate so the types and intent stay visible.
How to fix it
Extract the exception construction into its own variable, then throw that variable (as above). A direct constructor call in throw position is still allowed and idiomatic, because it expresses construction and throw together without hiding a factory selection step: 'throw Exception("oops")'. The [EXAMPLE] section below shows the nested call in a throw, then both forms that are allowed.
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E11070: nested call in throw - extract intermediate variable for the exception
#!ek9 defines module error.e11070.bad defines function ExceptionFactory() as pure abstract -> reason as String <- builtException as Exception? StandardExceptionFactory() is ExceptionFactory as pure -> reason as String <- builtException as Exception: Exception(reason) getExceptionFactory() -> factoryKind as String <- chosenFactory as ExceptionFactory: StandardExceptionFactory if factoryKind == "alt" chosenFactory: StandardExceptionFactory //Throw of a nested call: the factory selection step is hidden inside the throw badThrowNestedCall() stdout <- Stdout() try throw getExceptionFactory("std")("oops") catch -> caught as Exception stdout.println(caught.reason())
Fixed: Build the message first, then throw
#!ek9 defines module error.e11070.good defines function ExceptionFactory() as pure abstract -> reason as String <- builtException as Exception? StandardExceptionFactory() is ExceptionFactory as pure -> reason as String <- builtException as Exception: Exception(reason) getExceptionFactory() -> factoryKind as String <- chosenFactory as ExceptionFactory: StandardExceptionFactory if factoryKind == "alt" chosenFactory: StandardExceptionFactory //Form 1: direct constructor call - construction and throw on one line, no factory hidden throwDirectConstructor() stdout <- Stdout() try throw Exception("direct construction") catch -> caught as Exception stdout.println(caught.reason()) //Form 2: extract intermediate variable, then throw - factory selection visible throwExtractedVariable() stdout <- Stdout() try factory <- getExceptionFactory("std") ex <- factory("oops") throw ex catch -> caught as Exception stdout.println(caught.reason())
Questions and answers
These Q&A entries show E11070 and its fix: