Activándose y cambiando de mentalidad con Elixir

Creado en 2012, Elixir es un lenguaje funcional y dinámico, el cual es una opción real si queremos crear una aplicación web. Esto se debe a que Elixir corre bajo la máquina virtual de Erlang (BEAM) que lo hace realmente estable y maduro. Creado por José Valim, quien es un entusiasta de Ruby on Rails y ha ayudado mucho a la comunidad. Entonces, él tomó lo mejor de Rails y lo fusionó con la eficiencia de Elixir+Erlang.



Elixir es especial para sistemas de alta disponibilidad.



Funcional


Los primeros dos significados que tenemos que olvidar son objetos y clases. Todo está en torno a funciones que operan y transforman datos, funciones puras. No obstante, no podemos depender de que Elixir sea un lenguaje solamente funcional.

Dinámico


Las variables Elixir no necesitan ser declaradas como un tipo de datos específico, él verifica en tiempo de ejecución.

Inmutable


Las estructuras de datos no cambian. Por ejemplo:

# Usa el operador igualar para dar verdadero en la función
name = “Han Solo”


# Crea un valor totalmente nuevo para la nueva variable
name = "Luke Skywalker"

Concurrencia



Esto es algo que Elixir puede vender sin ninguna ayuda, es una gran característica aquí. Dado que, Elixir corre bajo BEAM, la concurrencia es mucho más sencilla. El código corre simultáneamente, ¡Gracias Erlang!




Confiabilidad


Elixir es un lenguaje nuevo, pero corre en la MV Erlang, la cual es un sistema bastante antiguo y probado, uno de los más probados mundialmente.

Fácil de leer y escribir


By taking some Ruby stuff, Elixir decided to go with the syntax, which is good for us. The code is easy to read and o write.

¿Modules o clases?


Como lo mencioné anteriormente las clases no son consideradas en Elixir, cada función es almacenada y manejada en un módulo, igual que su espacio de nombres


defmodule HelloModule dodef say_hi do IO.puts "Hello World !" endend

Estructuras



Este es un mapa donde configuramos un conjunto de llaves y sus valores por omisión. Está definido en el módulo.




defmoduleUserdo
defstruct name: "John", roles: []
end

iex> %User{name: "Han", roles: [:pilot, :shooter]}
User{name: "Steve", roles: [:pilot, :shooter]}

Cadenas



Una cadena es básicamente una secuencia de bytes, con código binario UTF-8. Elixir nos proporciona un conjunto de funciones para interactuar con nuestras cadenas.




Lenght.


length/1

Devuelve el número de bytes en nuestra cadena.

iex> String.length "Walter White"
12

Reemplazar


replace/3


Devuelve una nueva cadena, recibe tres parámetros, la cadena a ser cambiada, el patrón a ser reemplazado y la cadena de reemplazo.




iex> String.replace("Seed","e","i")"Siid"

Duplicar


duplicate/2

Devuelve una cadena específica duplicada el número de veces establecido.




iex> String.duplicat("Hello",3)"Hello Hello Hello "

Dividir


split/2

Devuelve una lista basada en el patrón de división.

iex> String.split("Hello, I'm Bond, James Bond", ",")["Hello", " I'm Bond", " James Bond"]

Colecciones


Lists



Una simple colección de valores, donde puede haber cualquier tipo de datos.




iex> ["String", 12.3 , :atom]["String", 12.3, :atom]


Yuxtaposición de Listas


++/2

iex> ["Luke", "Leia"] ++ ["Han"]
["Luke", "Leia", "Han"]

Subtracción de Listas


Sustracción de listas 


--/2

iex> ["String", 12.3 , :atom] -- [:atom]
["String", 12.3]

Cabecera / Cola



La cabecera es el primer elemento de nuestra lista y la cola son los restantes elementos en la lista.




iex> hd ["Head", 222, :tail]
"Head"

iex> tl ["Head", 222, :tail]
[222, :tail]

Lista de palabras claves


Es una lista asociativa compuesta por dos tuplas, donde la llave tiene que ser un tipo de dato átomo, ellas son ordenadas y las llaves pueden ser proporcionadas más de una vez



iex> [foo: "bar", hello: "world"]
[foo: "bar", hello: "world"]

iex> list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]

iex> list ++ [{:c, 3}]
[a: 1, b: 2, c: 3]

Mapas


Los mapas son más flexibles en comparación con las listas de palabras clave, las llaves pueden ser cualquier valor, incluyendo variables y las llaves de los mapas no siguen ningún tipo de orden.

iex> key = :one


iex> list = %{key => 1, :two => 2, "three" => 3, 4 => 4}
%{:one => 1, :two => 2, "three" => 3, 4 => 4}


# Obtener un valor específico de una llave dada.
iex
> Map.get(list, key)
1


# Agregar una tupla a la presente lista 
iex>
Map.put(list, :five, 5)
%{:one => 1, :two => 2, "three" => 3, 4 => 4, :five => 5}


# Devolver el mapa en formato de lista 
iex
> Map.to_list(list)
[{4, 4}, {:atom, 1}, {:five, 5}, {:two, 2}, {"three", 3}]

Enum


Es un conjunto de algoritmos para ser usado con las colecciones. En esta sección solo mostraremos algunos de ellos. Puede revisarlos todos aquí.

All


all?/2

Suministramos una fn(x) la cual será ejecutada para todos los elementos de nuestra colección. Devolverá verdadero si todas las llamadas devuelven verdadero, con solo una que devuelva falso el método completo devolverá falso.


iex> Enum.all?([1,2,3], fn(number) -> number < 5 end )
true

iex> Enum.all?([1,2,3], fn(number) -> number < 2 end )
false

Any


any?/2

Suministramos fn(x) que será ejecutada para todos los elementos de nuestra colección. Devolverá verdadero si por lo menos una invocación devuelve verdadero, de lo contrario devolverá falso.


iex> Enum.any?([1,2,3], fn(number) -> number < 2 end )
true

iex> Enum.any?([1,2,3], fn(number) -> number == 5 end )
false

Chunk By


chunk_by/2


Especialmente si necesitamos agrupar nuestra colección en base a una función dada.




iex> Enum.chunk_by(["one", "two", "three", "four", "five"], fn(x) -> String.length(x) end)

[["one", "two"], ["three"], ["four", "five"]]

Each


each/2



Invoca la función dada para cada elemento de la colección. Devuelve un átomo :ok


iex> Enum.each(["one", "two", "three"], fn(x) -> IO.puts(x) end)
one
two
three
:ok

Map


map/2


Invoca la función dada para cada elemento de la colección. Devuelve una nueva colección con los nuevos valores.




iex> Enum.map(["one", "two"], fn(x) -> String.upcase(x) end)
["ONE", "TWO"]

Member


member?/2


Verifica si un elemento existe en una colección.




iex>  Enum.member?(["one", "two", "three"], "three")
true

Reject


reject/2

Devuelve una nueva colección de elementos que devuelven falso para la fn(x) dada.

iex> Enum.reject([1,2,3,4,5,6], fn(x) -> Integer.is_even(x) end)
[1, 3, 5]

Sort


sort/2

Ordena la colección según la fn(x) dada.

iex> Enum.sort([%{:val => 2}, %{:val => 3}, %{:val => 1}], fn(x, y) -> x[:val] > y[:val] end)

[%{val: 3}, %{val: 2}, %{val: 1}]

Unique By


unique_by/2

Suprime todos los duplicados en nuestra colección

iex> Enum.uniq([1, 2, 3, 2, 1]) 
[1, 2, 3]

Operador Pipe


El operador pipe |>pasa el resultado de una expresión como el primer parámetro de otra expresión.

Cuando estaba utilizando elixir por primera vez este operador llamó mi atención inmediatamente. Dado que la programación funcional se ocupa de enviar datos y transformarlos mediante funciones se puede volver realmente enredada, pero el operador pipe es algo que nos ayuda con ese trabajo



Nuestro problema:


# Devuelve el valor final de un producto
formated_price(taxes(commision(product_value(), 0.01), 0.13))


Alguna OO solución:

prod_val = product_value()
prod_commision = commision(prod_val, 0.01)
prod_taxes = taxes(prod_commision, 0.13)
prod_final_value = formated_price(prod_taxes)
prod_final_value

Nuestra Solución Elixir:


product_value()
|> commision(0.01)
|> taxes(0.13)
|> formated_price()


Como puede ver cada valor devuelto por una función es pasado como el primer parámetro de la siguiente función. Ello hace nuestro código realmente fácil de leer.




Emparejar patrones ¿no eran solo asignaciones?


Esta es una funcionalidad profunda en Elixir. Para comprender esto un poco más tengo que decir que el operador = no necesariamente significa “asignar algo a una variable”, en cambio realmente significa “emparejar el lado izquierdo con el lado derecho”. Ello convierte la expresión completa en una ecuación.

iex> x = 1
1

iex> 1 = x
1

iex>  2 = x
** (MatchError) no match of right hand side value: 1

Enfoques útiles



El emparejamiento de patrones se vuelve realmente útil cuando lo usamos con tuplas, funciones o recursiones.





# Queremos asignar la concordancia de value con el número 2
iex>
{:ok, value} = {:ok, 2}
{:ok, 2}

iex> value
2

# Si el valor átomo:ok no concuerda devolverá una falla


iex> {:ok, value} = {:none, 2}
** (MatchError) no match of right hand side value: {:none, 2}


Luce realmente genial, sin embargo revise esto, algo un poco más funcional





defmodule Greet do
def say_hi(tuple) do
case tuple do
{:morning, name} ->
"Morning #{name} !"
{:evening, name} ->
"Good evening #{name} !"
_ ->
"Default Hi !!"
end
end
end

iex> Greet.say_hi({:morning, "Luke"})
"Morning Luke !"

iex> Greet.say_hi({:morning, "Han"})
"Morning Han !"

iex> Greet.say_hi({:defaut, "Nobody"})
"Default Hi !!"


Finalmente, el enfoque más útil para mi es el emplear la concordancia de patrones en las definiciones de función:





defmodule Greet do
def say_hi(:morning, name) do
"Morning #{name} !"
end

  def say_hi(:evening, name) do
"Good Evening #{name} !"
end

  def say_hi(name) do
"Default Hi #{name} !"
end
end


iex> Greet.say_hi(:morning, "Luke")
"Morning Luke !"

iex> Greet.say_hi(:morning, "Han")
"Morning Han !"

iex> Greet.say_hi("Leila")
"Default Hi Leila !"

Conclusión



Para mí, teniendo conocimientos de Ruby on Rails, Elixir luce bastante agradable y estoy procurando ir más y más a profundidad en este lenguaje. Esta publicación es parte de una introducción que realizamos en 4geeks, pero puede revisar el video completo.




Comentarios

Entradas más populares de este blog

Growth Hacking vs Publicidad Digital

4Geeks reúne a los principales actores financieros de Costa Rica

No es el mercado, es tu mentalidad