Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gohcl: WithRange[T] for concise decoding with source locations #516

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

apparentlymart
Copy link
Member

@apparentlymart apparentlymart commented Feb 28, 2022

Until now gohcl has forced callers to decide between two non-ideal options:

  • Decode directly into a "normal" Go type, like string, but then have no access to the source location of that value.
  • Decode into an hcl.Expression and then separately decode that expression into a normal type, which gives access to the source location but requires an extra decoding step downstream.

Source location information is important if subsequent code does any further validation of the value beyond what HCL can do itself, and so this forces implementers to decide between returning low-quality error messages or writing more lines of code for decoding, and since a lot of people would prefer to write less code despite the consequences, this has therefore led to software with poor-quality error messages.

Go 1.18 provides a compromise: we can use a generic struct type to pair up any normal value with an hcl.Range value, thus still achieving a one-shot decode behavior for simpler applications but giving those applications convenient access to both the resulting value and the source location it came from.

This is essentially just another "special" type that gohcl has bespoke handling for, similar to hcl.Body, hcl.Expression, and hcl.Attributes. If decoding an expression into a value of a WithRange[T] type then it behaves as if decoding into a T directly, but writes the result into the Value field of the WithRange[T] alongside the source range.

An important goal here is to do this without requiring all callers of this module to use Go 1.18. To achieve that, the generic code is all in a conditionally-compiled file, and we assume that any caller which includes WithRange[T] would inherently require 1.18 itself anyway, so we won't enter the codepath for handling this type on older versions of Go.


This is just a prototype for now. It needs some more thorough testing with various interesting variations to see if it's behavior is reasonable in each case. For example:

  • WithRange[cty.Value]
  • *WithRange[Foo] vs. WithRange[*Foo] for optional attributes.
  • WithRange[hcl.Expression] (redundant, but should either work or return an explicit error explaining why not)
  • Is it reasonable to use WithRange[T] for a field representing a nested block type, rather than one representing an attribute? Should the range in that case be the block's DeclRange, or something else?
  • Is it reasonable to use WithRange[T] on a ,remain-tagged field? (Probably not, but it should still return a sensible error message if someone tries.)
  • ...?

Until now gohcl has forced callers to decide between two non-ideal options:
 - Decode directly into a "normal" Go type, like string, but then have no
   access to the source location of that value.
 - Decode into an hcl.Expression and then separately decode that
   expression into a normal type, which gives access to the source
   location but requires an extra decoding step downstream.

Source location information is important if subsequent code does any
further validation of the value beyond what HCL can do itself, and so this
forces implementers to decide between returning low-quality error messages
or writing more lines of code for decoding, and since a lot of people
would prefer to write less code despite the consequences, this has
therefore led to software with poor-quality error messages.

Go 1.18 provides a compromise: we can use a generic struct type to pair up
any normal value with an hcl.Range value, thus still achieving a one-shot
decode behavior for simpler applications but giving those applications
convenient access to both the resulting value and the source location it
came from.

This is essentially just another "special" type that gohcl has bespoke
handling for, similar to hcl.Body, hcl.Expression, and hcl.Attributes. If
decoding an expression into a value of a WithRange[T] type then it behaves
as if decoding into a T directly, but writes the result into the Value
field of the WithRange[T] alongside the source range.
@apparentlymart apparentlymart added enhancement v2 Relates to the v2 line of releases gohcl labels Feb 28, 2022
@apparentlymart apparentlymart self-assigned this Feb 28, 2022
@hashicorp-cla
Copy link

hashicorp-cla commented Mar 12, 2022

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement gohcl v2 Relates to the v2 line of releases
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants