Diciamo che vogliamo implementare seguente calcolo:funzione di annidamento chiama GO
outval/err = f3(f3(f1(inval))
dove ognuno di f1
, f2
, f3
può fallire con un errore in quel momento ci fermiamo il calcolo e impostare err
all'errore restituito dalla funzione in errore. (Naturalmente, nidificazione può essere arbitrariamente lungo)
in linguaggi come C++/Java/C# può essere facilmente fatto avendo f1
, f2
e f3
gettare un'eccezione e che racchiude il calcolo in un blocco try-catch, mentre in lingue come Haskell possiamo invece usare le monadi.
Ora sto cercando di implementarlo in GO e l'unico approccio a cui riesco a pensare è la scala if if else che è piuttosto prolissa. Non ho problemi se non siamo in grado di nidificare le chiamate, ma a mio parere aggiungendo un controllo degli errori dopo ogni riga nel codice sembra brutto e si interrompe il flusso. Mi piacerebbe sapere se c'è un modo migliore per farlo.
Modifica: Modifica secondo il commento di peterSO
Di seguito si riporta l'esempio concreto e l'attuazione immediata
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 calc(in int) (out int, err error) {
var temp1, temp2 int
temp1, err = f1(in)
if err != nil {
return temp1, err
}
temp2, err = f2(temp1)
if err != nil {
return temp2, err
}
return f3(temp2)
}
func main() {
inval := 0
outval, err := calc3(inval)
fmt.Println(inval, outval, err)
}
Quello che sto cercando di illustrare è, funzione Calc fa alcune calcolo, eventualmente con l'ausilio di funzioni di libreria che può fallire e la semantica è se qualche chiamata fallisce calc fa propagare l'errore al chiamante (simile a non gestire l'eccezione). Secondo me, il codice per calc è brutto.
Tra per questo caso particolare in cui tutte le funzioni di libreria hanno esattamente stessa firma, siamo in grado di rendere il codice migliore (sto usando idea da http://golang.org/doc/articles/wiki/#tmp_269)
func saferun(f func (int) (int, error)) func (int, error) (int, error) {
return func (in int, err error) (int, error) {
if err != nil {
return in, err
}
return f(in)
}
}
Poi possiamo ridefinire calc come
func calc(in int) (out int, err error) {
return saferun(f3)(saferun(f2)(f1(in)))
}
o come
func calc(in int) (out int, err error) {
sf2 := saferun(f2)
sf3 := saferun(f3)
return sf3(sf2(f1(in)))
}
Ma senza generici s Tuttavia, non sono sicuro di come posso utilizzare questo approccio per qualsiasi set di funzioni di libreria.
Grazie per la risposta, ma non sono sicuro di aver capito il tuo approccio. Cosa succede se non controllo la firma di f1, f2, f3, ecc. Perché sono funzioni di libreria.A mio modesto parere, l'errore nella gestione delle infrastrutture in altre lingue non richiede che tu abbia un tale controllo, – Suyog
Suyog, citando dalla tua domanda originale, "... fatto avendo f1, f2 e f3 lanciare un'eccezione ...." sembra che tu permetta di modificare f1, f2 e f3 per generare eccezioni. Avere il panico non è diverso. Il panico è il meccanismo disponibile in Vai per separare lo stack da una profondità arbitraria, restituendo un valore nel processo. Non è il modo più idiomatico e preferito per gestire gli errori in Go, ma è il meccanismo che farà ciò che chiedi. – Sonia
Intendevo che f1, f2, f3 sono già definiti per generare un'eccezione. Scusa per una formulazione errata. In linguaggio come JAVA, molte funzioni di libreria sono definite per generare un'eccezione, mentre in GO sembra che lo schema sia che le funzioni di libreria restituiscano un errore. Quindi il problema che sto affrontando è che devo verificare immediatamente gli errori che si traducono nella scrittura di codice ripetitivo. – Suyog