golang 函数式编程库samber/mo使用: Either
如果您不了解samber/mo库, 请先阅读第一篇 Option
结构定义
有时候我们不确定值的类型, 一个值可能是int, 也可能是string, 这时候我们可以使用Either类型。 Either类型是一种表示两种可能值的类型, 和python中的 Optional类似。 结构定义如下:
type Either[L any, R any] struct {isLeft boolleft Lright R
}
其中 isLeft表示值的类型, left和right分别表示两种可能的值。 如果isLeft为true, 则left有值, right为nil; 如果isLeft为false, 则right有值, left为nil。
构造函数
主要有一下两个:
-
mo.Left()
函数定义为func Left[L any, R any](value L) Either[L, R]
L, R分别表示两种可能的值的类型, value表示左值。 doc -
mo.Right()
和Left类似 doc
使用示例
Either类型最常见的用法是处理错误, 正好适用于go语言。 因为go语言没有提供 try...catch
语法, 优点是错误显式处理,可以避免忘记捕获异常, 缺点是代码不够优雅。
举个例子, 如果我们用go自带的error处理, 代码如下:
package mainimport ("errors""fmt"
)var (ErrRedisNotFound = errors.New("redis not found")ErrDBNotFound = errors.New("db not found")
)func readFromRedis() (string, error) {// let's simulate a failed operationreturn "", ErrRedisNotFound
}func readFromDB() (string, error) {// let's simulate a successful operationreturn "user:1:Samber", nil
}func main() {data, err := readFromRedis()if err != nil {fmt.Println("redis not found, read from db")data, err = readFromDB()if err != nil {fmt.Println("db not found")} else {fmt.Println("data from db is:", data)}} else {fmt.Println("data from redis", data)}
}
可以看到里面充满了if else, 代码中的逻辑结构表达得不够清晰。 使用Either处理错误的代码如下:
package mainimport ("errors""fmt""github.com/samber/mo"
)var (ErrRedisNotFound = errors.New("redis not found")ErrDBNotFound = errors.New("db not found")
)func readFromRedis() mo.Either[string, error] {// let's simulate a failed operationreturn mo.Right[string, error](ErrRedisNotFound)
}func readFromDB() mo.Either[string, error] {// let's simulate a success operationreturn mo.Left[string, error]("user:1:Samber")
}func main() {readFromRedis().Match(func(data string) mo.Either[string, error] {fmt.Println("data from redis", data)return mo.Left[string, error](data)},func(err error) mo.Either[string, error] {fmt.Println("redis not found, read from db")return readFromDB().Match(func(data string) mo.Either[string, error] {fmt.Println("data from db is:", data)return mo.Left[string, error](data)},func(err error) mo.Either[string, error] {fmt.Println("db not found")return mo.Right[string, error](err)},)},)
}
代码虽然更长了,但是逻辑结构很清晰