Skip to content

Instantly share code, notes, and snippets.

Created October 5, 2017 16:08
Show Gist options
  • Save anonymous/a1f016937b2b45ed57af4b42bff47cc3 to your computer and use it in GitHub Desktop.
Save anonymous/a1f016937b2b45ed57af4b42bff47cc3 to your computer and use it in GitHub Desktop.
The worst idea i've ever had
package chain
import (
"fmt"
"reflect"
)
// Chain takes an array of functions, and calls them all, passing the result of the previous call to the next. Any funcs whose last return value is an error have the errors checked and not passed to the next. If an error occurs at any point chain returns with an empty list and the error that happened.
// here it is expressed vaguely maths-y which might be clearer.
// given a function f[n] which returns a set of values vs:
// if the last element of vs is of a type which implements the error type,
// the last element is removed from vs and stored as err.
// if err is not nil, return an empty slice of values and err.
// otherwise, call f[n+1](vs)
func Chain(funcs ...interface{}) (res []interface{}, err error) {
data := []reflect.Value{}
for _, f := range funcs {
val := reflect.ValueOf(f)
typ := reflect.TypeOf(f)
if typ.Kind() != reflect.Func {
data = []reflect.Value{}
err = fmt.Errorf("one of the funcs wasn't a func.")
return
}
data = val.Call(data)
if len(data) > 0 {
errVal := data[len(data)-1]
if errVal.Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) {
errI := errVal.Interface()
if errI != nil {
err = errI.(error)
return
}
data = data[:len(data)-1]
}
}
}
res = make([]interface{}, len(data))
for i, d := range data {
res[i] = d.Interface()
}
return
}
package chain_test
import (
"fmt"
"math"
"strconv"
)
func ExampleChain() {
res, err := chain.Chain(
func() (string, int) { return "400.2", 32 },
strconv.ParseFloat,
math.Ceil,
func(f float64) (string, float64) { return "%0.f", f },
fmt.Sprintf,
)
if err != nil {
fmt.Println("there was an error!")
}
fmt.Println(res[0])
// Output: 401
}
@telyn
Copy link

telyn commented Oct 5, 2017

this horror is on me - I've forked it because re-uploading seemed daft.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment