What's hot in the Erlang world?

Thought it would be nice to have a general thread about what’s happening and what’s hot in Erlang :003:

Apart from Elixir, what do you think is hot in Erlang right now? Any frameworks? Tools? Books? Prominent users? Cool ways of doing things? :upside_down_face:

10 Likes

Nice thread! Exploring the possiblities for Erlang/Elixir usage in the bioinformatics field I stumbled upon the Cuneiform project:

Cuneiform is a functional programming language for large-scale data analysis workflows. It is open because it easily integrates foreign tools and libraries, e.g., Python libraries or command line tools. It is general because it has the expressive power of a functional programming language while automatically parallelizing and distributing program execution. Cuneiform uses distributed Erlang to scalably run in cluster and cloud environments.

It looks really interesting to me…

3 Likes

Here is a recent talk by Cuneiform’s author Jörgen Brandt about how he modelled distribution using Petri nets, introducing his gen_pnet library.

spock cvm2

4 Likes

Out of curiosity I wanted to learn more about petri nets, implementing gen_pnet in Elixir. From the above mentioned talk, I decided to pick this very simple net presented as an example of modelling a Light:

01

So, I came up with this module:

defmodule GenPnetLight do
  alias :gen_pnet, as: GenPnet
  @behaviour GenPnet

  @name __MODULE__

  # Client

  def start_link(), do: GenPnet.start_link({:local, @name}, @name, :ok, [])

  def net_state() do
    {:ok, on_tokens} = GenPnet.ls(@name, :on)
    {:ok, off_tokens} = GenPnet.ls(@name, :off)
    %{on: on_tokens, off: off_tokens}
  end

  def stats(), do: GenPnet.stats(@name)

  def reset_stats(), do: GenPnet.reset_stats(@name)

  def on(), do: GenPnet.call(@name, :on)

  def off(), do: GenPnet.call(@name, :off)

  def stop(), do: GenPnet.stop(@name)

  # Net structure callbacks

  def place_lst(), do: [:on, :off]

  def trsn_lst(), do: [:turn_on, :turn_off]

  def preset(:turn_on), do: [:off]

  def preset(:turn_off), do: [:on]

  def init_marking(_place, _usr_info), do: []

  def is_enabled(:turn_on, %{off: [:sig]}, _usr_info), do: true

  def is_enabled(:turn_off, %{on: [:sig]}, _usr_info), do: true

  def is_enabled(_trns, _mode, _usr_info), do: false

  def fire(:turn_on, %{off: [:sig]}, _usr_info), do: {:produce, %{on: [:sig]}}

  def fire(:turn_off, %{on: [:sig]}, _usr_info), do: {:produce, %{off: [:sig]}}

  # Actor interface callbacks

  def code_change(_oldVsn, net_state, _extra), do: {:ok, net_state}

  def handle_call(:on, _from, _net_state) do
    consume_map = %{off: [:sig]}
    produce_map = %{on: [:sig]}
    {:reply, :ok, consume_map, produce_map}
  end

  def handle_call(:off, _from, _net_state) do
    consume_map = %{on: [:sig]}
    produce_map = %{off: [:sig]}
    {:reply, :ok, consume_map, produce_map}
  end

  def handle_call(_request, _from, _net_state), do: {:reply, {:error, :bad_msg}}

  def handle_cast(_request, _net_state), do: {:noreply}

  def handle_info(_info, _net_state), do: {:noreply}

  def init(_netArg), do: []

  def terminate(_reason, _net_state), do: :ok

  def trigger(_place, _token, _netState), do: :pass
end

I can start the net instance with

> GenPnetLight.start_link()
{:ok, #PID<0.237.0>}

Inspect its state:

> GenPnetLight.net_state()
%{off: , on: }

Switch it on:

> GenPnetLight.on()
:ok

Inspect its stats (current/max/min timestamped transitions per second):

> GenPnetLight.stats()
{:stats, {:stat, 1575456353300381440, 232072.40659085635},
{:stat, 1575456351982252032, 249003.984063745},
{:stat, 1575456251358955520, 10.362319216776951}}

Stop it (killing the net instance process):

> GenPnetLight.stop()
:ok

With some surprise, once the net is given the first :on call, it enters an infinite loop, switching ~ 240000 times per second!

Not what I was expecting, but on a second thought it seems correct, for the given net structure…
But still I couldn’t figure out how to model a proper switch though!
Any hint is very welcome :slight_smile: Thanks for reading.

2 Likes

As the previous attempt left me more confused than else, I wanted to try to understand this things better, so I decided to do it by implementing the Cookie Vending Machine of the lib code example.

cvm2

And -spoiler- It helped me a lot!

I.e. Now I can see how the Signal place is holding all the machine’s cookie_box debits when the machine goes out of cookie_boxes, but still accepts coins.

So I felt the need to add a way to refill the storage and then I was amazed to observe how, soon after having refilled the storage, a number of previously sold but not delivered cookie_boxes were dropped in the Compartment. Wonderfull ! :slight_smile:

For the curious ones, here’s the CookieVendingMachine module with client’s insert_coin and remove_cookie_box functions and two servicing functions to refill the storage, and to remove cash -this one require a key to open the machine to get the coins! Also I added couple of side effects in the trigger callbacks as very minimalistic user feedback.

defmodule CookieVendingMachine do
  alias :gen_pnet, as: GenPnet
  @behaviour GenPnet

  @name __MODULE__

  # Service Client

  def start_link(), do: GenPnet.start_link({:local, @name}, @name, :ok, [])

  def marking(), do: GenPnet.marking(@name)

  def refill_storage(), do: GenPnet.call(@name, :refill_storage)

  def remove_cash(%{key: key}), do: GenPnet.call(@name, {:remove_cash, key})

  def stats(), do: GenPnet.stats(@name)

  def stop(), do: GenPnet.stop(@name)

  # Users Client 

  def insert_coin(), do: GenPnet.call(@name, :insert_coin)

  def remove_cookie_box(), do: GenPnet.call(@name, :remove_cookie_box)

  # Net structure callbacks

  def place_lst(), do: [:coin_slot, :cash_box, :signal, :storage, :compartment]

  def trsn_lst(), do: [:a, :b]

  def preset(:a), do: [:coin_slot]
  def preset(:b), do: [:signal, :storage]

  def init_marking(:storage, _usr_info), do: [:cookie_box, :cookie_box, :cookie_box]
  def init_marking(_place, _usr_info), do: []

  def is_enabled(:a, %{coin_slot: [:coin]}, _usr_info), do: true
  def is_enabled(:b, %{signal: [:sig], storage: [:cookie_box]}, _usr_info), do: true
  def is_enabled(_trns, _mode, _usr_info), do: false

  def fire(:a, _mode, _usr_info), do: {:produce, %{cash_box: [:coin], signal: [:sig]}}

  def fire(:b, _mode, _usr_info), do: {:produce, %{compartment: [:cookie_box]}}

  # Actor interface callbacks

  def code_change(_oldVsn, net_state, _extra), do: {:ok, net_state}

  def handle_call(:insert_coin, _from, _net_state) do
    # %{:reply, reply, consume_map, produce_map}
    {:reply, :ok, %{}, %{coin_slot: [:coin]}}
  end

  def handle_call(:remove_cookie_box, _from, net_state) do
    case GenPnet.get_ls(:compartment, net_state) do
      [] -> {:reply, {:error, :empty_compartment}}
      [_ | _] -> {:reply, :ok, %{compartment: [:cookie_box]}, %{}}
    end
  end

  def handle_call(:refill_storage, _form, net_state) do
    case GenPnet.get_ls(:storage, net_state) do
      [] ->
        {:reply, :ok, %{}, %{storage: [:cookie_box, :cookie_box, :cookie_box]}}

      [_ | _] ->
        {:reply, {:error, :storage_not_empty_yet}}
    end
  end

  def handle_call({:remove_cash, key}, _form, net_state) do
    case key do
      "secret" ->
        cash = GenPnet.get_ls(:cash_box, net_state)
        {:reply, cash, %{cash_box: cash}, %{}}

      _ ->
        {:reply, {:error, :unauthorized}}
    end
  end

  def handle_call(_request, _from, _net_state), do: {:reply, {:error, :bad_msg}}

  def handle_cast(_request, _net_state), do: {:noreply}

  def handle_info(_info, _net_state), do: {:noreply}

  def init(_netArg), do: []

  def terminate(_reason, _net_state), do: :ok

  def trigger(:coin_slot, :coin, _netState) do
    IO.puts("🛎 > You hear the sound of a :coin falling in the :cash_box.")
    :pass
  end

  def trigger(:compartment, :cookie_box, _netState) do
    IO.puts("🍪 > A :cookie_box has fallen in the :compartment! Now you can remove_cookie_box().")
    :pass
  end

  def trigger(_compartment, _token, _netState), do: :pass
end

I decided to document this very brief exploration of mine mostly as a way of learning but also with the hope that others could be interested in the gen_pnet library. Thanks again. :slightly_smiling_face:

5 Likes