golang 函数式编程库samber/mo使用: Either
如果您不了解samber/mo库, 请先阅读第一篇 Option
结构定义
有时候我们不确定值的类型, 一个值可能是int, 也可能是string, 这时候我们可以使用Either类型。 Either类型是一种表示两种可能值的类型, 和python中的 Optional类似。 结构定义如下:
type Either[L any, R any] struct {
isLeft bool
left L
right 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 main
import (
"errors"
"fmt"
)
var (
ErrRedisNotFound = errors.New("redis not found")
ErrDBNotFound = errors.New("db not found")
)
func readFromRedis() (string, error) {
// let's simulate a failed operation
return "", ErrRedisNotFound
}
func readFromDB() (string, error) {
// let's simulate a successful operation
return "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 main
import (
"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 operation
return mo.Right[string, error](ErrRedisNotFound)
}
func readFromDB() mo.Either[string, error] {
// let's simulate a success operation
return 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)
},
)
},
)
}
代码虽然更长了,但是逻辑结构很清晰