Digamos que queremos implementar cálculo siguiente:Función de inserción llama en GO
outval/err = f3(f3(f1(inval))
donde cada uno de f1
, f2
, f3
puede fallar con un error en ese momento nos detenemos el cálculo y ajuste err
al error devuelto por la función de falla. (Por supuesto, la anidación puede ser arbitrariamente larga)
En lenguajes como C++/Java/C# que se puede hacer fácilmente por tener f1
, f2
y f3
lanzar una excepción y que encierra el cálculo en un bloque try-catch, mientras que en idiomas como Haskell podemos usar mónadas en su lugar.
Ahora estoy tratando de implementarlo en GO y el único enfoque que puedo pensar es obvio if-else ladder que es bastante detallado. No tengo problemas si no podemos anidar las llamadas, pero en mi opinión agregar una verificación de error después de cada línea en el código se ve feo y rompe el flujo. Me gustaría saber si hay alguna forma mejor de hacerlo.
edición: Edición de acuerdo con el comentario de peterSO
A continuación se muestra el ejemplo de aplicación concreta y directa
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)
}
Lo que estoy tratando de ilustrar es, la función Calc hace algún cálculo, posiblemente con la ayuda de las funciones de biblioteca que puede fallar y la semántica es que si falla una llamada, calc propaga el error a la persona que llama (similar a no manejar la excepción). En mi opinión, el código para calc es feo.
Entre para este caso particular en que todas las funciones de la biblioteca tienen exactamente misma firma, podemos hacer que el código mejor (estoy usando la idea de 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)
}
}
entonces podemos redefinir Calc como
func calc(in int) (out int, err error) {
return saferun(f3)(saferun(f2)(f1(in)))
}
o como
func calc(in int) (out int, err error) {
sf2 := saferun(f2)
sf3 := saferun(f3)
return sf3(sf2(f1(in)))
}
Pero sin genéricos s upport, no estoy seguro de cómo puedo utilizar este enfoque para cualquier conjunto de funciones de la biblioteca.
Gracias por responder, pero no estoy seguro de entender su enfoque. ¿Qué ocurre si no controlo la firma de f1, f2, f3, etc., porque son funciones de la biblioteca?En mi opinión modesta, el manejo incorrecto de las infraestructuras en otros idiomas no requiere que tenga dicho control – Suyog
Suyog, citando su pregunta original, "... hecho al tener f1, f2 y f3 arrojar una excepción ..." Seguro suena como si permitieras cambiar f1, f2 y f3 para lanzar excepciones. Tenerlos en pánico no es diferente. El pánico es el mecanismo disponible en Ir para desenrollar la pila de una profundidad arbitraria, devolviendo un valor en el proceso. No es la forma idiomática y preferida de manejar los errores en Go, pero es el mecanismo que hará lo que usted pida. – Sonia
Quise decir que f1, f2, f3 ya están definidos para lanzar una excepción. Perdón por malas palabras. En lenguaje como JAVA, muchas funciones de la biblioteca están definidas para arrojar una excepción, mientras que en el patrón GO parece que las funciones de la biblioteca devuelven un error. Por lo tanto, el problema al que me enfrento es que debo verificar los errores de inmediato, lo que da como resultado la escritura de código repetitivo. – Suyog