Last active
November 24, 2023 01:06
-
-
Save choonkeat/bc603c508b1f5a589cc0040ff18d9679 to your computer and use it in GitHub Desktop.
Task type in Go
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 main | |
import ( | |
"fmt" | |
"log" | |
"os" | |
"strconv" | |
"strings" | |
"try-go/task" | |
) | |
func main() { | |
for _, str := range os.Args[1:] { | |
t := task.Wrap(strconv.Atoi(str)). | |
Map(func(n int) int { return n * 2 }). | |
AndThen(func(n int) *task.Task[int] { | |
if n == 0 { | |
return task.Err[int](fmt.Errorf("n is zero")) | |
} | |
return task.Ok(n) | |
}) | |
// it's a pity that when we need to change data type, we break out of our Method chain | |
// and into a standalone function | |
st := task.Map(t, func(n int) string { return strings.Repeat("x", n) }) | |
// unwrap will give us back the idiomatic `data, err` | |
str, err := st.Unwrap() | |
log.Printf("str: %#v, err: %#v", str, err) | |
// Switch is the preferred way to handle the success and failure scenarios | |
st.Switch(task.Scenarios[string]{ | |
Ok: func(data string) { | |
log.Printf("[switch] data: %#v", data) | |
}, | |
Err: func(err error) { | |
log.Printf("[switch] err: %#v", err) | |
}, | |
}) | |
} | |
} |
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
$ go run main.go 1 2 hello | |
2023/11/24 09:05:56 str: "xx", err: <nil> | |
2023/11/24 09:05:56 [switch] data: "xx" | |
2023/11/24 09:05:56 str: "xxxx", err: <nil> | |
2023/11/24 09:05:56 [switch] data: "xxxx" | |
2023/11/24 09:05:56 str: "", err: &strconv.NumError{Func:"Atoi", Num:"hello", Err:(*errors.errorString)(0x100e0d870)} | |
2023/11/24 09:05:56 [switch] err: &strconv.NumError{Func:"Atoi", Num:"hello", Err:(*errors.errorString)(0x100e0d870)} |
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 task | |
type Task[T interface{}] struct { | |
data T | |
err error | |
} | |
func Ok[T interface{}](data T) *Task[T] { | |
return &Task[T]{data, nil} | |
} | |
func Err[T interface{}](err error) *Task[T] { | |
return &Task[T]{err: err} | |
} | |
func Wrap[T interface{}](data T, err error) *Task[T] { | |
return &Task[T]{data, err} | |
} | |
// Unwrap method returns the data and error, like Go idiomatic functions | |
// but we really should strive to use Switch method instead | |
func (t *Task[T]) Unwrap() (T, error) { | |
return t.data, t.err | |
} | |
type Scenarios[T interface{}] struct { | |
Ok func(data T) | |
Err func(err error) | |
} | |
// Switch method allows us to unambiguously handle the mutually exclusive | |
// scenarios of success and failure | |
func (t *Task[T]) Switch(s Scenarios[T]) { | |
if t.err != nil { | |
s.Err(t.err) | |
} else { | |
s.Ok(t.data) | |
} | |
} | |
// AndThen method returns an error if the task is already in error state | |
// Otherwise, pass the data to the callback to change our _task_ | |
func (t *Task[T]) AndThen(callback func(data T) *Task[T]) *Task[T] { | |
if t.err != nil { | |
return t | |
} | |
return callback(t.data) | |
} | |
// AndThen function is like AndThen _method_ but lets you change the type of the data | |
func AndThen[T, Y any](t *Task[T], callback func(data T) *Task[Y]) *Task[Y] { | |
if t.err != nil { | |
return &Task[Y]{err: t.err} | |
} | |
return callback(t.data) | |
} | |
// Map method returns an error if the task is already in error state | |
// Otherwise, pass the data to the callback to change our _data_ | |
func (t *Task[T]) Map(callback func(data T) T) *Task[T] { | |
if t.err != nil { | |
return t | |
} | |
return &Task[T]{callback(t.data), nil} | |
} | |
// Map function is like Map _method_ but lets you change the type of the data | |
func Map[T, Y any](t *Task[T], callback func(data T) Y) *Task[Y] { | |
if t.err != nil { | |
return &Task[Y]{err: t.err} | |
} | |
return &Task[Y]{data: callback(t.data), err: nil} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment