I know that Go doesn't support templates or overloaded functions, but I'm wondering if there's any way to do some kind of generic programming anyway?
I have many functions such as these:
func (this Document) GetString(name string, default...string) string {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(default) >= 1 {
return default[0]
} else {
return ""
}
}
return v.asString
}
func (this Document) GetInt(name string, default...int) int {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(default) >= 1 {
return default[0]
} else {
return 0
}
}
return v.asInt
}
// etc. for many different types
Is there any way to do this without having so much redundant code?
The most of what you can achieve is usage of interface{}
type, something like this:
func (this Document) Get(name string, default... interface{}) interface{} {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(default) >= 1 {
return default[0]
} else {
return 0
}
}
return v
}
GetValueFromDb
function should also be tweaked to return interface{}
value and not some wrapper like now.
Then in the client code you can do the following:
value := document.Get("index", 1).(int) // Panics when the value is not int
or
value, ok := document.Get("index", 1).(int) // ok is false if the value is not int
This will yield some runtime overhead though. I'd better stick with separate functions and try to restructure the code somehow.
Here's a working example of how you could change your code.
Since you know what type you expect for the given name, you can write your Get method in a generic way, returning interface{}
, and then assert the type at the call site. See the spec about type assertions.
There are different ways to emulate some aspects of generics in Go. There were lots of discussions on the mailing list. Often, there's a way to restructure code so it's less dependent on generics.
In the client code you can do like this :
res := GetValue("name", 1, 2, 3)
// or
// res := GetValue("name", "one", "two", "three")
if value, ok := res.(int); ok {
// process int return value
} else if value, ok := res.(string); ok {
// process string return value
}
// or
// res.(type) expression only work in switch statement
// and 'res' variable's type have to be interface type
switch value := res.(type) {
case int:
// process int return value
case string:
// process string return value
}