-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
proposal: text/template: ability to resolve values from a dataset using templates #67766
Comments
CC @robpike |
Templates are for formatting data into text. They are basically printf on steroids. I do not think that should change. In any case I think this would be both rarely used and would require a lot of preconditions because a typical template processes many values. Yes, you have presented a case where it makes sense, but I still feel it is an unusual situation not worth changing the basics of the template package. |
While I generally agree that the specific use-case above is unusual and beyond the scope of the regular usage of the template package, there is simply no way to do what we are trying to do with the current public API because Go's templating syntax is extremely powerful, and it seems to be a shame to restrict it to outputting strings when it could also be used for parsing/manipulating data at runtime with a relatively small, non-breaking change. This would open up a lot of possibilities for software that wants to expose the templating syntax to its users.
If we want to make the distinction between a template and a "resolver", we could make a separate This would still need to be contained within the |
I have wanted this before, but the reason I wanted it was that I wanted to use Go templates as the engine for my own new templating language. I think maybe exposing the ability to do Could you just fork the template library and expose the internals in your fork? |
This proposal has been declined as infeasible. |
Proposal Details
Background
Given a template string and a dataset, the
text/template
package allows a user to write data from the dataset as a formatted string to anio.Writer
. However, currently, it is not possible to use a template to resolve and return a value and preserve its type from a dataset.For example, suppose we set up a template and dataset as follows:
We are now able to execute the template with our dataset by calling the
Execute
method. This will write the formatted string to the givenio.Writer
.However, the output of the template is always a string and not the actual value from the dataset. There are a variety of use cases where it would be useful to fetch and mutate data while preserving types from a dataset using templating syntax - particularly when a template is given at runtime in software that surfaces Go's templating syntax.
Proposal
Add a new method to the
Template
type in thetext/template
package that allows the user to return a variable with its underlying type given a template and dataset. More precisely, a method with the following (or similar) signature:Using the same template and dataset example as before, we can now resolve the value from the dataset and preserve its type:
More interestingly, this allows us to do things like this:
It may be tempting to ask why we need templates to do this instead of doing something simple like this:
However, as I mentioned in the background, in cases where a template is being supplied at runtime by a user of the software, we need to use the templating engine to resolve the value. The case study below describes a real-world example of this.
Since this only really makes sense with a single
ActionNode
, callingResolve
on a template with multiple nodes or a non-ActionNode
should return an error.Case Study
I am one of the maintainers of Task, which is a popular task runner and build tool written in Go. One of the useful features of Task is the ability to use Go's templating engine to template tasks. Until recently, all variables in Task were strings, so using one variable inside another variable was as simple as referencing it in a Go template:
However, we recently added made our "Any Variables" experiment generally available. This allows users to define variables of "any" type (i.e.
string
,int
slice
etc.). This is great because users now have much more flexibility in how they define/process their task data. However, passing these variables from one task to another was only possible using templating and this caused variables to be stringified.To resolve this, we added a new
ref
keyword which will allow users to directly reference a variable and maintain its type:We decided that we wanted to keep using Go's templating engine to do the reference resolving. This has a couple of advantages:
Since this wasn't possible to do using the public API of the standard library's
text/template
package, we have forked the package and implemented the proposed API. The two additional methods can be viewed below. This is a provisional implementation specifically for Task. However, it is in our latest release and working well for us so far.Template.Resolve
- https://github.com/go-task/template/blob/960e6f576656d7e42a83a98307241c86a660f357/exec.go#L234-L253state.actionValue
- https://github.com/go-task/template/blob/960e6f576656d7e42a83a98307241c86a660f357/exec.go#L286-L301Since there aren't often significant changes to this package, we are happy to maintain this fork. However, we feel like this change would be a useful addition to the standard library and could benefit other users too.
The text was updated successfully, but these errors were encountered: