Anonymous struct as pipeline in template - go

Is there a way to do the following in a html/template?
{{template "mytemplate" struct{Foo1, Foo2 string}{"Bar1", "Bar2"}}}
Actually in the template, like above. Not via a function registered in FuncMap which returns the struct.
I tried it, but Parse panics, see Playground. Maybe just the syntax is wrong?

As noted by others, it's not possible. Templates are parsed at runtime, without the help of the Go compiler. So allowing arbitrary Go syntax would not be feasible (although note that it wouldn't be impossible, as the standard lib contains all the tools to parse Go source text, see packages "prefixed" with go/ in the standard lib). By design philosophy, complex logic should be outside of templates.
Back to your example:
struct{Foo1, Foo2 string}{"Bar1", "Bar2"}
This is a struct composite literal and it is not supported in templates, neither when invoking another template nor at other places.
Invoking another template with a custom "argument" has the following syntax (quoting from text/template: Actions):
{{template "name" pipeline}}
The template with the specified name is executed with dot set
to the value of the pipeline.
TL;DR; A pipeline may be a constant, an expression denoting a field or method of some value (where the method will be called and its return value will be used), it may be a call to some "template-builtin" function or a custom registered function, or a value in a map.
Where Pipeline is:
A pipeline is a possibly chained sequence of "commands". A command is a simple value (argument) or a function or method call, possibly with multiple arguments:
Argument
The result is the value of evaluating the argument.
.Method [Argument...]
The method can be alone or the last element of a chain but,
unlike methods in the middle of a chain, it can take arguments.
The result is the value of calling the method with the
arguments:
dot.Method(Argument1, etc.)
functionName [Argument...]
The result is the value of calling the function associated
with the name:
function(Argument1, etc.)
Functions and function names are described below.
And an Argument is:
An argument is a simple value, denoted by one of the following.
- A boolean, string, character, integer, floating-point, imaginary
or complex constant in Go syntax. These behave like Go's untyped
constants. Note that, as in Go, whether a large integer constant
overflows when assigned or passed to a function can depend on whether
the host machine's ints are 32 or 64 bits.
- The keyword nil, representing an untyped Go nil.
- The character '.' (period):
.
The result is the value of dot.
- A variable name, which is a (possibly empty) alphanumeric string
preceded by a dollar sign, such as
$piOver2
or
$
The result is the value of the variable.
Variables are described below.
- The name of a field of the data, which must be a struct, preceded
by a period, such as
.Field
The result is the value of the field. Field invocations may be
chained:
.Field1.Field2
Fields can also be evaluated on variables, including chaining:
$x.Field1.Field2
- The name of a key of the data, which must be a map, preceded
by a period, such as
.Key
The result is the map element value indexed by the key.
Key invocations may be chained and combined with fields to any
depth:
.Field1.Key1.Field2.Key2
Although the key must be an alphanumeric identifier, unlike with
field names they do not need to start with an upper case letter.
Keys can also be evaluated on variables, including chaining:
$x.key1.key2
- The name of a niladic method of the data, preceded by a period,
such as
.Method
The result is the value of invoking the method with dot as the
receiver, dot.Method(). Such a method must have one return value (of
any type) or two return values, the second of which is an error.
If it has two and the returned error is non-nil, execution terminates
and an error is returned to the caller as the value of Execute.
Method invocations may be chained and combined with fields and keys
to any depth:
.Field1.Key1.Method1.Field2.Key2.Method2
Methods can also be evaluated on variables, including chaining:
$x.Method1.Field
- The name of a niladic function, such as
fun
The result is the value of invoking the function, fun(). The return
types and values behave as in methods. Functions and function
names are described below.
- A parenthesized instance of one the above, for grouping. The result
may be accessed by a field or map key invocation.
print (.F1 arg1) (.F2 arg2)
(.StructValuedMethod "arg").Field
The proper solution would be to register a custom function that constructs the value you want to pass to the template invocation, as you can see in this related / possible duplicate: Golang pass multiple values from template to template?
Another, half solution could be to use the builtin print or printf functions to concatenate the values you want to pass, but that would require to split in the other template.

As mentioned by #icza, this is not possible.
However, you might want to provide a generic dict function to templates to allow to build a map[string]interface{} from a list of arguments. This is explained in this other answer: https://stackoverflow.com/a/18276968/328115

Related

Using tostring() to ensure functional conditionals in Terraform?

tl;dr: Is it an acceptable practice to use tostring() to cast values used for conditionals in Terraform >= 0.13 for handling a strictly defined set of input types?
Yesterday I asked a question that led me to a new question today:
Terraform count using bool?
What I learned is that there is some automatic type-casting applied to certain primitives in Terraform (going to and from strings to other data types mainly), but that these primitives cannot be used to infer a different data type (e.g. a bool cannot be passed as an input to the count argument because count only accepts a number type.
One comment on that question had a very simple way to use a bool as a condition:
count = var.my_var ? 1 : 0
The only potential issue with this, is if my_var can have different input types. In my use case, it'll be added to a Terraform module in which the user will decide what to supply for this argument; previously we've only been passing in string or number, but I find that to be a little less specific than I'd prefer, because Terraform can interpret count to be > 1 copy of a resource (I want a discrete 0 or 1 [specifically for something like var.create_this_resource whose value can be either true or false]); this also just doesn't look as nice to see "1" vs true IMO. So I'd like to start using bool instead, but also be able to handle when a user inputs a number. What I found is that I can use the following to accomplish this:
count = tostring(var.my_var) ? 1 : 0
Here, tostring() will take whatever is in the input and, presumably, cast it to a string. It only works for string, number, and boolean, and really, I'm only using it to get a number to a string because that's the only case where passing into a ternary operator is currently failing.
So my question is whether or not it's acceptable to do this? I've tested it with string, bool, and number, as well as unsupported types (i.e. an empty list or null); it seems to work well in code but the following made me think I shouldn't use it:
From the docs:
Explicit type conversions are rarely necessary in Terraform because it will convert types automatically where required. Use the explicit type conversion functions only to normalize types returned in module outputs.
In most cases I would suggest avoiding designs where a particular variable could have different types in different situations, unless your module is treating the value as entirely opaque and just passing it through to something else which has broader validation rules.
Since your module is working directly with this value, it would typically be best to specify an exact type constraint for the variable and make the caller of the module write expressions to convert the value if the automatic conversions are insufficient. That way the caller can get better feedback about what sort of value your module is expecting, and can decide for themselves how to convert their value of a different type.
Converting to string can only produce a value that can automatically convert to bool in the following situations:
The value was already a string, and was either "true" or "false".
The value was a bool value, in which case tostring will convert it to a string and then the conditional operator will immediately convert it back to bool again, which would be redundant.
If you declare the variable as being bool itself then the same rules will apply, but the conversion will happen inside the calling module block rather than in the count expression:
variable "my_var" {
type = bool
}
module "example" {
# ...
# This will automatically convert to bool true,
# just as it would've in the conditional operator.
my_var = "true"
}
If you really cannot avoid supporting various unusual ways of writing boolean values then you can potentially write your own conversion table which would be based on strings, and would specify the boolean value for each possible string after conversion:
locals {
sloppy_bool = tomap({
"1" = true
"true" = true
"0" = false
"false" = false
})
my_var = local.sloppy_bool[var.my_var]
}
Because mapping types (map types and object types) only support strings as keys, local.sloppy_bool[var.my_var] will automatically convert var.my_var to string, just as if you'd written tostring(var.my_var). It'll then look up the result in the table and return the corresponding boolean value, which means you can then use local.my_var instead of var.my_var elsewhere in your module and rely on it always being a true boolean value.
I would suggest doing this only if you had a previous version of the module which tolerated this sort of typing weirdness and you need to remain compatible with it. For an entirely new module, I would consider this to be non-idiomatic and probably confusing for anyone already familiar with Terraform who is trying to use the module, because they will need to become familiar with your unusual definition of the type conversion rather than relying on their knowledge of the built-in conversion rules.

Function with 1/2 values and short variable declaration assigned twice to same variable

I have two questions for following code
emptyinterface.(int) can return one or two values, how the function is defined to achieve that effect?
ok has been declared twice using short variable declaration, why it is possible in this context?
package main
import (
"fmt"
)
func main() {
var emptyinterface interface{}
emptyinterface=4
i1:=emptyinterface.(int)
fmt.Println(i1)
i2,ok:=emptyinterface.(int)//<- how the function is defined such that it can return either 1 (i1) or 2 values (i2,ok)?
fmt.Println(i2,ok)
i3,ok:=emptyinterface.(string) //<--why I can reassign to ok, which has assign previously?
fmt.Println(i3,ok)
}
It's not a function, it's a language feature. You can't write a function that does that, but the compiler writers can create a bit of syntax that does.
A := is invalid if there are no new variables on its left side. If there is at least one new variable being declared, it's allowed.
In each of the cases, there is at-least new variable created along with ok, i.e. i2 and i3, so redeclaration of ok is perfectly fine.
This is well documented in the language spec (emphasis mine) under Short variable declarations
Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration.
Also it is unclear, what you are referring as a function here, Type assertion is a feature of the language that asserts if a value within the interface is of a particular type. It always returns the underlying value if the assertion was successful or a failure if its not. You should always be checking the return value of the type assertion (2nd argument) before meaningfully using it elsewhere.

How does a loosely typed language know how to handle different data types?

I was working on a simple task yesterday, just needed to sum the values in a handful of dropdown menus to display in a textbox via Javascript. Unexpectedly, it was just building a string so instead of giving me the value 4 it gave me "1111". I understand what was happening; but I don't understand how.
With a loosely typed language like Javascript or PHP, how does the computer "know" what type to treat something as? If I just type everything as a var, how does it differentiate a string from an int from an object?
What the + operator will do in Javascript is determined at runtime, when both actual arguments (and their types) are known.
If the runtime sees that one of the arguments is a string, it will do string concatenation. Otherwise it will do numeric addition (if necessary coercing the arguments into numbers).
This logic is coded into the implementation of the + operator (or any other function like it). If you looked at it, you would see if typeof(a) === 'string' statements (or something very similar) in there.
If I just type everything as a var
Well, you don't type it at all. The variable has no type, but any actual value that ends up in that variable has a type, and code can inspect that.

Why are 'new' and 'make' not reserved keywords?

With syntax highlighting enabled, it's distracting while reading code like answer to this question with new used as a variable name.
I'm trying to think of a reason why only a subset of keywords would be reserved and can't come up with a good one.
Edit: Alternate title for this question:
Why are Go's predeclared identifiers not reserved ?
That's because new and make aren't really keywords, but built-in functions.
If you examine the full list of the reserved keywords, you won't see len or cap either...
In short: because using predeclared identifiers in your own declarations don't make your code ambiguous.
new and make are builtin functions, amongst many others. See the full list in the doc of the builtin package.
Keywords are a carefully chosen small set of reserved words. You cannot use keywords as identifiers because that could make interpreting your source ambiguous.
Builtin functions are special functions: you can use them without any imports, and they may have varying parameter and return list. You are allowed to declare functions or variables with the names of the builtin functions because your code will still remain unambiguous: your identifier in scope will "win".
If you would use a keyword as an identifier, you couldn't reach your entity because an attempt to refer to it by its name would always mean the keyword and not your identifier.
See this dirty example of using the predeclared identifiers of the builtin function make and the builtin type int (not recommended for production code) (try it on the Go Playground):
make := func() string {
return "hijacked"
}
int := make() // Completely OK, variable 'int' will be a string
fmt.Println(int) // Prints "hijacked"
If you could use keywords as identifiers, even interpreting declarations would cause headache to the compiler: what would the following mean?
func() - is it calling your function named func or is it a function type with no params and no return types?
import() - is it a grouped import declaration (and importing nothing) or calling our function named import?
...

What expressions are allowed in tracepoints?

When creating a tracepoint in Visual Studio (right-click the breakpoint and choose "When Hit..."), the dialog has this text, emphasis mine:
You can include the value of a variable or other expression in the message by placing it in curly braces...
What expressions are allowed?
Microsoft's documentation is rather sparse on the exact details of what is and is not allowed. Most of the below was found by trial and error in the Immediate window. Note that this list is for C++, as that's what I code in. I believe in C#, some of the prohibited items below are actually allowed.
Most basic expressions can be evaluated, including casting, setting variables, and calling functions.
General Restrictions
Only C-style casts supported; no static_cast, dynamic_cast, reinterpret_cast, const_cast
Can't declare new variables or create objects
Can't use overloaded operators
Ternary operator doesn't work
Can't use the comma operator because Visual Studio uses it to format the result of the expression; use multiple sets of braces for multiple expressions
Function Calls
Prohibited calls:
Lambdas (can't define or call them)
Functions in an anonymous namespace
Functions that take objects by value (because you can't create objects)
Permitted calls:
Member functions, both regular and virtual
Functions taking references or pointers, to either fundamental or class types
Passing in-scope variables
Using "&" to pass pointers to in-scope variables
Passing the literals "true", "false", numbers
Passing string literals, as long you don't run afoul of the "can't create objects" rule
Calling multiple functions with one tracepoint by using multiple sets of braces
Variable Assignment
Prohibited:
Objects
String literals
Permitted:
Variables with fundamental types, value either from literals or other variables
Memory addresses, after casting: { *(bool*)(0x1234) = true }
Registers: { #eip = 0x1234 }
Use Cases
Calling functions from tracepoints can be quite powerful. You can get around most of the restrictions listed above with a carefully set up function and the right call. Here are some more specific ideas.
Force an if
Pretty straightforward: set up a tracepoint to set a variable and force an if-condition to true or false, depending on what you need to test. All without adding code or leaving the debug session.
Breakpoint "toggling"
I've seen the question a few times, "I need to break in a spot that gets hit a lot. I'd like to simply enable that breakpoint from another breakpoint, so the one I care about only gets breaks from a certain code path. How can I do that?" With our knowledge above, it's easy, although you do need a helper variable.
Create a global boolean, set to false.
Create a breakpoint at your final destination, with a condition to break only when the global flag is true.
Set tracepoints in the critical spots that assign the global flag to true.
The nice thing is that you can move the tracepoints around without leaving the debugging session. Use the Immediate window or the Watch window to reset your global flag, if you need to make another run at it. When you're done, all you need to clean up is that global boolean. No other code to remove.
Automatically skip code
The EIP register (at least on x86) is the instruction pointer. If you assign to it, you can change your program flow.
Find the address of the line you want to skip to by breaking on it once and looking at the value of EIP, either in the Registers window or the Watch window with "#eip,x". (Note that the value in the Registers window is hex, but without the leading "0x".)
Add a tracepoint on the line you want to skip from, with an expression like {#eip = address}, using the address from step 1.
EIP assignment will happen before anything on the line is executed.
Although this can be handy, be careful because skipping code like this can cause weird behavior.
As Kurt Hutchinson says, string assignment is not allowed in a tracepoint. You can get around this by creating a method that assigns the string variable, and call that.
public static class Helper
{
public static void AssignTo(this string value, out string variable)
{
variable = value;
}
}
Then in your tracepoint message:
{"new string value".AssignTo(out stringVariable)}

Resources