Pensamiento funcional Parte 8

Hola Habr! Regresamos un poco tarde de las vacaciones de Año Nuevo con la continuación de nuestra serie de artículos sobre programación funcional. Hoy hablaremos sobre la comprensión de las funciones a través de las firmas y la definición de sus propios tipos para las firmas de funciones. Detalles debajo del corte!




No es obvio, pero F # tiene dos sintaxis: para expresiones regulares (significativas) y para definiciones de tipo. Por ejemplo:


[1;2;3] //   int list //   Some 1 //   int option //   (1,"a") //   int * string //   

Las expresiones para tipos tienen una sintaxis especial que difiere de la sintaxis de las expresiones ordinarias. Es posible que haya notado muchos ejemplos de esta sintaxis mientras trabajaba con FSI (FSharp Interactive), como Los tipos de cada expresión se muestran junto con los resultados de su ejecución.


Como sabe, F # utiliza un algoritmo de inferencia de tipos, por lo que a menudo no necesita escribir tipos explícitamente en código, especialmente en funciones. Pero para trabajar de manera efectiva con F #, debe comprender la sintaxis de los tipos para poder definir sus propios tipos, errores de conversión de tipos de depuración y leer firmas de funciones. En este artículo, me enfocaré en el uso de tipos en firmas de funciones.


Estos son algunos ejemplos de firmas de sintaxis tipo:


 //   //   let add1 x = x + 1 // int -> int let add xy = x + y // int -> int -> int let print x = printf "%A" x // 'a -> unit System.Console.ReadLine // unit -> string List.sum // 'a list -> 'a List.filter // ('a -> bool) -> 'a list -> 'a list List.map // ('a -> 'b) -> 'a list -> 'b list 

Comprender las funciones a través de firmas


A menudo, incluso solo estudiando la firma de una función, puede hacerse una idea de lo que hace. Considere algunos ejemplos y analícelos a su vez.


 int -> int -> int 

Esta función toma dos parámetros int y devuelve otro int . Lo más probable es que sea un tipo de funciones matemáticas, como la suma, la resta, la multiplicación o la exponenciación.


 int -> unit 

Esta función toma una unit int y devuelve, lo que significa que la función hace algo importante como efecto secundario. Porque no devuelve un valor útil, el efecto secundario probablemente produce operaciones de escritura en IO, como el registro, la escritura en la base de datos o algo similar.


 unit -> string 

Esta función no acepta nada, pero devuelve una string , lo que puede significar que la función recibe una cadena desde el aire. Como no hay una entrada explícita, la función probablemente hace algo con la lectura (digamos de un archivo) o la generación (como una cadena aleatoria).


 int -> (unit -> string) 

Esta función toma un int y devuelve otra función que devolverá una cadena cuando se llame. De nuevo, probablemente, la función realiza una operación de lectura o generación. Es probable que la entrada de alguna manera inicialice la función de retorno. Por ejemplo, la entrada puede ser el identificador del archivo y la función devuelta es similar a readline() . O bien, la entrada puede ser el valor inicial para un generador de cadenas al azar. No podemos decir con certeza, pero podemos sacar algunas conclusiones.


 'a list -> 'a 

La función acepta una lista de cualquier tipo, pero solo devuelve un valor de este tipo. Esto puede indicar que la función agrega la lista o selecciona uno de sus elementos. Una firma similar es List.sum , List.max , List.head , etc.


 ('a -> bool) -> 'a list -> 'a list 

Esta función toma dos parámetros: el primero es una función que convierte algo en un bool (predicado), el segundo es una lista. El valor de retorno es una lista del mismo tipo. Los predicados se usan para determinar si un objeto cumple un cierto criterio y si esta función parece seleccionar elementos de una lista de acuerdo con un predicado, verdadero o falso. Después de eso, devuelve un subconjunto de la lista original. Un ejemplo de una función con esta firma es List.filter .


 ('a -> 'b) -> 'a list -> 'b list 

La función toma dos parámetros: conversión del tipo 'a al tipo 'b una lista del tipo 'a . El valor de retorno es una lista de tipo 'b . Es razonable suponer que la función toma cada elemento de la lista 'a , y lo convierte en 'b , usando la función pasada como primer parámetro, y luego devuelve la lista 'b . De hecho, List.map es el prototipo de una función con dicha firma.


Buscar métodos de biblioteca con firmas


Las firmas de funciones son muy importantes para encontrar funciones de biblioteca. Las bibliotecas F # contienen cientos de funciones, que pueden ser confusas al principio. A diferencia de los lenguajes orientados a objetos, no puede simplemente "ingresar un objeto" a través de un punto para encontrar todos los métodos relacionados. Pero si conoce la firma de la función deseada, puede limitar rápidamente su búsqueda.


Por ejemplo, tiene dos listas y desea encontrar una función que las combine en una. ¿Qué firma tendría la función deseada? Tendría que tomar dos listas como parámetros y devolver una tercera, todas del mismo tipo:


 'a list -> 'a list -> 'a list 

Ahora vaya al sitio de documentación de MSDN para el módulo Lista y busque una función similar. Resulta que solo hay una función con dicha firma:


 append : 'T list -> 'T list -> 'T list 

Lo que necesitas!


Definición de tipos nativos para firmas de funciones


Algún día querrás definir tus propios tipos para la función deseada. Esto se puede hacer usando la palabra clave "tipo":


 type Adder = int -> int type AdderGenerator = int -> Adder 

En el futuro, puede usar estos tipos para limitar los valores de los parámetros de la función.


Por ejemplo, una segunda declaración caerá debido a una restricción con un error de conversión. Si lo eliminamos (como en el tercer anuncio), el error desaparecerá.


 let a:AdderGenerator = fun x -> (fun y -> x + y) let b:AdderGenerator = fun (x:float) -> (fun y -> x + y) let c = fun (x:float) -> (fun y -> x + y) 

Prueba de las firmas de funciones de comprensión


¿Entiendes bien las firmas de funciones? Verifique usted mismo si puede crear funciones simples con las firmas a continuación. ¡Evite especificar tipos explícitamente!


 val testA = int -> int val testB = int -> int -> int val testC = int -> (int -> int) val testD = (int -> int) -> int val testE = int -> int -> int -> int val testF = (int -> int) -> (int -> int) val testG = int -> (int -> int) -> int val testH = (int -> int -> int) -> int 

Recursos Adicionales


Hay muchos tutoriales para F #, incluidos los materiales para aquellos que vienen con experiencia en C # o Java. Los siguientes enlaces pueden ser útiles a medida que profundiza en F #:



También se describen varias otras formas de comenzar a aprender F # .


Finalmente, la comunidad F # es muy amigable para principiantes. Hay un chat muy activo en Slack, respaldado por la F # Software Foundation, con salas para principiantes a las que puedes unirte libremente . ¡Recomendamos encarecidamente que haga esto!


¡No te olvides de visitar el sitio de la comunidad de habla rusa F # ! Si tiene alguna pregunta sobre el aprendizaje de un idioma, estaremos encantados de discutirlo en las salas de chat:



Sobre autores de traducción


Traducido por @kleidemos
La traducción y los cambios editoriales fueron realizados por los esfuerzos de la comunidad de desarrolladores de F # de habla rusa . También agradecemos a @schvepsss y @shwars por preparar este artículo para su publicación.

Source: https://habr.com/ru/post/es433402/


All Articles