10
votes

Fonction de nidification appelle à Go

laissez-nous dire que nous voulons mettre en œuvre le calcul suivant:

woval / err = f3 (f3 (f1 (p) code> p>

où chacun des F1 code>, f2 code>, F3 code> peut échouer avec une erreur à ce moment-là, nous arrêtons le calcul et définir err code> sur une erreur renvoyée par l'échec Fonction. (Bien sûr, la nidification peut être arbitrairement longue) p>

dans des langues telles que C ++ / Java / C #, il peut être facilement effectué en ayant F1 code>, F2 code > et f3 code> jetez une exception et joignez le calcul dans un bloc de try-catch, tandis que dans des langues comme HASKELL, nous pouvons utiliser des monads à la place. P>

Maintenant, j'essaie de le mettre en œuvre Aller et la seule approche que je peux penser est évidente si, d'autre échelle qui est plutôt verbeuse. Je n'ai pas de problème si nous ne pouvons pas nidifier les appels, mais à mon avis, ajouter une vérification d'erreur après chaque ligne dans le code a l'air laid et Il enfreint le flux. J'aimerais savoir s'il y a une meilleure façon de le faire. P>

Edit: Modification selon le commentaire de Peterso

Vous trouverez ci-dessous l'exemple concret et la mise en oeuvre droite p> xxx pré>

Ce que j'essaie d'illustrer est, la fonction CALC a-t-elle un certain calcul avec l'aide de fonctions de bibliothèque pouvant échouer et que la sémantique est si Tout appel échoue Calc propage de l'erreur à l'appelant (semblable à ne pas manipuler l'exception). À mon avis, le code de Calc est laid. P>

entre ce cas particulier dans lequel toutes les fonctions de la bibliothèque ont exactement la même signature, nous pouvons rendre le code mieux (j'utilise une idée de http://golang.org/doc/articles/wiki/#Tmp_269 ) p>

func calc(in int) (out int, err error) {
    sf2 := saferun(f2)
    sf3 := saferun(f3)
    return sf3(sf2(f1(in)))
}

go

0 commentaires

6 Réponses :


6
votes

La discussion entre les erreurs vs exceptions est longue et fastidieuse. Je ne vais donc pas y entrer.

La réponse la plus simple à votre question concerne votre intégré intégré , panique et récupérer Fonctions Comme indiqué dans Ce blog poste. Ils peuvent offrir un comportement similaire à des exceptions. xxx


3 commentaires

Merci d'avoir répondu, mais je ne suis pas sûr de comprendre votre approche. Et si je ne contrôlais pas la signature de F1, F2, F3, etc. Dites parce qu'ils sont des fonctions de bibliothèque. Dans mon avis d'opinion modeste, les infrastructures d'erreur d'erreur dans d'autres langues ne nécessitent pas que vous ayez un tel contrôle


SUYOG, citant de votre question originale, "... FAIT en prenant une exception F1, F2 et F3 ..." Il semble que vous ayez convaincu que vous permettant de changer de F1, F2 et F3 pour lancer des exceptions. Les avoir panique n'est pas différent. La panique est le mécanisme disponible en déplacement pour dérouler la pile de profondeur arbitraire, renvoyant une valeur dans le processus. Ce n'est pas le moyen idiomatique et préféré de gérer les erreurs en déplacement, mais c'est le mécanisme qui fera ce que vous demandez.


Je voulais dire F1, F2, F3 est déjà défini pour lancer une exception. Désolé pour une mauvaise formulation. Dans la langue comme Java, de nombreuses fonctions de bibliothèque sont définies pour lancer une exception, tandis qu'en aller, il semble que les fonctions de la bibliothèque renvoient une erreur. Donc, le problème que je suis confronté est que je dois vérifier les erreurs immédiatement, ce qui entraîne la rédaction de code répétitif.



-1
votes

Sans exemple concret, vous inclinez des moulins à vent. Par exemple, par votre définition, les fonctions FN renvoient une valeur et une erreur. Les fonctions FN sont des fonctions d'emballage dont la signature ne peut pas être modifiée. Utilisation de votre exemple,

package main

import "fmt"

func f1(in int) (out int, err error) {
    return in + 1, err
}

func f2(in int) (out int, err error) {
    return in + 2, err
}

func f3(in int) (out int, err error) {
    return in + 3, err
}

func main() {
    inval := 0
    outval, err := f3(f2(f1(inval)))
    fmt.Println(inval, outval, err)
}


0 commentaires

7
votes

Si vous voulez vraiment pouvoir faire cela, vous pouvez utiliser une fonction de composition.

func compose(fs ...func(Value) (OutVal, error)) func(Value) (OutVal, error) {
  return func(val Value) OutVal, Error {
    sVal := val
    var err error
    for _, f := range fs {
      sval, err = f(val)
      if err != nil {
        // bail here and return the val
        return nil, err
      }
    }
    return sval, nil
  }
}

outVal, err := compose(f1, f2)(inVal)


0 commentaires

7
votes

Premièrement, une version élargie du style try-capte que vous utilisez, empruntant évidemment de la réponse de Jimt et de la réponse de Peterso.

package main

import "fmt"

// Some dummy library functions with different signatures.
// Per idiomatic Go, they return error values if they have a problem.
func f1(in string) (out int, err error) {
    return len(in), err
}

func f2(in int) (out int, err error) {
    return in + 1, err
}

func f3(in int) (out float64, err error) {
    return float64(in) + .5, err
}

func main() {
    inval := "one"

    // calc3 three is the function you want to call that does a computation
    // involving f1, f2, and f3 and returns any error that crops up.
    outval, err := calc3(inval)

    fmt.Println("inval: ", inval)
    fmt.Println("outval:", outval)
    fmt.Println("err:   ", err)
}

func calc3(in string) (out float64, err error) {
    // Ignore the following big comment and the deferred function for a moment,
    // skip to the comment on the return statement, the last line of calc3...
    defer func() {
        // After viewing what the fXp function do, this function can make
        // sense.  As a deferred function it runs whenever calc3 returns--
        // whether a panic has happened or not.
        //
        // It first calls recover.  If no panic has happened, recover returns
        // nil and calc3 is allowed to return normally.
        //
        // Otherwise it does a type assertion (the value.(type) syntax)
        // to make sure that x is of type error and to get the actual error
        // value.
        //
        // It does a tricky thing then. The deferred function, being a
        // function literal, is a closure.  Specifically, it has access to
        // calc3's return value "err" and can force calc3 to return an error.
        // A line simply saying  "err = xErr" would be enough, but we can
        // do better by annotating the error (something specific from f1,
        // f2, or f3) with the context in which it occurred (calc3).
        // It allows calc3 to return then, with this descriptive error.
        //
        // If x is somehow non-nil and yet not an error value that we are
        // expecting, we re-panic with this value, effectively passing it on
        // to allow a higer level function to catch it.
        if x := recover(); x != nil {
            if xErr, ok := x.(error); ok {
                err = fmt.Errorf("calc3 error: %v", xErr)
                return
            }
            panic(x)
        }
    }()
    // ... this is the way you want to write your code, without "breaking
    // the flow."
    return f3p(f2p(f1p(in))), nil
}

// So, notice that we wrote the computation in calc3 not with the original
// fX functions, but with fXp functions.  These are wrappers that catch
// any error and panic, removing the error from the function signature.
// Yes, you must write a wrapper for each library function you want to call.
// It's pretty easy though:
func f1p(in string) int {
    v, err := f1(in)
    if err != nil {
        panic(err)
    }
    return v
}

func f2p(in int) int {
    v, err := f2(in)
    if err != nil {
        panic(err)
    }
    return v
}

func f3p(in int) float64 {
    v, err := f3(in)
    if err != nil {
        panic(err)
    }
    return v
}
// Now that you've seen the wrappers that panic rather than returning errors,
// go back and look at the big comment in the deferred function in calc3.


1 commentaires

Merci beaucoup pour une réponse étendue!



0
votes

trouvé mailing thread sur les noix de passe pour ce sujet. L'ajout à la référence.


0 commentaires

-1
votes

Dommage que celui-ci soit déjà fermé ... Ceci:

c.funA().funB().funC()


0 commentaires