Do 表示法是一种语法糖,主要用于 Haskell 和 Scala 等函数式编程语言。它简化了单子操作的链接,使代码更具可读性和可维护性。通过将此功能引入 Go,我们现在可以在使用 monad 时编写更清晰、更具表现力的代码。
在处理 monad 时,尤其是在复杂的业务逻辑中,链接操作可能会变得很麻烦。错误处理和管理不同的状态通常会导致难以理解的深层嵌套结构。 Do 表示法通过允许我们以顺序风格编写一元操作来解决这个问题,类似于命令式编程,但具有函数式编程的所有优点。
在 Go 中,实现 do 表示法并不简单,但我设法使用 Do 函数来实现它。以下是如何使用它的示例:
package main import ( "errors" "fmt" "github.com/samber/mo" ) func validateBooking(params map[string]string) mo.Result[map[string]string] { if params["guest"] != "" && params["roomType"] != "" { return mo.Ok(params) } return mo.Err[map[string]string](errors.New("validation failed")) } func createBooking(guest string) mo.Result[string] { if guest != "" { return mo.Ok("Booking Created for: " guest) } return mo.Err[string](errors.New("booking creation failed")) } func assignRoom(booking string, roomType string) mo.Result[string] { if roomType != "" { return mo.Ok("Room Assigned: " roomType " for " booking) } return mo.Err[string](errors.New("room assignment failed")) } // This could be a service package that performs the entire process func bookRoom(params map[string]string) mo.Result[[]string] { return mo.Do(func() []string { // Validate booking parameters values := validateBooking(params).MustGet() // Create booking booking := createBooking(values["guest"]).MustGet() // Assign room room := assignRoom(booking, values["roomType"]).MustGet() // Return success with booking and room details return []string{booking, room} }) } func main() { params := map[string]string{ "guest": "Foo", "roomType": "Suite", } result := bookRoom(params) if result.IsError() { fmt.Println("Error:", result.Error()) } else { fmt.Println("Success:", result.MustGet()) } }
在此示例中,bookRoom 使用 Do 函数顺序执行多个操作:验证预订参数、创建预订和分配房间。每个步骤都会返回一个结果,可以使用 Do 函数无缝链接该结果,确保干净且可读的错误处理。
没有 Do 符号
您可以有两个选择:
1。使用绑定(如果实现):
当存在许多单子操作时,由于这些操作的嵌套和顺序性质,单子中的“绑定”操作可能类似于回调地狱。当许多此类操作链接在一起时,代码可能会变得深度嵌套且难以阅读,类似于异步编程中回调的深度嵌套。如果绑定是在 Mo 包中实现的,则在本例中使用它看起来像这样:
func bookRoom(params map[string]string) mo.Result[[]string] { return bind(validateBooking(params), func(values map[string]string) mo.Result[[]string] { return bind(createBooking(values["guest"]), func(booking string) mo.Result[[]string] { return bind(assignRoom(booking, values["roomType"]), func(room string) mo.Result[[]string] { return mo.Ok([]string{booking, room}) }) }) }) }
这种方法很快就会变得难以阅读和维护。
2.使用 .Get():
另一种选择是在 monad 上使用 .Get() 来打开 monad 并获取底层值和错误。这看起来像典型的 Go 代码,但错误处理可能很冗长:
func bookRoom(params map[string]string) mo.Result[[]string] { values, err := validateBooking(params).Get() if err != nil { return mo.Err[[]string](err) } booking, err := createBooking(values["guest"]).Get() if err != nil { return mo.Err[[]string](err) } room, err := assignRoom(booking, values["roomType"]).Get() if err != nil { return mo.Err[[]string](err) } return mo.Ok([]string{booking, room}) }
这种方法比使用绑定更具可读性,但仍然涉及大量样板错误处理。
带有 Do 符号
使用 do 表示法,您可以在 monad 上调用 .MustGet() 来直接获取底层值,不会出现错误。如果 monad 出现错误,该函数(MustGet())将会出现错误;但是,如果出现错误, do 表示法将处理该问题并短路执行或返回未包装的值:
func bookRoom(params map[string]string) mo.Result[[]string] { return mo.Do(func() []string { values := validateBooking(params).MustGet() booking := createBooking(values["guest"]).MustGet() room := assignRoom(booking, values["roomType"]).MustGet() return []string{booking, room} }) }
这种方法干净、简洁且易于阅读,显着减少了样板错误处理代码。
使用 do 表示法的一大优点是您不必在每次单子操作后检查错误。即使 monad 可以有错误类型,如果发生错误,do 表示法将自动处理错误传播并短路执行。这会带来更干净、更易于维护的代码,这在复杂的工作流程中特别有价值。
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3