Patrones de función

Completado

Hasta ahora, hemos tomado expresiones repetitivas y las hemos colocado en funciones. Ahora, el código se ve mejor y es más fácil de leer. A medida que se acostumbre más al uso de funciones, merece la pena empezar a ver algunos patrones eficaces que existen en este espacio. Mediante el uso de estos patrones, obtiene un código que es más fácil de leer y mantener.

Declarativo frente a imperativo

Al empezar a escribir código, lo más habitual es escribir una expresión. Luego, escribiremos otra expresión, seguida de otra, una después de la otra. Nuestro cometido es resolver el problema y estamos siendo específicos sobre cómo resolverlo. Este método se conoce como método imperativo, y no tiene nada de malo, ya que resuelve el problema en cuestión. Pero se puede tomar otro camino, conocido como método declarativo. Un ejemplo de método declarativo es consultar una base de datos mediante SQL.

Esta es una expresión de ejemplo:

SELECT * 
FROM Students s
WHERE s.Location = "Ohio" 

Lo que hace que este código sea declarativo es que se pide lo que se quiere, pero no se especifica cómo. El cómo es cosa de SQL.

Este método también se puede usar con F#. El siguiente código emplea un método declarativo:

let studentsFromOhio = 
    allStudents  
    |> filterLocation "Ohio"

En el código de arriba, trabajamos con datos y pedimos lo que queremos sin especificar cómo queremos que se haga. Cuando el código es similar a este de aquí, es fácil de leer y deducir. Para llegar a este punto, echemos un vistazo a algunos patrones útiles que se admiten en F#.

Patrones funcionales

Hay algunos patrones útiles en F# que nos permiten adoptar una postura más funcional. Nos centraremos en los siguientes patrones:

  • Composición: una composición combina varias funciones en una sola.
  • Canalización: una canalización comienza con un valor y, luego, llama secuencialmente a muchas funciones usando la salida de una función como entrada de la siguiente.

Composición

La composición trata de combinar funciones y aplicarlas, una después de otra, en un orden determinado. El operador de composición toma dos funciones y devuelve una nueva.

Al crear código, a menudo nos encontraremos llamando a una función, y luego a otra justo después de ella. Imaginemos, por ejemplo, que queremos ordenar una lista y filtrar todos los productos con descuento.

En el siguiente ejemplo, se llama a la función add2() y su resultado se introduce en la función multiply3().

let add2 a = a + 2
let multiply3 a = a * 3 
let addAndMultiply a =
    let sum = add2 a
    let product = multiply3 sum
    product

printfn "%i" (addAndMultiply 2) // 12

Este patrón es tan común que F# tiene un operador propio. El operador >> permite combinar dos o más funciones en una función mayor. Si usamos el operador >>, podemos simplificar el código anterior, como se muestra aquí:

let add2 a = a + 2
let multiply3 a = a * 3 
let addAndMultiply = add2 >> multiply3

printfn "%i" (addAndMultiply 2) // 12

La función combinada addAndMultiply() aplica las funciones que la componen de izquierda a derecha. En este ejemplo, add2() sucede en primer lugar y multiply3(), en último.

Canalización

El operador de canalización |> toma una función y un argumento y devuelve un valor. Veamos cómo la canalización difiere de la composición con este ejemplo:

let list = [4; 3; 1]
let sort (list: int list) = List.sort list
let print (list: int list)= List.iter(fun x-> printfn "item %i" x) list

list |> sort |> print // item 1 item 3 item 4

En la última línea de código, se empieza con una lista de enteros, list, que sirve de entrada para la primera función, sort(). El resultado de esa operación se introduce en print(). La principal diferencia entre canalización y composición es que, en la canalización, se empieza con algunos datos (una lista de enteros, en nuestro caso) y, luego, se llevan por un conjunto de funciones.