Skip to content
This repository has been archived by the owner on Jun 5, 2024. It is now read-only.

Generate non-pointer-laden Go types? #1

Open
sdboyer opened this issue Jun 8, 2022 · 4 comments
Open

Generate non-pointer-laden Go types? #1

sdboyer opened this issue Jun 8, 2022 · 4 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@sdboyer
Copy link
Contributor

sdboyer commented Jun 8, 2022

Right now, the generated Go types represent optionality in the schema using pointers. So, if we have a CUE/thema schema like this:

Obj: {
	field?: string | *"foo"
}

We'd get the corresponding Go struct:

type Obj struct {
	Field *string `json:"field,omitempty"`
}

This isn't great. It means the most convenient way of writing an Obj instance is:

func Ptr[T any](v T)*T { return &v }

var Obj {
	Field: Ptr("foo")
}

This is awkward. Generics makes this easier, but it's still pretty poor DX compared to just good ol' fashioned struct declarations.

Unfortunately, a nil pointer is the only mechanism Go's type system has for expressing the absence of a field/var. The presence of omitempty doesn't help us - that conflates existence with defaultness, which are things that CUE clearly delineates between. Go, OTOH, just has anemic zero values for defaults.

This results in a pile of ambiguities. With string, we can't tell if the user wants "", or simply didn't specify a value for the field:

  • If we observe the absence of field in encoded JSON, all we know for sure is that omitempty was declared on Obj. The user may or may not have wanted "" as the value "foo".
  • If we observe "field": "", all we know for sure is that omitempty wasn't on Obj; we don't know if the user wanted "" (in which case a default should not be applied) or simply declared nothing (in which case it should, but CUE can't do it anyway because it can't override the value).

These limitations apply whether in the encoded JSON or directly in Go (the former arises from the latter). https://go.dev/play/p/WbDTMNJ_hdx illustrates how only the presence of *string lets us disambiguate.

Still, i wonder if, perhaps in the limited context of use by a builder, we might generate types that don't have all the pointers, just for the sake of DX. Gonna keep this issue open as a reference/to ruminate.

@sdboyer sdboyer added enhancement New feature or request question Further information is requested labels Jun 8, 2022
@sdboyer
Copy link
Contributor Author

sdboyer commented Jun 8, 2022

Hmm, maybe the best of both worlds would be generating different code if CUE's default is the Go zero value

@sdboyer
Copy link
Contributor Author

sdboyer commented Jun 8, 2022

Also, non-pointer types would be much better for the read-time cases, and probably wouldn't have such issues with existence/default ambiguity so long as they don't need to be re-marshaled

@DanCech
Copy link

DanCech commented Jul 13, 2022

This is one of the most annoying aspects of go for me. Not sure what the best answer is but I would be very hesitant to lose the ability to disambiguate between "not specified" and "set to zero value"

@sdboyer
Copy link
Contributor Author

sdboyer commented Jul 14, 2022

Yeah, i quite agree. That's why i ultimately took the pointerful route for initial Go codegen.

i think i may have the pieces of at least a partial solution to it with Thema, but i don't yet see how they fit together into a pattern that i feel is simple enough for everyday use.

Specifically, i have two pieces:

  • With schemas, it's feasible to generate different variants of a type, rather than having to rely on just one. So for example, we generate two Go types - one with all the pointers, and one without.
  • Given a schema which can specify defaults (like Thema), this generic function can be used to create a type initializer that sets schema-specified defaults.

The generic initializer doesn't solve the ambiguity problem in non-pointer types. Nothing can. Still, i have the nagging feeling that there's something just outside my field of view that, combined with these two pieces, lets us significantly relieve the tension between DX and ambiguity.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants