20130905

La terrible exclamación, o, resultados inesperados de métodos con ! en Ruby

Ésta es una traducción del artículo «The Big Bad Bang, or, The OTHER Gotcha with Ruby's Bang Methods», escrito originalmente por David Aronson en Attack of the Codosaurus!, y es publicado bajo su permiso.


     Algunas personas me han preguntado el por qué ciertos métodos de Ruby terminan con un signo de exclamación (!), comúnmente conocido como bang en los círculos de programación. Los ejemplos incluyen upcase! (para obtener una versión completamente en mayúsculas de una cadena de caracteres) y uniq! (para obtener los elementos únicos dentro de un arreglo). Para acortar la explicación, el bang significa que debes usar ese método con precaución.

     Esto es debido a que usualmente el método modifica el objeto que recibe y luego lo devuelve (proceso destructivo), en lugar de devolver sólo una copia modificada del objeto. (En Rails y otros frameworks similares, puede indicar que el método arrojará una excepción si algo sale mal. Pero, nos enfocaremos en los métodos básicos de Ruby.) Les mostraré otra razón en un momento, pero por ahora, vamos a examinar el comportamiento usual y esperado.

     Por ejemplo:


cadena = 'foo'
p cadena.upcase # > FOO
p cadena        # > foo

     Obtenemos FOO y después foo. Cuando upcase devolvió la versión en mayúsculas, no modificó la cadena.

     Por otro lado, si agregamos un bang, así:


cadena = 'foo'
p cadena.upcase! # > FOO
p cadena         # > FOO

     ¡Obtenemos FOO y otra vez FOO! En otras palabras, upcase! devolvió la versión en mayúsculas, justo como la versión sin bang, ¡pero también modifico la cadena!

     De forma similar, si usamos uniq:


arreglo = [1, 3, 3, 7]
p arreglo.uniq # > [1, 3, 7]
p arreglo      # > [1, 3, 3, 7]

     Obtenemos [1, 3, 7] y [1, 3, 3, 7], mostrando que la versión sin bang devolvió los valores únicos dentro del arreglo, pero no lo modificó, mientras que si agregamos un bang:


arreglo = [1, 3, 3, 7]
p arreglo.uniq! # > [1, 3, 7]
p arreglo       # > [1, 3, 7]

     Obtenemos [1, 3, 7] y [1, 3, 7] de nuevo, mostrando que el arreglo fue modificado ésta vez.

     Hasta ahora todo bien. ¡Pero espera! ¡Aún hay más! Hay un comportamiento inesperado aguardando ser encontrado...

No esperes que las versiones con bang de los métodos devuelvan el mismo valor que las versiones sin bang. (Aunque ese valor parezca ser la razón de ser de ambas funciones.)

     En los casos antes mencionados, ese es el punto. Pero observemos lo que pasa si la variable ya tiene el valor que necesitamos –en otras palabras, si la cadena ya está en mayúsculas, o el arreglo ya cuenta sólo con valores únicos.

     Si usamos:


cadena = 'FOO'
p cadena.upcase! # > nil
p cadena         # > FOO

     Como esperamos de la cadena, al ya contener el valor que necesitamos, no fue modificada. Pero observa el valor devuelto por cadena.upcase! –¡es nil!

     Veamos que pasa en el caso numérico:


arreglo = [1, 3, 7]
p arreglo.uniq! # > nil
p arreglo       # > [1, 3, 7]

     Justo como en el caso anterior, el arreglo no sufrió cambios, ¡pero el valor devuelto por arreglo.uniq! es nil! ¿Cómo es eso posible?

     De forma resumida, los métodos básicos en Ruby que incluyen el bang, con frecuencia devuelven nil si no se realizó ningún cambio. El problema es que esto no pasa de una forma consistente, por lo tanto, cuando utilices un método que incluye bang con el que no estés familiarizado, asegurate de leer el manual.
Publicar un comentario