Issue #1047: Decision: does a function field type an untyped let lambda by first use (#997, #1045 row b)?

Open and answer this issue on GitHub.

decision · opened 2026-10-31 · updated 2026-10-31 · 1 comment

Question

#997 types an untyped lambda by its first use, but only by its call arguments. Should that typing also take a function field the lambda is put into?

struct H { f: fn(i64) -> i64 }
fn main() {
    let g = fn(x) { 5 }
    let h = H { f: g }
    print(h.f(1))
}

Today, after #1045 (fc3e3377), this program is refused NK1245: no type is known for x. The same lambda handed straight to a call argument, apply(g, 1), runs. A body that uses x (x + 1) also runs, because the body types x.

Options

  1. Yes: a field counts as a first use. H { f: g } gives g the type fn(i64) -> i64, just as a parameter of that type does. This is one more place in #997’s typing by use (check_core_stmt / places_stored). The rule reads: “an untyped lambda takes its type from the first place it is handed to: a call argument or a field of a known function type.”
  2. No: keep NK1245. The fix is to write the parameter type (fn(x: i64) { 5 }). The refusal names the parameter. The rule stays smaller: only calls type a lambda.

Recommendation

Option 1. A struct literal is a place with a declared type, just like a parameter. Treating the two differently is a surprise, and the error appears far from where the type is plainly given. The cost is small: one test, plus one sentence in the rule of Part I for #997.

To build (if 1)

Raised by brief b1045 (report docs/briefs/b1045-let-lambda-fn-field.md.report.md).


Generated by Claude Code

Comments

keywan-ghadami, 2026-10-31

A related case, found while building the resume in scripts/agent.nika (3cfed755). It is the same question asked of a collection literal: does an untyped let take its type from its first use?

use std::process
fn main() throws {
    let x = "v"
    let env = [("A", x.clone())]
    let r = process::run("true", []; env: env)
    print(f"{r.code}")
}

This gives NK1106: The option env of process::run takes Vec[(String, String)], but you're passing Vec[(ref String, String)]. Written inline at the call, the same literal is accepted. If the decision here is “typing by first use”, it should probably cover a literal bound by let as well as a lambda.


Generated by Claude Code

Back to Open Issues