Assigning types to variables in Go - go

How to have a variable whose type is a variable type?
What do I mean by that? I have a python and java background. In both languages I can do things like assigning a class name to variable.
#Python
class A:
def __init__(self):
self.a = "Some Value"
# And asigning the class name to a variable
class_A = A
# And create instance from class A by using class_A
a = class_A()
Is there such a mechanism in go that allows me to do that? I couldn't know where to look at for such things in their documentation. Honestly, generally I don't know what these are called in programming languages.
For example I would like to be able to do:
// For example, what would be the type of myType?
var myType type = int
I will use this mechanism to take "type" arguments. For example:
func Infer(value interface{}, type gotype) {...}

Is there such a mechanism in go that allows me to do that?
The short answer is: No.
The long answer is: This sounds like an XY Problem, so depending on what you're actually trying to accomplish, there may be a way. Jump to the end to see where I address your specific use-case.
I suspect that if you want to store a data type in a variable, it's because you either want to compare some other variable's type against it, or perhaps you want to create a variable of this otherwise unknown type. These two scenarios can be accomplished in Go.
In the first case, just use a type switch:
switch someVar.(type) {
case string:
// Do stringy things
case int:
// Do inty things
}
Semantically, this looks a lot like assigning the type of someVar to the implicit switch comparison variable, then comparing against various types.
In the second case I mentioned, if you want to create a variable of an unknown type, this can be done in a round-about way using reflection:
type := reflect.TypeOf(someVar)
newVar := reflect.New(type).Interface()
Here newVar is now an interface{} that wraps a variable of the same type as someVar.
In your specific example:
I will use this mechanism to take "type" arguments. For example:
func Infer(value interface{}, type gotype) {...}
No, there's no way to do this. And it actually has much less to do with Go's variable support than it does with the fact that Go is compiled.
Variables are entirely a runtime concept. Function signatures (like all other types in Go) are fixed at compilation time. It's therefore impossible for runtime variables to affect the compilation stage. This is true in any compiled language, not a special feature (or lack thereof) in Go.

Is there such a mechanism in go that allows me to do that?
No there is not.

Use the empty interface:
var x, y interface{}
var a uint32
a = 255
x = int8(a)
y = uint8(a)
Playground example

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.

Get the underlying type from a string in go? [duplicate]

Is there a way to use the reflection libraries in Go to go from the name of a type to its Type representation?
I've got a library where the user needs to provide Type representations for some code generation. I know it must be possible (in a sense) because they can just create a variable of that type and call the TypeOf function, but is there a way to circumvent this and just get representation from the name?
The question is not quite explicit, it can be interpreted in 2 ways, to one of which the answer is no, not possible; and the other to which the answer is yes, it's possible.
At runtime
If the type name is provided as a string value, then at runtime it's not possible as types that are not referred to explicitly may not get compiled into the final executable binary (and thus obviously become unreachable, "unknown" at runtime). For details see Splitting client/server code. For possible workarounds see Call all functions with special prefix or suffix in Golang.
At "coding" time
If we're talking about "coding" time (source code writing / generating), then it's possible without creating / allocating a variable of the given type and calling reflect.TypeOf() and passing the variable.
You may start from the pointer to the type, and use a typed nil pointer value without allocation, and you can navigate from its reflect.Type descriptor to the descriptor of the base type (or element type) of the pointer using Type.Elem().
This is how it looks like:
t := reflect.TypeOf((*YourType)(nil)).Elem()
The type descriptor t above will be identical to t2 below:
var x YourType
t2 := reflect.TypeOf(x)
fmt.Println(t, t2)
fmt.Println(t == t2)
Output of the above application (try it on the Go Playground):
main.YourType main.YourType
true

Polymorphism without methods in go

Note: I'm editing this question to a concrete example of why I want to do this, which is why some of the answers might no longer make sense in context.
I am writing a bit of code that passes data from an input. The data is in the form of tags that have an identifier of what kind of data they contain and then the data.
Unfortunately I have no control over the input and don't know in advance what tags will be in it, one might be an int another might be a string, yet another might be an array of ints.
The problem arises when I need to handle all tags like the same type, for instance if I have a slice of tags, of a function that either accepts or returns a tag.
The solutions I have so far seen to this is to define the slices/functions with an empty interface which would allow me to do so, however that is kinda undesirable as it would not tell anything to other people using the package about what types are expected and also kinda defies the point of having a typed language in the first place.
Interfaces does however seem to be the solution here, and i would love to have a Tag interface to pass around, that does require though that I define methods on them, and there are really no methods they need.
My current solution looks like this
type Tag interface{
implementTag()
}
type TagInt int
func (tag TagInt) implementTag() {}
type TagString string
func (tag TagInt) implementTag() {}
While this does indeed work and solves my problem, having to define dummy methods just for that feels very wrong.
So my question sums up in this: Are there any way that I can define that something is a Tag without having to define dummy methods?
And now want to make a slice that can hold both t1 and t2 but nothing else.
You cannot do that. Sorry.
What I would do in your scenario is accept any type in the parameters with an empty interface then use a type assertion inside to confirm that it's the type that you want.
if t1, ok := interfaceInput.(t1); !ok{
// handle it being the wrong type here
return
}
Also if you want the tight coupling between a data type and it's method, namely an object, what's so wrong with having it be a method of the object?
You can use []interface{} for a "slice of any type" but then it's up to you to use type assertions and/or type switches to discover the actual runtime types of that slice's members.
Learn more about empty interfaces in the Tour of Go
And now want to make a slice that can hold both t1 and t2 but nothing
else.
This is quite an unusual requirement and you're unlikely to need this in Go. But you could also do your own discriminated union with:
type item struct {
typeSelector int
t1Value t1
t2Value t2
}
And then use []item, checking typeSelector at runtime to see which value is populated.
Alternatively you could even use *t1 and *t2 and have nil signify "no value in this field".

Initialize struct with global scope where type depends on boolean

I started using Golang recently and stumbled across a problem:
I have two structs, human and alien, which are both based on the creature struct. I want to initialize one of them based on the value of the isAlien boolean inside of an if-statement.
Using the human := human{} notation or the alien equivalent inside the if blocks to initialize, the instances aren't accessible from outside of the if-statement.
On the other hand, the usual solution of declaring the type and the name of the variable before the if-statement and initializing the variable inside the if-statement doesn't work, because there two are different types:
var h human //use human or alien here?
if isAlien {
h = alien{} //Error: incompatible types
} else {
h = human{}
}
//same when swapping human with alien at the declaration
I know that I could just declare both types before the if-statement but that solution doesn't seem elegant to me.
Is there some obvious solution that I'm missing here?
As you noted, the problem is clearly represented by this statement:
var h human //use human or alien here?
If you plan to use that h variable there after creating the objects, then the type of h must be one that can accept either a human or alien as a value.
The way to do this in Go is by using an ìnterface that both alien and human can fulfil.
So you should declare an interface like:
type subject interface {
// you should list all functions that you plan to use on "h" afterwards
// both "human" and "alien" must implement those functions
}
Then:
var h subject
Will do the trick.
So, I'm going to go out on a limb and say you're probably thinking about this the wrong way.
The first question that occurs to me looking at your example is: what's the return type of this function? In other words, what signature do you need h to be? If alien has an embedded struct creature (which seems to be the inheritance pattern you're trying to follow), and you return a human from your function after declaring h to be a creature, anything that consumes your function will only know that it's dealing with a creature, so there's no point in declaring it a human or an alien in the first place.
I suspect that what you really want to be doing is moving away from concrete structs here and instead using interfaces. In that world, you'd have a creature interface, and both human and alien would satisfy the creature interface. You wouldn't necessarily know which one you were dealing with downstream, but you'd be able to reliably call creature methods and the appropriate human or alien implementation would be invoked.

Rationale for Go's method syntax

Ok, I have to admit, I don't really use Go very much at all, but I did just observe something that strikes me as odd for a language that strives for minimality and all that good stuff as Go does. I would be surprised if there isn't a legitimate rationale behind it, so that's what I'm looking for.
So when you have a method, you define it like this:
func (s *SomeStruct) Foo(x int) { }
but why have an extra parameter list just for the "receiver", as I think it's called? Would it not be a simpler and more elegant design to just do
func Foo(s *SomeStruct, x int) { }
and then have s.Foo(5) just be translated to a call to a function Foo(s, 5)?
Methods are fundamentally special and different from regular functions.
Methods must live in the same package as the receiver type.
Methods are used to satisfy interfaces.
Receiver parameters are the only parameters which may be overloaded.
When anonymous struct fields have methods, those methods are "inherited".
With your proposal, the line between a function and a method becomes very blurry and it's difficult to figure out how to resolve the above issues.
That said, I think it would be really interesting to design a language with multimethods and interfaces. However, that language would not be Go.
Your question correctly points out that any method is a function. However, the Go language needs to be able to explicitly distinguish between methods and functions. The reason for this is that methods have features that functions do not have. The choice of whether Foo is a function or a method needs to be made by the programmer.
Go's minimalism means that the language defines only a small set of keywords. The Go authors could have chosen a new keyword, such as method, to distinguish methods from functions:
method Foo(receiver *T, arg1 int) {} // 'Foo' is a method, not a function
Looking around the Go programming language, we can see that the philosophy is to reuse already existing keywords rather than to have a separate keyword for each occasion. The for keyword is a good example of this approach:
for {} // Infinite loop
for a>0 {a--} // A while-do loop
for i := range channel {} // Receive values from a channel
for i:=0; i<N; i++ {} // C-style for loop
The basic idea is that for the parser (and Go programmers) to distinguish the various types of for loops from each other, there is no need to introduce a new keyword if the options can be distinguished by the syntax of what comes after the for keyword: ; := range identifier ..., their sequential order, and their presence/absence.
The func keyword follows the same pattern. It can be used in multiple contexts:
function definitions: func f() {}
function types: type F func(int) int
method definitions: func (t T) SomeMethod() {}
closures: { ...; go func(){c<-1}(); ...}
From minimalism viewpoint, a single func keyword is both simpler and more elegant than having multiple keywords.
The extra parameter list just for the receiver
func (t *T) Foo(x int) {}
enables the parser to distinguish methods and functions:
func IDENTIFIER ... This is going to be a function
func ( ... This is going to be a method
Thus, the parser (as well as Go programmers) can make the distinction based on whether the func keyword is followed by an identifier or by (.
The proposed replacement is not semantically identical to the current state, i.e. it's not a syntactic change only. It will [attempt to] automagically create methods of any [local package] type that happens to be the first parameter of a function. Considering how fundamental method sets are wrt to Go's concept of automatic interface satisfaction rules, this will quite probably lead to a big big mess.
In short, I think such change to the Go language improves nothing while damaging a lot of it's nice features related to methods and interfaces.
Probably because go isn't Python.
Also, because a function declared this way is not a method.
You cannot declare a method outside the object package. So I guess the main rationale in the syntax difference between methods and functions is to be able to differentiate them.

Resources