How to define static variable in elixir? How to do counter?

Hi,
I’m kind new with elixir code and I was wondering if there away to create counter in it?
E,g,

    x=0;
    depf sum() {
     x=x+1;
   end 
   depf callsum() {
    loop     
      sum();
    } 

Thanks,
SL

In general there is nothing comparable to a “static variable” as you are used to it from C. This would violate the rules of immutability.

The concept that comes closest were a longrunning process which holds the counters value in its state.

But before explaining how to do that exactly, I’d like to ask what you need that counter for. Perhaps there are much more idiomatic ways to solve your particular problem than a stateholding process.

2 Likes

Thanks for your response.
Well, I’m trying to see how many atoms created in my apps.
I tried the erlang command, but I’m running OTA 19 and not 20. On 20 there’s build-in function for it.
So I thought to sum how many atom I have and print it.

How’d you know if you have created a new one or re-used an old one? There’s not really a way to check that in advance.

In OTP 19 though the following erlang code (shamelessly borrowed from https://engineering.klarna.com/monitoring-erlang-atoms-c1d6a741328e) can help to retrieve the currents atom table size:

-module(atom_table).

-export([count/0]).

count() ->
  Info      = erlang:system_info(info),
  Chunks    = binary:split(Info, <<"=">>, [global]),
  [TabInfo] = [X || <<"index_table:atom_tab", X/binary>> <- Chunks],
  Lines     = binary:split(TabInfo, <<"\n">>, [global]),
  Chunks2   = [binary:split(L, <<": ">>) || L <- Lines, L =/= <<>>],
  binary_to_integer(proplists:get_value(<<"entries">>, Chunks2)).

It roughly translates to the following elixir code:

defmodule AtomTable do
  @tab_prefix "index_table:atom_tab"

  def count() do
    data = :info
    |> :erlang.system_info()
    |> String.split("=")
    |> Enum.filter(fn
      @tab_prefix <> _ -> true
      _ -> false
    end)
    |> hd()
    |> rm_prefix()
    |> String.split("\n")
    |> Enum.reject(&String.empty?/1)
    |> Enum.map(&String.split(&1, ": "))
    
    "entries"
    |> :proplist.get_value(data)
    |> String.to_integer()
  end

  defp rm_prefix(@tab_prefix <> str), do: str
end

The translation is very rough and has its edges (also it is untested). Perhaps you just put the erlang module into src/atom_table.erl in your project and call it as :atom_table.count().

2 Likes

I’n not sure if this trick still works in Erlang 20, but you can actually enumerate all the currently defined Atoms in Erlang 19.

A more verbose and pedestrian version:

## file: atom_info.ex
defmodule AtomInfo do
  @info_begin <<"index_table:atom_tab">>
  @info_end <<"=">>
  @info_keys [:size, :limit, :entries]
  @info_names Enum.reduce(@info_keys, %{}, &(Map.put &2, (Atom.to_string &1), &1))

  def props do
    all_info = :erlang.system_info :info
    with {:ok, begin} <- (atom_info_begin all_info),
         {:ok, length} <- (atom_info_length all_info, begin) do

      # extract the atom_tab items from info
      # and put the desired ones in a map
      props =
        all_info
        |> Kernel.binary_part(begin, length)
        |> Kernel.to_string()
        |> String.split("\n", trim: true)
        |> Enum.reduce(%{}, &put_props/2)

      {:ok, props}

    else
      _ ->
        {:error, :undefined}
    end
  end

  defp atom_info_begin(info) do
    case :binary.match info, @info_begin do
      {start, length} ->
        {:ok, start + length}
      _ ->
        {:error, :undefined}
    end
  end

  defp atom_info_length(info, begin) do
    remain = (Kernel.byte_size info) - begin
    case :binary.match info, @info_end, [{:scope, {begin, remain}}] do
      {end_pos, _length} ->
        {:ok, end_pos - begin}
      _ ->
        {:ok, remain}
    end
  end

  defp put_props(item, props) do
    name_int_value = ~r/^([a-z]+)\s*:\s*(\d+)$/
    with [_full, name, value] <- Regex.run(name_int_value, item),
         {:ok, key} <- Map.fetch(@info_names, name) do
      Map.put props, key, (String.to_integer value)

    else
      _->
        props # i.e. don't add if match failed or name not listed
    end
  end

end
$ iex
iex(1)> c("atom_info.ex")
[AtomInfo]
iex(2)> AtomInfo.props 
{:ok, %{entries: 13232, limit: 1048576, size: 13312}}
iex(3)> 
2 Likes

Everyone so far is talking about counting atoms, but it seems to me that the more fundamental question here is about how to do iteration.

1 Like

Iterating all existing atoms has been linked by @bbense, that that hasn’t been asked for…

The question was, how one could check how many atoms there are currently registered to avoid creating new ones when a certain threshold is reached,


Besides of what was asked or not asked… The easiest way to avoid overflowing the atom-table is to not create any new atoms, but those that are used literally in the sources…

thanks for you implementation its very helpful. :icon_idea: