Golang: Best way to evaluate a function once in a specific case - go

Suppose, I have a Eval struct and multiple goroutines can call its Evaluate method to get the value of the result field. Following is the basic structure of code:
type Eval struct {
done bool
result interface{}
}
func (e *Eval) Evaluate(f func() interface{}) interface{} {
if e.done == false {
e.result = f()
e.done = true
}
return e.result
}
As the function passed as argument of Evaluate method can take quite some time to execute, so I want to prevent calling that function multiple times. Only the first goroutine to call the Evaluate function will actually "evaluate" the value of result. The other goroutines will simply wait for the result field to be ready.
Here is what I came up with:
type Eval struct {
mu *sync.Mutex
cond *sync.Cond // cond uses mu as sync.Locker
once *sync.Once
done bool
result interface{}
}
func (e *Eval) Evaluate(f func() interface{}) interface{} {
go e.once.Do(func() {
defer e.cond.Broadcast()
v := f()
e.mu.Lock()
e.result = v
e.done = true
e.mu.Unlock()
})
e.mu.Lock()
if e.done == false {
e.cond.Wait()
}
v := e.result
e.mu.Unlock()
return v
}
But I feel like it is an overkill and using a sync.Mutex to control access to a variable that will be written once and read many times is not the efficient solution. What is the best and most "golang" way to achieve what I want?
Edit: As mentioned in comment, only using sync.Once does the job.

Related

How to combine two return params into one value

What I have many times in different versions in my code:
func f() (bool, bool) {
value, successFulOperation := someStuff()
return value, successFulOperation
}
// somewhere else
value, successfulOperation := f()
if value && successfulOperation {
// do stuff
}
// do stuff should only be executed if value is true AND the operation that retrieved value succeeded without an error. In other words: I don't care about value or successfulOperation. I only care about value && successfulOperation.
A solution I want to avoid (seems verbose):
value, successfulOperation := f()
actualValue := value && successfulOperation
if actualValue {...}
Above code is really simplified. In reality the if conditions will be nested and more complicated.
What I want:
A wrapper for f that combines both values into one. How do I do that? The solution should work for any function taking any parameters and returning two bools.
The following does not work:
type doubleBoolFunc func(...interface{}) (bool, bool)
func and(fn doubleBoolFunc, params ...interface{}) bool {
b1, b2 := fn(params...)
return b1 && b2
}
actualValue := and(f())
You can't write a wrapper function to turn the two bools into one until generics are in the language in go 1.181. Or at least you can, using reflect, but it's a mess.
But you can write this with go 1.17:
func both(x, y bool) bool {
return x && y
}
func f() (bool, bool) {
return true, false
}
func main() {
r := both(f())
fmt.Println(r)
}
But more practical (in my opinion) is to eschew this complication, and use a 1-line if. Not everything needs to be a function or abstracted away:
if a, b := f(); a && b {
...
}
[1] Even when generics are introduced in go 1.18, I don't think there'll be a way to specific a generic type that represents a function with arbitrary arguments that returns two bools.
func and(a, b bool) bool {
return a && b
}
then if f return 2 bool
value := and(f())
will work
To fix the code at the end of your question, don't invoke the function f(), simply reference the function name f and then list its args:
// actualValue := and(f(args...))
actualValue := and(f, args...)
https://go.dev/play/p/KAT2L58ZQy3

How to get return type of a function

I'm writing higher order functions in Go, and am trying to figure out the return type of the inner function f.
As a simple example, let's say I want to return the default value of the return type of the inner function:
if f returns string, GetDefault(f) returns ""
if f returns []byte, GetDefault(f) return []byte{}
func GetDefault(func(interface{})) {
// How would I write this function?
}
Is it possible to write such a function in Go, without running f?
You can use reflection to get the type and initialize a default value.
func GetDefault(f interface{}) interface{} {
ft := reflect.TypeOf(f)
if ft.Kind() != reflect.Func {
panic("not a func")
}
out0 := ft.Out(0) // type of the 0th output value
return reflect.New(out0).Elem().Interface()
}
https://play.golang.org/p/BhevFvsut5z

Can you get a data race when deferring a mutex unlock in go?

Is this Get method buggy and prone to a theoretical data race?
type item struct {
val int
mutex sync.RWMutex
}
func (i *item) Set(val int) {
i.mutex.Lock()
defer i.mutex.Unlock()
i.val = val
}
func (i *item) Get() int {
i.mutex.RLock()
defer i.mutex.RUnlock()
return i.val
}
I ask because I saw a rare data race when running my tests with -race with the former code, but can't find any way of duplicating the effect.
Is it possible for i.val to be set to a different value between when the defer carries out the RUnlock, and when we read and return the value from the struct?
Must Get() be something like this instead?:
func (i *item) Get() int {
i.mutex.RLock()
defer i.mutex.RUnlock()
val := i.val
return val
}
Your code is safe, deferred functions are executed after the expression list of the return statement is evaluated. If you would have named result parameters, the return values would also be assigned to them before calling the deferred functions (and you could even modify the return values before "truly" returning from the enclosing function).
No need to create a local variable to store i.val.

Function types in Go - particular type casting to more general type

What cast / assertion need I do in Go in order to pass to a function expecting a generic function like func(interface{}) interface{}, a more specific function like func(int) int instead?
For example, in code like this, fooA can be passed to MakeExclamer, but not fooB:
func MakeExclamer(foo func (interface{}) interface{}, n int) func () {
return func() {
fmt.Printf("%v!!!", foo(n))
}
}
func fooA(x interface{}) interface{} {
return x.(int)*2
}
func fooB(x int) int {
return x * 10
}
func main() {
exclamerA := MakeExclamer(fooA, 12)
exclamerA()
exclamerB := MakeExclamer(fooB, 66)
// >> cannot use fooB (type func(int) int) as type func(interface {}) interface {} in argument to MakeExclamer
exclamerB()
}
(Go Playground link: https://play.golang.org/p/xGzfco0IAG)
I'm not interested much in alternative code structure patterns, since this is how I want it to work: a specific function should be passed to a general function transformer (accepting function of type Any -> Any) that will return another general function (Any -> Any). This may not be idiomatic in Go, but it is the pattern that I want my code to follow.
To use type assertions, every possible type must be enumerated in MakeExclamer:
func MakeExclamer(fn interface{}, arg interface{}) func() {
switch fn := fn.(type) {
case func(int) int:
return func() {
fmt.Printf("%v!!!\n", fn(arg.(int)))
}
case func(interface{}) interface{}:
return func() {
fmt.Printf("%v!!!\n", fn(arg))
}
default:
panic("not supported")
}
}
To accept a function of any type, the fn argument is declared as type interface{}. The code uses a type switch to handle the different function types.
playground example
Reflection can be used to write a more general function.
func MakeExclamer(fn interface{}, arg interface{}) func() {
fnr := reflect.ValueOf(fn)
argr := reflect.ValueOf(arg)
return func() {
resultr := fnr.Call([]reflect.Value{argr})
fmt.Printf("%v!!!\n", resultr[0].Interface())
}
}
playground example
First things first : When it comes to typing in Go, everything is theoretically possible. That's because even though the compiler does a lot of checks at compile-time, it is possible to change the runtime... at runtime. So-called runtime hacks, where you dynamically manipulate runtime structs that you're NOT supposed to handle.
Now, you have an interesting question, whose answer doesn't include the need to use the 'unsafe' package. However, the way I found of generalizing a function involves heavy reflection.
How to call a function (via reflection) ?
The documentation for the reflect package can be found here.
So, like all elements in Golang, functions have a Type. Without going through all fields, functions do take an array of arguments and produce an array of results. It is possible to investigate the Type of arguments and results through the In(int) and Out(int) method.
func investigate(fn interface{}) {
fnType := reflect.TypeOf(fn)
for idx := 0; idx < fnType.NumIn(); idx ++ {
fmt.Printf("Input arg %d has type %v\n", idx, fnType.In(idx))
}
for idx := 0; idx < fnType.NumOut(); idx ++ {
fmt.Printf("Output arg %d has type %v\n", idx, fnType.Out(idx))
}
}
We won't use this code. However, two important things are to be noted at this point :
The generic type under which a function can be passed around without caring about its type is interface{}. Something like "func(interface{}) interface{}" is not a generalization of a function, it is already a concrete type. Hence, "func(interface{}) interface{}" is not a generalization of "func(int) int", those are two different function types entirely. This is why you can't use type assertions/cast to convert from one function type to another.
A function can be represented as something that takes an input array and produces and output array.
Now, in order to call a function, you have to get not its Type, but its Value. Once you get its value, you can call it using an array of arguments, which must all be Values.
The prototype is:
func (v Value) Call(in []Value) []Value
Using this method, it is possible to call any function.
The code
So, the only thing you need is to convert whichever arguments array you have to an array of Values, then you will be able to call your function.
Here is your code:
package main
import (
"fmt"
"reflect"
)
func MakeExclamer(foo interface{}, n int) func() {
exclamer := generalize(foo, n)
return func() {
fmt.Printf("%v!!!\n", exclamer())
}
}
func fooA(x interface{}) interface{} {
return x.(int) * 2
}
func fooB(x int) int {
return x * 10
}
func generalize(implem interface{}, args ...interface{}) func() interface{} {
valIn := make([]reflect.Value, len(args), len(args))
fnVal := reflect.ValueOf(implem)
for idx, elt := range args {
valIn[idx] = reflect.ValueOf(elt)
}
ret := func() interface{} {
res := fnVal.Call(valIn)
// We assume the function produces exactly one result
return res[0].Interface()
}
return ret
}
func main() {
exclamerA := MakeExclamer(fooA, 12)
exclamerA()
exclamerB := MakeExclamer(fooB, 66)
exclamerB()
}
Playground
The important bit is the generalize function which makes the translation between your arguments and an array of Values, then returns a new function whith all parameters already filled.
Do not hesitate if you need any precision !

Functions type converting

How can I convert func add (a, b int) int to func(...interface{}) interace{} type ?
Any ideas about implementing generic functions using the reflect package ?
As JimB said, you can't cast in Go and you cannot convert functions just like that but by using closures, you can rapidly wrap your function:
func add(a, b int) int {
return a + b;
}
wrap := func(args ...interface{}) interface{} {
return interface{} (add(args[0].(int), args[1].(int)))
}
Note that wrap will panic if you give it arguments that are not of type int. If you want to avoid that you can slightly modify wrap:
wrap := func(args ...interface{}) (interface{}, error) {
a, k := args[0].(int)
b, l := args[1].(int)
if !k || !l {
return nil, errors.New("Arguments must be of type int")
}
return add(a,b), nil
}
If you'd like to do different things with wrap, depending on it's arguments types you can do so by using a type switch:
func addInts(a, b int) int {
return a + b;
}
func addFloat64s(a, b float64) float64 {
return a + b;
}
wrap := func(args ...interface{}) interface{} {
switch args[0].(type) {
case int: return interface{}(addInts(args[0].(int), args[1].(int)))
case float64: return interface{}(addFloat64s(args[0].(float64), args[1].(float64)))
}
}
Note that this last version of wrap makes the assumption that all given parameters will have the same type and at least 2 arguments are given.
There is no "casting" is go (well, using the "unsafe" package kind of is like casting).
You cannot convert function types like this, since they have different layouts in memory. Generic-like functions can be made through the reflect package, though with significant overhead. See http://golang.org/pkg/reflect/#example_MakeFunc for an example.
For most use cases of generic functions, you're probably better off accepting an interface, and using type assertions or switches (http://golang.org/ref/spec#Type_switches), rather than the reflection library.

Resources