cue-lang / docs-and-content

A place to discuss, plan, and track documentation on cuelang.org
5 stars 1 forks source link

docs/concept/understanding-cue-lexical-scopes: Understanding CUE's lexical scopes #118

Open jpluscplusm opened 2 months ago

jpluscplusm commented 2 months ago

From Slack: https://cuelang.slack.com/archives/CLT3ULF6C/p1709841334996459.

Original thread #### Andrei Hello, can someone please explain this scoping issue? I'd like to avoid setting `_protocol` inside the `s` struct and instead pick up the default from the `#ServiceEntry` definition but this behaviour is surprising to me ```cue #ServiceEntry: { _protocol: *"foo" | "bar" c: string } s: #ServiceEntry & { c: "\(_protocol)" } s.c: reference "_protocol" not found ``` #### Noam Dolovich The `_protocol` field is not in scope in `s`, you will need to reference it like this: ```cue s: #ServiceEntry s: X={ c: X._protocol } ``` In general though, it's a best practice to avoid setting default values as part of your schema - this makes the schema more general - it can be used in more places, and the consumer can decide what the default value should be, if any. Also, your setup could be simplified like this: ```cue #ServiceEntry: { protocol: "foo" | "bar" } s: #ServiceEntry s: protocol: _ | *"foo" ``` no need for the extra field (although it depends of course on your use case, it might be more complex than your example) #### Andrei hmm thanks, but it's still not clear that `_protocol` is not in the scope, because when I actually define it it's unified with the field from the definition... ```cue #ServiceEntry: { _protocol: "foo" | "bar" c: string } s: #ServiceEntry & { _protocol: "baz" c: "\(_protocol)" } s._protocol: conflicting values "bar" and "baz": ``` and for my usecase I'd rather use a definition containing a hidden field with a default value #### Noam Dolovich then my original suggestion should solve your problem (the first message) #### Andrei yes this should work ```cue #ServiceEntry: { _protocol: "foo" | *"bar" c: string } s: #ServiceEntry s: X={ c: X._protocol } ``` #### Noam Dolovich when writing a field identifier, it will only be found if it is in the lexical scope - otherwise you need to access it via a reference to a struct #### myitcv It feels like there is a good bit of content here relating to why _protocol is not in scope, despite it being "present" in the unified result. Something that really pushes hard at the "lexically scoped" aspect of CUE, forcing us to do a good job to explain it well with examples (and how dereferencing via an alias, for example, is "different")

What do we really mean by "lexical scope"? What does it mean for a CUE user unifying structs with other structs, or with a single struct's definition spread across multiple locations? cf. https://cuelang.org/docs/reference/spec/#declarations-and-scope.

Target audience: it feels to me like, using the right, carefully chosen examples, we should be able to write something that's accessible to the competent CUE novice - even one who isn't yet aware of CUE.