Maison >développement back-end >Golang >Présentation de Do Notation dans le package Mo pour Golang
La notation Do est un sucre syntaxique principalement utilisé dans les langages de programmation fonctionnels comme Haskell et Scala. Il simplifie l'enchaînement des opérations monadiques, rendant le code plus lisible et maintenable. En apportant cette fonctionnalité à Go, nous pouvons désormais écrire du code plus propre et plus expressif lorsque nous travaillons avec des monades.
Lorsqu'il s'agit de monades, en particulier dans une logique métier complexe, le chaînage des opérations peut devenir fastidieux. La gestion des erreurs et la gestion des différents états conduisent souvent à des structures profondément imbriquées et difficiles à suivre. La notation Do résout ce problème en nous permettant d'écrire des opérations monadiques dans un style séquentiel, semblable à la programmation impérative, mais avec tous les avantages de la programmation fonctionnelle.
En Go, l'implémentation de la notation do n'était pas simple, mais j'ai réussi à y parvenir en utilisant la fonction Do. Voici un aperçu rapide de la façon dont vous pouvez l'utiliser avec un exemple :
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()) } }
Dans cet exemple, bookRoom utilise la fonction Do pour effectuer séquentiellement plusieurs opérations : valider les paramètres de réservation, créer une réservation et attribuer une salle. Chaque étape renvoie un résultat qui peut être enchaîné de manière transparente à l'aide de la fonction Do, garantissant une gestion des erreurs propre et lisible.
Sans Do-Notation
Vous pouvez avoir deux options :
1. Utilisation de bind (si implémenté) :
L'opération de « liaison » dans les monades peut ressembler à l'enfer des rappels lorsqu'il existe de nombreuses opérations monadiques en raison de la nature imbriquée et séquentielle de ces opérations. Lorsque de nombreuses opérations de ce type sont enchaînées, le code peut devenir profondément imbriqué et plus difficile à lire, de la même manière que les rappels peuvent être profondément imbriqués dans la programmation asynchrone. Si bind était implémenté dans le package Mo, son utilisation dans cet exemple ressemblerait à ceci :
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}) }) }) }) }
Cette approche devient vite difficile à lire et à maintenir.
2. Utilisation de .Get() :
Une autre option consiste à utiliser .Get() sur la monade pour déballer la monade et obtenir la valeur et l'erreur sous-jacentes. Cela ressemble à du code Go typique, mais la gestion des erreurs peut être verbeuse :
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}) }
Cette approche est plus lisible que l'utilisation de bind, mais implique toujours beaucoup de gestion des erreurs passe-partout.
Avec Do-Notation
Avec la notation do, vous pouvez appeler .MustGet() sur la monade pour obtenir la valeur sous-jacente directement sans erreur. Cette fonction (MustGet()) paniquera si la monade a une erreur ; cependant, la notation do gérera cela et court-circuitera l'exécution s'il y a une erreur ou renverra la valeur non enveloppée :
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} }) }
Cette approche est claire, concise et facile à lire, réduisant considérablement le code de gestion des erreurs passe-partout.
L'un des grands avantages de l'utilisation de la notation do est que vous n'avez pas à vérifier les erreurs après chaque opération monadique. Même si une monade peut avoir un type d'erreur, la notation do gérera automatiquement la propagation des erreurs et court-circuitera l'exécution si une erreur se produit. Cela conduit à un code plus propre et plus maintenable, ce qui est particulièrement précieux dans les flux de travail complexes.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!