ADR 0021: Inferred results for private expression bodies; pass-through `return`
Status: Accepted (refines 0008)
Context
Section titled “Context”Two small frictions came up repeatedly in backend code.
- Private helpers such as
fun isSuccess(code: Int): Bool = code in 200..299repeat a type that the body already states. Kotlin infers the result of expression bodies, and the spec reserved this as a compatible extension. - In a
throwsfunction, returning another fallible call neededreturn f()?. Besides reading worse than Go’sreturn f(), it returns zero values on failure, which breaks contracts such asio.Writer: a wrapper’sWritemust report the bytes written before an error.
Decision
Section titled “Decision”- A private function or method with an expression body may omit its result type. It is inferred from the body on demand, when a call or another body first needs it, so declaration order does not matter. A result that depends on itself must be declared (E0208).
pubfunctions always declare their result type (E0208), because Go callers, documentation, and API review read it. A fallible body must declarethrowsand its result, since fallibility is never inferred (ADR 0012). A body producing several values declares them too.- In a
throwsfunction,return f()wheref()is fallible and its success values have the function’s result types, or are interface values, lowers to Go’sreturn f(): every result, the error included, passes through unchanged. A concrete value that would become an interface does not pass through, because a failing call’s nil pointer would become a non-nil interface (the typed-nil trap); such code writesreturn f()?.
Alternatives
Section titled “Alternatives”- Inference for every function: changes a package’s API whenever a body changes, invisibly to Go callers. Kotlin recommends explicit types on public API for the same reason.
- Inference for block bodies: several
returnstatements must agree, and the type is harder to see when reading the function. - Always requiring
?on returned calls: one rule fewer, but it loses partial results and is not how Go code reads. - Pass-through with any assignable type: reintroduces the typed-nil trap that Lyth avoids elsewhere.
Consequences
Section titled “Consequences”Inference needs on-demand checking of function bodies with cycle detection,
and a private helper’s type can change with its body, which only affects its
own package. return f() and return f()? now differ on failure: the first
returns the callee’s values with the error, the second zero values.