You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make them variadic. This is worth doing on its own merits — note("saw", n, "packages") is the natural
way to write it — and it is a hard prerequisite for #698, because the builtin print these methods are
about to replace is variadic.
The contract to match
starlark-go's print (starlark/library.go:799):
variadic over positional arguments
sep keyword, defaulting to " "
strings written as-is; Bytes written raw; everything else through writeValue, i.e. its repr
returns None
Variadic provider methods already work — file.Join(parts ...string) is announced today — so the
signature change needs no bridge work.
Steps
(args ...any) on all six; Fail(args ...any) error
Join the way the builtin does: strings as-is, everything else through the value's Starlark representation
Decide sep — a matching keyword parameter, or a documented narrowing. The bridge has a kwargs map[string]any convention (flow.Degraded uses it), so it is available if we want it
Update the method table in docs/architecture/3.5.16-ui-provider.md and its status companion
Regenerate; confirm all six still claim deterministic, so a hermetic planning runtime keeps its output
Verification
ui.note("a", 1, True) renders each argument the way Starlark would
make check, make test-race, make test-scenario
Claim check green across "", darwin, linux, windows
Correction to step 3, after reading the generator.
goast marks a parameter variadic with variadic := isLast && isEllipsis (cmd/star/provider/goast/helpers.go:438). Go requires ... to be last, so nothing can follow the variadic parameter — there is no signature that is both Go-variadic and carries a sep keyword.
The bridge's kwargs sink does not rescue it: it must be last too, and a non-final []any is not marked variadic by that rule. That is why flow.Degraded's args []any is an ordinary list parameter, not a variadic sink.
So step 3 is a fork, not a preference:
Drop sep — Print(args ...any), joined with " "
sep first — inverts the ergonomics, makes print("a") bind to the separator. Rejected
Teach goast a declared variadic sink so Print(args []any, sep string) is expressible — generator and bridge work, for one keyword
Sweep result: zerosep= call sites across every .star file in the tree. Recommending 1.
Print(args ...any), joined with " ". Zero sep= call sites in the tree, so the narrowing costs nothing, and option 3 (teaching goast a declared variadic sink) stays available if the need ever appears.
Rationale from the ruling: sep has always been dumb.
Closing: no signature changes.ui.Provider methods keep (msg string).
Two candidate reconciliations with the builtin were considered and both fail:
fmt.Printf semantics — double interpretation. Starlark's % is an operator that runs before the call, so print("%s" % name) arrives as a finished string. Printf semantics would re-scan that string for verbs, turning 50% off into 50%!o(MISSING)ff whenever the data contains a %. Go avoids this by making Print and Printf different functions; one function cannot be both. Everyday form: print("100% done") becomes a bug.
Variadic space-join — the bridge has already destroyed the types.fillVariadicSlot runs toGoInto, so a Go-side args ...any receives Go natives: True as bool, None as nil, a list as []any. Rendering those Go-side gives true, <nil>, [a b] where Starlark says True, None, ["a"]. Faithful output would mean reimplementing writeValue on the far side of a conversion that already threw the information away.
(msg string) is therefore correct rather than a compromise: the script renders with str() and % — Starlark's own rendering, deterministic by construction — and hands over a finished string. For a configuration language, requiring explicit str() is a feature.
Survey of the tree confirms nothing breaks:
Call sites
Multi-argument
Non-string argument
print(
64
0
0
fail(
65
0
0
Every site passes one string, built with %, +, or an explicit str(...).
Residual failure, named:print("a", "b") and print(42) are legal against the builtin and become errors against us. Nothing in the tree does either, but a Python reflex will produce one. The errors are legible — toGoScalar uses starlark.AsString with no coercion, so print(42) reports expected string, got int. Documenting the one-string contract moved to #700.
Activity
David-Noble-at-work commentedon Aug 26, 2026
Correction to step 3, after reading the generator.
goastmarks a parameter variadic withvariadic := isLast && isEllipsis(cmd/star/provider/goast/helpers.go:438). Go requires...to be last, so nothing can follow the variadic parameter — there is no signature that is both Go-variadic and carries asepkeyword.The bridge's kwargs sink does not rescue it: it must be last too, and a non-final
[]anyis not marked variadic by that rule. That is whyflow.Degraded'sargs []anyis an ordinary list parameter, not a variadic sink.So step 3 is a fork, not a preference:
sep—Print(args ...any), joined with" "sepfirst — inverts the ergonomics, makesprint("a")bind to the separator. Rejectedgoasta declared variadic sink soPrint(args []any, sep string)is expressible — generator and bridge work, for one keywordSweep result: zero
sep=call sites across every.starfile in the tree. Recommending 1.David-Noble-at-work commentedon Aug 26, 2026
Settled: no
sepkeyword.Print(args ...any), joined with" ". Zerosep=call sites in the tree, so the narrowing costs nothing, and option 3 (teachinggoasta declared variadic sink) stays available if the need ever appears.Rationale from the ruling:
sephas always been dumb.David-Noble-at-work commentedon Aug 26, 2026
Closing: no signature changes.
ui.Providermethods keep(msg string).Two candidate reconciliations with the builtin were considered and both fail:
fmt.Printfsemantics — double interpretation. Starlark's%is an operator that runs before the call, soprint("%s" % name)arrives as a finished string. Printf semantics would re-scan that string for verbs, turning50% offinto50%!o(MISSING)ffwhenever the data contains a%. Go avoids this by makingPrintandPrintfdifferent functions; one function cannot be both. Everyday form:print("100% done")becomes a bug.Variadic space-join — the bridge has already destroyed the types.
fillVariadicSlotrunstoGoInto, so a Go-sideargs ...anyreceives Go natives:Trueasbool,Noneasnil, a list as[]any. Rendering those Go-side givestrue,<nil>,[a b]where Starlark saysTrue,None,["a"]. Faithful output would mean reimplementingwriteValueon the far side of a conversion that already threw the information away.(msg string)is therefore correct rather than a compromise: the script renders withstr()and%— Starlark's own rendering, deterministic by construction — and hands over a finished string. For a configuration language, requiring explicitstr()is a feature.Survey of the tree confirms nothing breaks:
print(fail(Every site passes one string, built with
%,+, or an explicitstr(...).Residual failure, named:
print("a", "b")andprint(42)are legal against the builtin and become errors against us. Nothing in the tree does either, but a Python reflex will produce one. The errors are legible —toGoScalarusesstarlark.AsStringwith no coercion, soprint(42)reportsexpected string, got int. Documenting the one-string contract moved to #700.All remaining work is on #700.