Ideation on how to utilize Go generics to design a better Go SDK experience for Pulumi users.
This requires a go1.18 toolchain that is currently pre-release.
It can be built from source directly or via a helper Nix wrapper:
https://github.com/t0yv0/pulumi-nix-devenv
$ cd pulumi-nix-devenv
$ nix-shell devenv-with-go-dev.nix
$ cd pulumi-go-generics
$ go build .
Having separate Input[T]
and Output[T]
has issues as @pgavlin
pointed out.
-
map[string]Input[T]
andmap[string]Output[T]
do not safely convert (more generally, types do not convert with I/O in nested positions) -
arguments typed
Input[T]
fail to inferT
at call site when passed anOutput[T]
-
generic aliases
type Input[T] = Output[T]
are prohibited
See examples4.go
.
Similar to what @frassle suggested for .NET the idea is to drop
Input[T]
type entirely. If Input exists for dev experience and not
fundamental needs, it may be removed in the name of dev experience. If
we do need some fundamentals like preserving promptness through
combinators and inspecting for promptness, this can be added to the
Output type implementation.
Some languages are happy to infer types and make the syntax of apply chains easy as in:
x.apply(y => y.apply(z => z.apply(f)))
With Go Generics, the options available seem to be too baroque. Apply cannot be a method (needs to be a top-level func).
func Apply[A any](
ctx *Context,
body func(t *T) A
) Output[A]
func Eval[A any](
t *T,
out Output[A],
) A
Apply(ctx, func(t *T) ZType {
y := Eval(t, x)
z := Eval(t, y)
return f(z)
})
More in examples3.go
.
There is clunkiness around variations on the All
theme:
All([]Output[T]) Output[[]T]
Variants like Pair, Triple, Tuple4 have difficulty expressing the explosion of generics. Boxing/unboxing is unreadable.
Dedicated All
is simply a sugar over Apply. See all.go
. Tuple
forms are omitted, use block Apply. See examples.go
In some languages we support equivalence of the two forms:
x.apply(xv => xv.foo)
x.foo
Previously solved by helper types with methods.
Block-form Apply and Eval get the user back to manipulating raw values, so for complex examples lifted property access loses some of the motivation. Possibly can consider helpers for common boilerplate scenarios.