Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

BEAM ecosystem

talk.livemd

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.

erlang

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

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

Modelo de actores

Los actores son la unidad básica de computación.

actores_1

Kino.Process.render_seq_trace(fn ->
  _armando = self()

  _betty = spawn(fn ->
    :ok
  end)

end)

Los actores se comunican entre si enviandose mensajes

actores 2

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.

actores 3

actores 4

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).

actores 5

actores 6

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

Whatsapp

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

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

Elixir in Action

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?

Referencias