Elixir

Elixir es un lenguaje de programación funcional para la creación de aplicaciones distribuidas y escalables que corre sobre la plataforma Erlang. Hoy día, Elixir se usa para crear todo tipo de aplicaciones, destacando aquellas orientadas a las redes y a los servicios, como aplicaciones web, colas de mensajes o plataformas de chat.

9:41

1. ¿Qué es Elixir?

Elixir es un lenguaje de programación funcional para la creación de aplicaciones distribuidas y escalables que corre sobre la plataforma Erlang. Hoy día, Elixir se usa para crear todo tipo de aplicaciones, destacando aquellas orientadas a las redes y a los servicios, como aplicaciones web, colas de mensajes o plataformas de chat.

8:40

2. Instalación de Elixir

Vamos a ver cómo instalar Elixir en los principales sistemas operativos del mercado (Windows, Linux, macOS y BSD), así como cómo ejecutarlo desde Docker.

19:41

3. Programación funcional explicada para programadores imperativos

Otro vídeo introductorio destinado a explicar a personas que tengan experiencia con programación imperativa pero no con programación funcional las diferencias entre ambos paradigmas y qué debemos hacer para adaptar nuestra mente a un paradigma de programación distinto. 00:00 Introducción 01:13 ¿Cómo se estructura un programa imperativo? 03:37 ¿Cómo se estructura un programa funcional? 05:18 Ejemplo factorial: imperativo vs funcional 09:29 Ejemplo TAD Lista y operación Filtro 14:45 Funciones como elementos de primera clase 17:43 Resumen: ¿qué caracteriza a un lenguaje funcional?

6:57

4. Escribiendo expresiones en la REPL

Arrancamos la REPL de Elixir (iex) por primera vez, y vemos cómo escribir algunas expresiones simples que se devuelven a sí mismas.

7:07

5. Asignaciones... más o menos

A ver, en Elixir no hay asignaciones en el sentido más literal de la palabra. Ya lo veremos. Sin embargo, un tipo de expresiones que nos son útiles es poder darle identificadores al resultado de evaluar otras expresiones para poder hacerlas más simbólicas y poder utilizarlas cómodamente al fabricar otras expresiones compuestas.

6:29

6. Operadores aritméticos básicos

Mediante los operadores de la suma, resta, multiplicación y producto podemos escribir expresiones que representan operaciones aritméticas, que cuando se evalúan devuelven el resultado de la misma operación como expresión resultante.

5:10

7. ¿Qué quiere decir "tipos de datos"?

Antes de empezar a hablar de tipos de datos como tal, una pregunta más obvia: ¿qué es un tipo de datos y por qué existen?

4:10

8. Átomos

Un átomo es un tipo de datos primitivo en el que el valor de la expresión se corresponde con su nombre. Parece sencillo y realmente lo es, pero acostumbráos a verlos porque en Elixir se utilizan en muchísimas situaciones.

5:40

9. Tres átomos muy especiales: nil, true y false

nil, true y false son tres átomos tan importantes que los vas a poder escribir sin tener que poner el caracter : delante de ellos. nil se usa para representar los valores nulos (o sea, la ausencia de dato); mientras que true y false se emplean para representar lógicos, es decir, el concepto de verdadero y el concepto de falso.

7:59

10. Operadores lógicos de comparación

Pues como en cualquier lenguaje de programación, tenemos operadores que trabajan con lo que le pongas a la izquierda y a la derecha y en función de las reglas del operador harán que la expresión evalúe, bien a true o bien a false, para representar lo verdadero y lo falso.

3:28

11. Comparaciones entre tipos y comparadores estrictos

Entre distintos tipos de datos también se pueden usar operadores de comparación, pero las reglas son un poco diferentes. Mira esto para ver el orden completo: https://hexdocs.pm/elixir/operators.html#term-ordering. Además, con el operador estricto (===) puedes también asegurar que ambos operandos numéricos son del mismo tipo (Integer o Float).

8:07

12. Operadores lógicos proposicionales

Los operadores lógicos proposicionales evalúan a true o false según cómo de true o de false sean los inputs. NOT niega la veracidad de su input, AND sólo es true cuando ambas entradas sean true, OR es true salvo que ninguna entrada sea true.

9:46

13. Sintaxis e invocación de funciones

Las funciones son una parte primordial de Elixir. En una función se define una regla para transformar una serie de datos de entrada en una serie de datos de salida. En este vídeo explico en qué consiste, y también muestro cómo la sintaxis para aplicar o invocar una función en el lenguaje de programación Elixir.

8:30

14. Aridades, módulos y más funciones interesantes

La aridad es el número de parámetros que acepta una función. En Elixir es importante porque dos funciones que se llamen igual pero que tengan aridades diferentes son distintas. Es legal tener dos funciones con el mismo nombre a la vez en el mismo módulo siempre que tengan distinta aridad. Hablando de módulos, ¿qué son? Y por el camino, presento algunas funciones interesantes, como Kernel.is_atom/1, Kernel.is_integer/1...

7:47

15. Cadenas de caracteres

Strings o caracteres encadenados. Si ya sabes lo que es una cadena de caracteres, me preocuparía que no veas este vídeo a velocidad 1.5x o que no te saltes la parte en la que explico que es lo que va entre comillas, en verdad.

9:05

16. Entrada y salida estandar de la mano de gets y puts

Venga, por fin avanza un poco más deprisa. Las funciones puts y gets del módulo IO permiten imprimir de verdad en pantalla, y leer del teclado. Esto lo necesitaremos si queremos ver letras en la terminal cuando abandonemos IEx.

9:10

17. Concatenar e interpolar strings

La concatenación de cadenas de caracteres permite juntar varias cadenas de caracteres en una. Además, con la interpolación, vamos a poder meter unas cadenas en otras. Por lo demás, aprovecho para hablar más sobre cómo escapar caracteres.

8:52

18. Código fuente en archivos

Es hora de salir de IEx y empezar a crear nuestros propios archivos de script reusables con código que podamos lanzar una y otra vez. Aquí os explico cómo se van a llamar estos archivos, qué extensión van a tener y creamos un Hola Mundo.

11:00

19. Condicional IF y bloques DO-END

IF es la primera de las estructuras del lenguaje que vamos a ver. Evalúa a una expresión u otra según si una tercera expresión denominada condicional es verdadera o no. Por ejemplo, if A do B else C end evalúa a B si A, y evalúa a C si !A. Además, los bloques DO-END, que sirven para agrupar múltiples expresiones, evaluando a la última expresión del bloque.

12:08

20. IFs anidados, UNLESS y COND

Pequeña reseña del condicional UNLESS, que es como el condicional IF pero al revés, y luego lo más relevante, el condicional COND, que sirve para fabricar tablas de condicionales más concisas que usar IFs anidados.

11:08

21. Definimos funciones

En Elixir podremos crear nuestros propios módulos en los que insertar nuestras propias funciones, que podremos así utilizar para desarrollar programas en los que funciones propias se agrupan para componer la lógica del programa.

5:47

22. Compilando módulos

Maneras de compilar nuestros módulos escritos en archivos, para hacerlos accesibles a través de Elixir o a través de iex para poder emplear interactivamente las funciones que se declaran.

8:19

23. Guardas

Una guarda es una anotación condicional que se pone en una función y que hace que esa función sólo pueda ser utilizada si la precondición que hemos indicado se cumple de antemano con los parámetros. Lo podemos usar para controlar los tipos o los valores que se están intentando proporcionar a la función.

7:21

24. Funciones anónimas

Una función anónima es una función aislada que en vez de ser declarada de manera formal (por ejemplo, dentro de un módulo), se declara como una expresión más que podría ser asignada a variables.

4:27

25. Capturar funciones

Al capturar funciones podemos obtener referencias a funciones que sí esten declaradas en módulos, para poderlas emplear en casos como pasarlas como parámetro a otras funciones, parecido a las funciones anónimas.

7:09

26. Funciones del mismo módulo y privadas

Algunas puntualizaciones sobre llamadas a funciones pertenecientes al mismo módulo, y uso de defp para crear funciones que sólo pueden ser invocadas desde dentro del mismo módulo.

7:32

27. Tuplas y función elem

Las tuplas son estructuras de datos que permitirán agrupar múltiples expresiones en una única expresión compuesta. Con la función elem, podemos extraer el elemento que se encuentre en una posición concreta de las tuplas.

8:01

28. Introducción al pattern matching

El pattern matching es uno de los elementos esenciales de Elixir. Con el pattern matching se pueden escribir expresiones con algunas incógnitas (en forma de variable) y dejar que sea el propio lenguaje quien trate de buscar las soluciones. Por el camino, nos dice qué valor tendrán esas incógnitas.

10:45

29. Pattern matching en funciones

El pattern matching de Elixir puede ser aprovechado en funciones para distintos propósitos. Por ejemplo, en vez de usar guardas, si queremos forzar a meter parámetros concretos en posiciones concretas, podemos poner una constante en los parámetros. Además, las funciones de destructuring también están disponibles.

7:29

30. Las tuplas :ok, :error

Al hilo de lo del pattern matching y el destructuring de tuplas, en las funciones no puras de Elixir que pueden provocar errores, un patrón muy común es envolver los retornos en algún tipo de tupla que permita devolver, no sólo el retorno de la función, sino también si ha salido bien o mal.

10:00

31. case

case es una estructura del lenguaje que permite hacer una tabla de matcheos mediante la cual es posible escoger una de entre muchas expresiones resultantes en función de cuál es el primer matcheo compatible.

6:37

32. pin

El operador pin sirve para fijar los elementos que hay a la izquierda de un igual en Elixir, de modo que deje de verlos como incógnitas a despejar durante un matcheo y lo vea como variables ya declaradas cuyo valor tiene que forma parte del casamiento en el despeje.

4:49

33. Recursividad (volumen 1)

En la recursividad, una función se llame a sí misma con distintos parámetros. La ventaja de disponer de pattern matching en Elixir es que podemos escribir código recursivo de una forma muy simple en la que los casos base se teclean como constantes o usando guardas.

8:52

34. Listas

Las listas son una estructura de datos que en Elixir permite agrupar de forma dinámica múltiples elementos. A diferencia de las tuplas, tienen una estructura diferente compuesta de cabeza y cola, lo cual nos puede dar juego en algunos casos, pero sin olvidarnos de sus consecuencias.

10:07

35. Operadores y funciones de lista

El operador concatenación y el operador diferencia, y también algunas de mis funciones favoritas del módulo List y del módulo Enum, para poder trabajar con listas.

8:20

36. Listas de palabras clave

La primera estructura de datos asociativa que vamos a ver es la lista de palabras clave o keyword list, que permite asociar valores a átomos. Aquí su construcción, consulta con el operador [:x], y una particularidad importante sobre do, end y else que tal vez te ayude mucho.

7:28

37. Mapas

Un mapa es una estructura asociativa que permite asociar dos expresiones entre sí formando un sistema clave-valor: toda expresión término (clave) tiene una definición (valor). Como las keyword lists, por otra parte. La diferencia es que los mapas nos dan más juego.

5:52

38. Pattern matching de mapas y keyword lists

Ambas estructuras nos permiten usar el pattern matching para casar estructuras en función de si tienen o no elementos, además de poder sacar por el camino el valor de estas claves.

5:16

39. Operadores y funciones para mapas y keyword lists

Veamos qué operadores tenemos a nuestra disposición para trabajar con keyword lists. Con mapas es un poco más complicado porque no nos vale el operador ++, pero tenemos el módulo Map para hacer todo este tipo de cosas.

10:49

40. Estructuras

Con la palabra clave defstruct podemos crear estructuras. Son como mapas, pero tienen una semántica que declaramos nosotros y que es mucho más fija. Conviene tenerlo en mente si pretendemos usar librerías como Ecto (y por ende, por ejemplo, Phoenix).

11:17

41. Bitstrings

El último tipo de datos que vamos a ver es bitstring, y es literalmente eso: una cadena de bits. De este modo podemos guardar números con una longitud predefinida, como en los lenguajes de programación tradicionales. Además, binarios y su relación con las cadenas de caracteres. 00:00 Introducción a las bitstrings 04:32 Bitstrings con múltiples valores 05:18 Comparación de bitstrings 06:12 Binaries y su relación con los strings

10:12

42. Tu lista no está rota, es una charlist

Si alguna vez te ha pasado la de crear una lista de enteros y que te lo muestre como un montón de caracteres, no es que se haya roto tu instalación de Elixir, es que estás tratando con una charlist. No se usa mucho, pero ahí está. 00:00 Introducción a las charlists 03:02 Historias sobre codificación: la tabla ASCII 06:20 Codificación internacional: Unicode 08:12 Un caracter Unicode es más de un byte 09:05 Resumen de todo esto

5:24

43. Sobre las funciones de alto orden

En Elixir, como en muchos lenguajes de programación funcionales, en vez de tener un montón de funciones especializadas en un montón de tipos de datos diversos, tenemos funciones generales de alto orden que aceptan como parámetro más funciones, con las cuales podemos especificar cómo queremos que funcionen.

9:37

44. Enum.filter

La función filter del módulo Enum sirve para extraer de una colección enumerada de elementos una subcolección en la que sólo nos quedamos con aquellos elementos que cumplan con una condición booleana que le indiquemos previamente.

6:51

45. Enum.map

La función map del módulo Enum sirve para transformar uno a uno cada elemento de una colección enumerada, recogiendo cada uno de esos elementos transformados en otra colección resultante que obtenemos tras evaluar.

8:45

46. Enum.reduce

La función reduce del módulo Enum sirve para acumular parcialmente cada uno de los elementos de una colección enumerada entrante, hasta evaluarse completamente a un único resultado final.

11:00

47. Pipelines

El operador Pipeline es un operador especial de Elixir que sirve para hacer más fáciles de comprender algunas invocaciones a funciones, porque nos permite separar el primer parámetro de una aplicación del resto de parámetros, algo que nos viene bien porque podemos escribir expresiones tipo a(b(c(d(e(f(x))))) como x → f → e → d → c → b → a.

11:28

48. Rangos y Streams

A diferencia de Enum.map y Enum.filter, con Stream.map y Stream.filter podemos obtener Streams. Un Stream en Elixir permite hacer procesamiento pospuesto: Elixir sabe que quiero procesar una colección con una función, pero no ejecuta el cómputo hasta que no haga falta, algo que en algunas ocasiones es ventajoso para hacer programas más eficientes.

13:45

49. Recursividad (volumen 2)

Volvemos con la recursividad, esta vez para hablar de cómo aprovechar el pattern matching en listas, por ejemplo, para hacer funciones reductoras o transformadoras. Normalmente querremos usar las funciones nativas del módulo Enum o Stream, pero cuando no quede otra, tenemos a nuestra disposición recursividad.

10:08

50. mix

Mix es una herramienta que forma parte de Elixir que sirve para gestionar un proyecto, manteniendo múltiples archivos .ex con los distintos módulos que forman nuestro programa. También nos permiten compilar aplicaciones para distribuirlas, instalar nuevas dependencias, y mantener una suite de tests para comprobar el estado de nuestro proyecto.

9:31

51. Documentando código: comentarios, docs y moduledocs

En todo este tiempo no he contado cómo se escribe un comentario en Elixir, no me lo creo. Además, cómo usar doc y moduledoc para escribir documentación sobre el propósito de una función o de un módulo cuando estemos escribiendo nuestros programas.

9:26

52. Atributos de módulo

Los atributos de módulo son anotaciones tipo clave-valor que empiezan por una arroba (como doc, moduledoc, ya vistos) y que sirven para asignar metadatos a los módulos, aunque también son empleados para crear constantes.

11:33

53. Dependencias

Normalmente en Elixir el código que escribimos no vive aislado, sino que es habitual utilizar funciones escritas en otros proyectos con los que nos conectamos, para reutilizar módulos y no programar lo mismo dos veces. hex.pm es el gestor de paquetes primario de Elixir para estos fines y en este vídeo te cuento cómo traerte dependencias de hex.pm a tu mix.exs.

13:09

54. Un ejemplo práctico de módulo útil

13 minutos de programación comentada en vivo de un módulo usando una dependencia y cosas que hemos visto en los últimos capítulos, como pipelines o funciones de alto orden transformadoras, con el objetivo de dar la excusa para refactorizarlo en el próximo vídeo.

10:22

55. Alias e import

Después de escribir este código, vamos a usarlo como base para tratar algunas herramientas útiles que nos ofrece Elixir para controlar el código de nuestros módulos. Alias sirve para darle otro nombre a un módulo y hacerlo más fácil de escribir. Import sirve para traer definiciones de otros módulos y así no tener que poner su prefijo delante.

11:11

56. Sobre las macros, require y use

Use es una palabra clave empleada para invocar una macro declarada en otro módulo con el objetivo de importar código en nuestro módulo. Como si fuese un copia y pega, se traerá definiciones que haya en ese módulo. Require sirve para importar macros específicas. Esto requiere que presente por encima la metaprogramación, aunque no es el día de hablar de ella.

13:11

57. Concurrencia y OTP: creando procesos

Con la programación concurrente podemos tener de forma simultánea múltiples hilos de ejecución para evaluar en paralelo múltiples expresiones. Con la primitiva spawn podemos pedirle a la plataforma OTP que nos lance un nuevo proceso para evaluar por separado una expresión.

13:48

58. Procesos que comunican

Con las primitivas send y receive podemos hacer que dos procesos se intercambien información. En Elixir, la forma estandar de compartir información entre procesos es mediante sistema de paso de mensajes, teniendo procesos aislados que se comunican mediante un sistema de mailboxes (bandejas de mensaje), formando un sistema de actores.

15:03

59. Procesos que recuerdan cosas

Algunas estrategias sobre cómo hacer procesos que pueden persistir un estado entre receive y receive.

12:42

60. Procesos que fallan

Por defecto, en caso de que un proceso falle y se detenga, no nos vamos a enterar, porque OTP descarta esos errores silenciosamente. Sabemos que ha dejado de operar porque deja de estar alive pero poco más. Con un link (Process.link/1 y spawn_link) podemos conectar dos procesos que dependen entre sí para que el fallo de uno afecte al otro.

10:10

61. Procesos que se monitorizan

Vale, dejar caer un proceso cuando sus procesos conectados caen no es precisamente lo más robusto. Por eso, podemos pedirle a la OTP en su lugar que en vez de dejar caer un proceso, simplemente nos informe mediante otro paso de mensaje, cuando un proceso haya finalizado mal. Presentamos la monitorización, que con Process.monitor/1 o con spawn_monitor permite una forma más dulce de detectar cuando un proceso cae.

15:22

62. GenServer (parte 1)

El GenServer es una estructura de alto nivel construida por encima de la API de Procesos de Elixir para facilitar el uso de procesos en los cuales se envían mensajes y se gestionan estados. En este vídeo empezamos viendo init y handle_info, funciones útiles para empezar a trabajar con procesos. Además, de refilón: ¿os habéis fijado que con pid/1 puedo obtener un PID dado un string con sus tres números? ¿y que con r() puedo recompilar sobre la marcha mediante hot swap un módulo en IEx?

18:55

63. GenServers (parte 2: handle_call y handle_cast)

Siguiendo en el tour de los GenServer, las funciones más importantes de un GenServer son, sobre todo, handle_call; y también, handle_cast. Usaremos handle_call para gestionar llamadas síncronas por parte de los clientes que se conecten a nuestro servidor; y handle_cast para las llamadas asíncronas. 00:00 impl handle_call 06:05 GenServer.call 08:53 Timeouts 13:10 impl handle_cast 15:23 handle_info vs handle_cast 15:40 GenServer.cast 17:22 Envolviendo calls y casts

19:06

64. GenServer (parte 3: control de errores y otros asuntos)

Un popurrí de trucos y consejos para usar los GenServers, así como una explicación sobre cómo señalizar correctamente los errores en un GenServer mediante el átomo :stop. Síganme para más recetas: https://hexdocs.pm/elixir/GenServer.html 00:00 ¿Pero cuál de todas estas funciones debo usar? 03:35 Devolviendo :stop o :ignore en el init 07:51 Timeouts en el servidor 11:37 Cuando las cosas no salen bien: devolviendo :stop 15:10 Devolviendo :noreply en el handle_call. 17:50 Se acabó GenServer

10:34

65. Procesos con nombres propios

Registrar un nombre en un proceso (o un GenServer, o un Supervisor) tiene ventajas frente a dejarlos anónimos en términos de identificarlos. Además, cómo podemos explorar en el observer información sobre procesos (como su nombre, por ejemplo).

17:26

66. Supervisores (parte 1)

Un supervisor es un tipo de proceso que permite controlar automáticamente el ciclo de vida de otros procesos (o GenServers) reiniciándolos automáticamente al detectar un fallo que los tumbe.

17:59

67. Supervisores (parte 2)

Más sobre las child-specs y las estrategias para lanzar supervisores, y cómo crear supervisores usando módulos separados.

11:07

68. Supervisores (parte 3)

Finalmente, la diferencia entre start_link/2 y start_link/3 y el uso de init/2 para desacoplar la creación de un Supervisor de la especificación de los procesos supervisados y de las opciones de supervisión.

12:18

69. Un resumen sobre procesos OTP

Hemos visto demasiadas cosas en la última docena de episodios. ¡Es hora de poner un poco de orden con un mindmap que ayude a aclarar las ideas!

12:00

70. Aplicaciones (parte 1)

En la máquina BEAM una aplicación consiste en un supervisor y una configuración. El código fuente del paquete compone la aplicación, y la configuración permite parametrizar el comportamiento de la aplicación.

13:36

71. Aplicaciones (parte 2)

Un ejemplo real con la librería Plug de cómo podemos incorporar distintos procesos en el GenServer que debe declararse en una Aplication. Además, por qué hace falta no-halt.

10:06

72. Typespecs (parte 1, usando tipos básicos)

Con los type specs se pueden especificar los tipos de los parametros y retornos de las funciones para que herrramientas como Dialyzer puedan analizar de forma estática el código a fin de comprobar que se pasen los parámetros de forma correcta en el código.

10:31

73. Typespecs (parte 2, tipos propios y t())

A medida que desarrollemos estructuras de datos más complejas, el sistema de tipos de Elixir se nos quedará corto. Por suerte lo podemos extender creando nuestras propias definiciones de tipos para fabricar abstracciones sobre otras estructuras más complejas.

11:26

74. Comportamientos

Los comportamientos es la respuesta de Elixir a la programación por contrato y a la fabricación de interfaces que encontramos en otros lenguajes de programación. Mediante callback y behaviour podemos crear módulos que especifican primitivas que otros módulos deben implementar.

7:59

75. Tratamiento de errores con rescue

Otra forma más de tratar errores es usar un bloque try-rescue para envolver el código problemático y capturar posibles errores que puedan ocurrir durante su uso para evaluar a expresiones alternativas en caso de error.

8:24

76. Elevando errores con raise

Cuando encontramos una situación anómala en nuestras funciones, podemos interrumpir la evaluación del resto de la función mediante el comando `raise`, que nos permite lanzar hacia fuera un Error que puede ser tratado en otro bloque try-rescue.

13:44

77. with

with permite agrupar múltiples expresiones de tipo pattern matching en un único bloque do-end, de tal manera que la única forma de evaluar su interior es que todos los matches sean válidos a la vez. Con esto podemos desplegar cómodamente expresiones envueltas en tuplas sin crear un excesivo número de cases.

7:53

78. Sigilos

Un sigilo es un símbolo que insertamos junto a una expresión para que Elixir la trate de forma especial. Normalmente se usan para crear listas de átomos o cadenas de caracteres sin escribir tanta comilla o para no tener que escapar las comilas en una string o una charlist, pero Elixir permite extender el lenguaje creando sigilos personalizados, como te cuentan aquí: https://elixir-lang.org/getting-started/sigils.html#custom-sigils.

11:40

79. Tests con ExUnit

ExUnit es un framework para escribir tests en Elixir. En este vídeo, por qué los tests nos vienen bien, y un ejemplo rápido de cómo crear tests con Elixir que profundizaremos en siguientes episodios.

13:12

80. Más particularidades de ExUnit

ExUnit no es la librería de TDD más sofisticada del mundo, pero ofrece pequeños apoyos para facilitar escribir tests. Tenemos otros asertos para comprobar que algo es falso o que el intercambio de mensajes entre procesos de OTP se hace bien, así como hooks adicionales.