Explorar funciones

Completado

Muchas veces nos encontraremos en situaciones en las que nuestro código parece repetitivo; Está haciendo lo mismo en muchos lugares, o hay solo pequeñas diferencias. En situaciones como estas, debemos pensar en cómo hacer que el código sea reutilizable. Para afrontar a esta situación, hay que extraer estas expresiones y crear una o varias funciones. Una función es un bloque de creación fundamental en un sinfín de lenguajes de programación, y F# no es diferente en este sentido.

Una función.

Una función es una referencia con nombre que tiene un cuerpo que consta de una o varias expresiones. También puede tener parámetros. Una función comienza por la palabra clave let y el nombre de la función. Tras esto, hay un operador de asignación y a la derecha de este operador está el cuerpo de la función.

Esta es la sintaxis de una función:

let <function name> <parameters> = <function body>

Este podría ser un ejemplo de función real, como muestra la sintaxis del ejemplo anterior:

let add a b = a + b 

En este ejemplo, el nombre de la función es add y los parámetros son a y b. El cuerpo de la función suma los dos parámetros, a+b, y devuelve el resultado.

Valores devueltos

Como se aprecia en el primer ejemplo, no hay ninguna palabra clave return que indique lo que se devuelve. En F#, lo que se devuelve es la información de la última línea de una función. Consideremos esta función multilinea:

let addAndMultiply a b c = 
    let sum = a + b
    let product = sum * c
    product

Al poner product en la última línea, ello nos indica qué es lo que se devuelve. ¿Y cómo se llama a una función como esta?

Llamada a una función

Para llamar o invocar una función, hay que usar el nombre de la función y agregar los parámetros que queramos usar separados por un carácter de espacio. Por ejemplo, para llamar a la función addAndMultiply(), escribiremos el siguiente código:

addAndMultiply 2 3 3 // 15

Para ver los resultados de la función, podemos asignarle una variable o imprimirla directamente de la siguiente manera:

let sum = addAndMultiply 2 3 3
printfn "%i" sum
// OR
printfn "%i" (addAndMultiply 2 3 3)

Tipos deducidos

Vamos a tomar el código siguiente:

let add a b = a + b
let sum = add 2 2 
let concat = add "hello" "world" // will give compilation error

Este código genera un error en la última línea con el siguiente mensaje:

error FS0001: This expression was expected to have type
    'int'    
but here has type
    'string'    

El motivo del error es que F# ya ha decidido el tipo de parámetro que se debe usar, y ese tipo debe ser un entero. El motivo de decantarse por un entero se debe a la información de la segunda fila, let sum = add 2 2. La función add() se usó de forma tal que se constataba que el tipo de parámetro definitivamente era un entero.

Si la segunda fila no existiera, el código funcionaría:

let add a b = a + b
// let sum = add 2 2 
let concat = add "hello" "world" // will work

El compilador ve que el primer uso de la función se realiza con cadenas y concluye que el tipo de parámetro son cadenas. Por lo tanto, el contexto y el primer uso infieren el tipo.

Tipos explícitos

Hay situaciones en las que se quiere ser explícito, tanto sobre el tipo que adoptan los parámetros como sobre lo que devuelve la función. Ser específico ayuda a facilitar la lectura, aunque lo más probable es que el compilador no lo necesite.

Veamos la siguiente función, por ejemplo:

let convert a =
    int a

Para agregarle tipos, agregaremos un elemento :<type> del siguiente modo:

let convert (a:string) =
    int a

Ahora, el parámetro tiene el tipo string a través del código let convert (a:string). En este momento podemos ser más explícitos aún agregando un tipo de valor devuelto. Para ello, agregue otro elemento :<type> justo después del parámetro:

let convert (a:string) :int =
    int a

Ahora los paréntesis encierran el parámetro, tal como se muestra en (a:string). El tipo de valor devuelto es la última anotación :<type> que va a producirse, que es :int. F# es lo suficientemente inteligente como para descifrar la mayoría de los escenarios, pero a veces es necesario dar un empujón siendo más explícitos en el código. Otra ventaja de agregar tipos es que otros desarrolladores lo tendrán más fácil para averiguar lo que pasa.