-
-
Save telyn/7ee88a52a68367075e6dff1f1bdf1af8 to your computer and use it in GitHub Desktop.
Function chaining in go. It's essentially the same as the Either Monad - but a little less monad-y
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package chain_test | |
import ( | |
"fmt" | |
"math" | |
"strconv" | |
chain "." | |
) | |
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment