A notação Do é um açúcar sintático usado principalmente em linguagens de programação funcionais como Haskell e Scala. Simplifica o encadeamento de operações monádicas, tornando o código mais legível e de fácil manutenção. Ao trazer esse recurso para Go, agora podemos escrever código mais limpo e expressivo ao trabalhar com mônadas.
Ao lidar com mônadas, especialmente em lógicas de negócios complexas, as operações de encadeamento podem se tornar complicadas. O tratamento de erros e o gerenciamento de diferentes estados geralmente levam a estruturas profundamente aninhadas que são difíceis de seguir. A notação Do resolve isso, permitindo-nos escrever operações monádicas em um estilo sequencial, semelhante à programação imperativa, mas com todos os benefícios da programação funcional.
Em Go, implementar a notação do não foi simples, mas consegui fazê-lo usando a função Do. Aqui está uma rápida olhada em como você pode usá-lo com um exemplo:
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()) } }
Neste exemplo, bookRoom usa a função Do para realizar diversas operações sequencialmente: validar parâmetros de reserva, criar uma reserva e atribuir um quarto. Cada etapa retorna um resultado que pode ser perfeitamente encadeado usando a função Do, garantindo um tratamento de erros limpo e legível.
Sem notação de execução
Você pode ter duas opções:
1. Usando bind (se implementado):
A operação "bind" em mônadas pode se assemelhar ao inferno de retorno de chamada quando há muitas operações monádicas devido à natureza aninhada e sequencial dessas operações. Quando muitas dessas operações são encadeadas, o código pode ficar profundamente aninhado e mais difícil de ler, semelhante à profundidade dos retornos de chamada na programação assíncrona. Se o bind fosse implementado no pacote Mo, usá-lo neste exemplo seria algo assim:
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}) }) }) }) }
Essa abordagem rapidamente se torna difícil de ler e manter.
2. Usando .Get():
Outra opção é usar .Get() na mônada para desembrulhar a mônada e obter o valor e o erro subjacentes. Parece um código Go típico, mas o tratamento de erros pode ser detalhado:
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}) }
Essa abordagem é mais legível do que usar bind, mas ainda envolve muito tratamento de erros padrão.
Com notação Do
Com a notação do, você pode chamar .MustGet() na mônada para obter o valor subjacente diretamente sem erros. Esta função (MustGet()) entrará em pânico se a mônada apresentar um erro; no entanto, a notação irá lidar com isso e causar um curto-circuito na execução se houver um erro ou retornar o valor desembrulhado:
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} }) }
Essa abordagem é limpa, concisa e fácil de ler, reduzindo significativamente o código padrão de tratamento de erros.
Uma das grandes vantagens de usar a notação do é que você não precisa verificar se há erros após cada operação monádica. Mesmo que uma mônada possa ter um tipo de erro, a notação tratará automaticamente a propagação do erro e causará um curto-circuito na execução se ocorrer um erro. Isso leva a um código mais limpo e de fácil manutenção, o que é particularmente valioso em fluxos de trabalho complexos.
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3