BEAM ecosystem
Mix.install(
[
{:kino, "~> 0.14.1"},
{:kino_bumblebee, "~> 0.5.0"},
{:exla, ">= 0.0.0"}
],
config: [nx: [default_backend: EXLA.Backend]]
)
Erlang
Lenguaje de programación funcional, dinámico, que corre sobre una máquina virtual (BEAM) -> Bogdan/Björn’s Erlang Abstract Machine, diseñado especialmente para la concurrencia y la alta disponibilidad. Fue inventado inicialmente para manejar sistemas de telecomunicaciones en 1986 por Ericsson.
Una de las filosofías con las que se diseñó el lenguaje es “let it crash”, es decir, los sistemas eventualmente fallarán, y es casi imposible manejar todos los errores. Por lo tanto, lo que se hace es aislar el error en un proceso (no confundir con el concepto de proceso del Sistema Operativo), “matarlo” y volver a comenzar en un estado conocido.
¿Se han preguntado muchas veces cómo, simplemente apagando y encendiendo la computadora, se arregla el problema? Bueno, es algo similar.
Elixir
Es un lenguaje de programación funcional, inventado en 2011, que corre sobre la misma máquina virtual que Erlang. El creador de Elixir se preocupaba porque no había muchos lenguajes buenos para manejar de manera eficiente y efectiva la concurrencia, por lo que decidió crear Elixir. Es parecido a Erlang, pero con una sintaxis más amigable y moderna. El creador fue core contributor de Ruby on Rails, por lo que Elixir se asemeja en cierta forma a Ruby en cuanto a la sintaxis.
Elixir 101
Si se pudieran definir los bloques de construcción del lenguaje, en mi opinión serían:
- módulos
- funciones
- listas
- mapas
- tuplas
- pattern matching
- recursión
# Modulos
defmodule Hello do
# Funciones publicas o privadas
def world do
"Hello World"
end
# Pattern matching
def world(name) do
"Hello #{name}"
end
# Lista
def world_list do
"Hello World"
|> String.split()
# String.split("Hello World")
# ["Hello", "World"]
end
def world_tuple do
{:hello, :world}
end
def world_map(test) do
%{"hello" => "world"}
end
end
# Pattern matching
- Es de tipado dinámico, es decir, no especificamos el tipo de dato, la máquina lo infiere.
- inmutable: cuando se modifica algo, en realidad es un nuevo término.
Menciones honoríficas de otros lenguajes en BEAM
- Gleam (2016) soporte nativo de tipos y compilación a JavaScript.
- LFE (2008) implementación de Lisp.
Modelo de actores
Los actores son la unidad básica de computación.
Kino.Process.render_seq_trace(fn ->
_armando = self()
_betty = spawn(fn ->
:ok
end)
end)
Los actores se comunican entre si enviandose mensajes
Kino.Process.render_seq_trace(fn ->
_armando = self()
betty = spawn(fn ->
:ok
end)
send(betty, :"betty_la_amo")
end)
Los actores solo se comunican entre sí mediante mensajes; es lo único que puede modificar el “estado” del otro.
Kino.Process.render_seq_trace(fn ->
armando = self()
betty = spawn(fn ->
receive do
"te amo!" -> send(armando,"si? ha bueno")
end
end)
send(betty, "te amo!")
receive do
"tambien te amo" -> "toy feliz"
"si? ha bueno" -> "toy triste"
end
end)
Introducir nuevos actores es muy barato en términos computacionales (a diferencia de en la vida real).
Kino.Process.render_seq_trace(fn ->
_armando = self()
_betty = spawn(fn -> :ok end)
_marce = spawn(fn -> :ok end)
_patricia = spawn(fn -> :ok end)
end)
Todos se pueden hablar entre sí mientras tengan sus direcciones (PID); incluso incluso puedo crear todo ecomoda si queremos
for _ <- 0..1_000_000 do
spawn(fn -> :ok end)
end
de eso tan bueno… no dan tanto. :(
-
No es una bala de plata.
-
Puede ser muy bueno para manejar un servidor de videojuegos, pero no para escribir un videojuego: servidor de WoW en Elixir.
-
Hacer paralelismo o concurrencia sobre cualquier problema no lo va a solucionar más rápido necesariamente; incluso podría hacerlo más lento.
-
Escoge la herramienta adecuada para el trabajo adecuado, o incluso combina varias herramientas para lograr el mejor resultado posible.
-
No es particularmente bueno en procesamiento de señales (audio o imágenes), escribir drivers de sistemas operativos, o en general cosas que requieran muy bajo nivel y operaciones de alto rendimiento en el espacio numérico, aunque se está haciendo un esfuerzo para cambiar esto, en especial para apuntar al público de machine learning.
-
A pesar de sus limitantes, existen herramientas curiosas, como 🤯 modelado 3D.
Casos de uso exitoso
En 2016, WhatsApp alcanzó más de mil millones de usuarios y tenía las siguientes estadísticas de carga:
- 42 mil millones de mensajes enviados diariamente.
- 1.6 mil millones de imágenes enviadas diariamente.
- 250 millones de videos enviados diariamente.
- Tiene uno de los clústeres más grandes conocidos de Erlang.
Discord
Fue de los primeros que le apostó a Elixir. En 2017 tenían 5 millones de usuarios concurrentes y millones de eventos por segundo.
Menciones honoríficas
- Helium: Blockchain que opera principalmente en IoT devices, y gran parte de su core está escrito en Erlang.
- Sonic Pi: Programa para hacer música con código. El “orquestador de instrumentos” está escrito en Elixir.
- Farmbot: Proyecto de agricultura de precisión. Las máquinas de FarmBot utilizan tecnología IoT para facilitar a los agricultores la gestión remota de sus jardines.
- Bancolombia for the win (algún día): le están apostando a usar Elixir:
- Touring Live: Aplicación en la que le das una foto de un lugar turístico y te da detalles o una visita guiada.
Frameworks
WEB/creación de aplicaciones
- Phoenix: El default para aplicaciones web.
- Ash: No es solo orientado a aplicaciones web, es más algo opinado para construir la capa de aplicación.
Machine learning
- NX: Busca compilar nativamente un subset de Elixir y apuntar a las GPUs.
{:ok, model_info} = Bumblebee.load_model({:hf, "openai/whisper-tiny"})
{:ok, featurizer} = Bumblebee.load_featurizer({:hf, "openai/whisper-tiny"})
{:ok, tokenizer} = Bumblebee.load_tokenizer({:hf, "openai/whisper-tiny"})
{:ok, generation_config} = Bumblebee.load_generation_config({:hf, "openai/whisper-tiny"})
generation_config = Bumblebee.configure(generation_config, max_new_tokens: 100)
serving =
Bumblebee.Audio.speech_to_text_whisper(
model_info,
featurizer,
tokenizer,
generation_config,
compile: [batch_size: 4],
chunk_num_seconds: 30,
timestamps: :segments,
stream: true,
defn_options: [compiler: EXLA]
)
audio_input = Kino.Input.audio("Audio", sampling_rate: featurizer.sampling_rate)
form = Kino.Control.form([audio: audio_input], submit: "Run")
frame = Kino.Frame.new()
Kino.listen(form, fn %{data: %{audio: audio}} ->
if audio do
audio =
audio.file_ref
|> Kino.Input.file_path()
|> File.read!()
|> Nx.from_binary(:f32)
|> Nx.reshape({:auto, audio.num_channels})
|> Nx.mean(axes: [1])
Kino.Frame.render(frame, Kino.Text.new("(Start of transcription)", chunk: true))
for chunk <- Nx.Serving.run(serving, audio) do
[start_mark, end_mark] =
for seconds <- [chunk.start_timestamp_seconds, chunk.end_timestamp_seconds] do
seconds |> round() |> Time.from_seconds_after_midnight() |> Time.to_string()
end
text = "
#{start_mark}-#{end_mark}: #{chunk.text}"
Kino.Frame.append(frame, Kino.Text.new(text, chunk: true))
end
Kino.Frame.append(frame, Kino.Text.new("\n(End of transcription)", chunk: true))
end
end)
Kino.Layout.grid([form, frame], boxed: true, gap: 16)
NERVES
Framework para sistemas embebidos.
LiveNative
Correr aplicaciones de elixir nativamente (Orientado a cross platform development)
Recursos Recomendados
Exercism.io: Aprende Elixir haciendo ejercicios de programación. Está muy orientado a enseñarte las particularidades del lenguaje (no es programación competitiva) 🌟
Curso completo self-paced de programación funcional en Elixir
Colorario
- Introduciendo tipos en un lenguaje no tipado: Gradual Typing
- Metaprogramación
- OTP
Preguntas y respuesta?
- como funciona el hot code swaping?
- es beam una maquina jhon von newman
- por que eligiria a elixir sobre erlang?