{"id":32173593,"url":"https://github.com/volnyvolnyvolny/agent_map","last_synced_at":"2026-06-08T14:32:11.152Z","repository":{"id":57478761,"uuid":"130062140","full_name":"volnyvolnyvolny/agent_map","owner":"volnyvolnyvolny","description":"AgentMap can be seen as a stateful Map that parallelize operations made on different keys. Basically, it can be used as a memoization, computational framework, simple in-memory transactional key-value store and, sometimes, as an alternative to GenServer.","archived":false,"fork":false,"pushed_at":"2021-02-10T17:31:25.000Z","size":636,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-23T06:18:19.319Z","etag":null,"topics":["agent","elixir","map"],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/agent_map","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/volnyvolnyvolny.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-18T12:54:39.000Z","updated_at":"2026-02-09T23:46:39.000Z","dependencies_parsed_at":"2022-09-17T04:20:48.031Z","dependency_job_id":null,"html_url":"https://github.com/volnyvolnyvolny/agent_map","commit_stats":null,"previous_names":["zergera/agent_map"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/volnyvolnyvolny/agent_map","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/volnyvolnyvolny%2Fagent_map","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/volnyvolnyvolny%2Fagent_map/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/volnyvolnyvolny%2Fagent_map/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/volnyvolnyvolny%2Fagent_map/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/volnyvolnyvolny","download_url":"https://codeload.github.com/volnyvolnyvolny/agent_map/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/volnyvolnyvolny%2Fagent_map/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34067348,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","elixir","map"],"created_at":"2025-10-21T18:59:19.095Z","updated_at":"2026-06-08T14:32:11.146Z","avatar_url":"https://github.com/volnyvolnyvolny.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AgentMap\n\n`AgentMap` can be seen as a stateful `Map` that parallelize operations made on\ndifferent keys.\n\nFor instance, execution of this code:\n\n```elixir\nmap =                                         #  am =\n  %{a: 1, b: 1}                               #    %{a: 1, b: 1}\n  |\u003e Map.new()                                #    |\u003e AgentMap.new()\n  |\u003e Map.update!(:a, \u0026(sleep(10) \u0026\u0026 \u00261 + 1))  #    |\u003e AgentMap.cast(:a, …)\n  |\u003e Map.update!(:b, \u0026(sleep(10) \u0026\u0026 \u00261 + 1))  #    |\u003e AgentMap.cast(:b, …)\n                                              #\nMap.get(map, :a) == 2                         #  AgentMap.get(am, :a) == 2\nMap.get(map, :b) == 2                         #  AgentMap.get(am, :b) == 2\n```\n\nwill take about `20` ms. While the following is twice as fast due to\nparallelization:\n\n```elixir\nam =\n  %{a: 1, b: 1}\n  |\u003e AgentMap.new()\n  |\u003e AgentMap.cast(:a, \u0026(sleep(10) \u0026\u0026 \u00261 + 1))\n  |\u003e AgentMap.cast(:b, \u0026(sleep(10) \u0026\u0026 \u00261 + 1))\n                          \nAgentMap.get(am, :a) == 2 \nAgentMap.get(am, :b) == 2 \n```\n\nBasically, `AgentMap` can be used as a memoization, computational framework and,\nsometimes, as an alternative to `GenServer`. `AgentMap` supports operations made\non a group of keys ([\"multi-key\" calls](AgentMap.Multi.html)).\n\nSee [documentation](https://hexdocs.pm/agent_map) for the details.\n\n## Examples\n\n`AgentMap` can be started in an `Agent` manner:\n\n```elixir\niex\u003e {:ok, pid} = AgentMap.start_link()\niex\u003e pid\n...\u003e |\u003e AgentMap.put(:a, 1)\n...\u003e |\u003e AgentMap.get(:a)\n1\n```\n\nOr similarly to an ordinary `Map`:\n\n```elixir\niex\u003e am = AgentMap.new(a: 42, b: 24)\niex\u003e AgentMap.get(am, :a)\n42\niex\u003e AgentMap.keys(am)\n[:a, :b]\niex\u003e am\n...\u003e |\u003e AgentMap.update(:a, \u0026 \u00261 + 1)\n...\u003e |\u003e AgentMap.update(:b, \u0026 \u00261 - 1)\n...\u003e |\u003e AgentMap.take([:a, :b])\n%{a: 43, b: 23}\n```\n\nFunction `new/1` creates a special `%AgentMap{}` struct that is compatible with\n`Enumerable` and `Collectable` protocols.\n\n```elixir\niex\u003e {:ok, pid} = AgentMap.start_link()\niex\u003e am = AgentMap.new(pid)\n...\u003e Enum.empty?(am)\ntrue\n#\niex\u003e Enum.into([a: 1, b: 2], am)\niex\u003e AgentMap.take(am, [:a, :b])\n...\u003e %{a: 1, b: 2}\n```\n\nMore complicated example involves memoization:\n\n```elixir\ndefmodule Calc do\n  def fib(0), do: 0\n  def fib(1), do: 1\n  def fib(n) when n \u003e= 0 do\n    unless GenServer.whereis(Calc) do\n      AgentMap.start_link([], name: Calc)\n      fib(n)\n    else\n      AgentMap.get_and_update(Calc, n, fn\n        nil -\u003e\n          # This calculation will be made in a separate\n          # worker process.\n          res = fib(n - 1) + fib(n - 2)\n          # Return `res` and set it as a new value.\n          {res, res}\n\n        _value -\u003e\n          # Change nothing, return current value.\n          :id\n      end)\n    end\n  end\nend\n```\n\nTake a look at the\n[test/memo.ex](https://github.com/zergera/agent_map/blob/master/test/memo.ex).\n\n`AgentMap` provides a possibility to make multi-key calls (operations on\nmultiple keys). Let's see an accounting demo:\n\n```elixir\ndefmodule Account do\n  def start_link() do\n    AgentMap.start_link([], name: __MODULE__)\n  end\n\n  def stop() do\n    AgentMap.stop(__MODULE__)\n  end\n\n  @doc \"\"\"\n  Returns `{:ok, balance}` or `:error` in case there is no\n  such account.\n  \"\"\"\n  def balance(account) do\n    AgentMap.fetch(__MODULE__, account)\n  end\n\n  @doc \"\"\"\n  Withdraws money. Returns `{:ok, new_amount}` or `:error`.\n  \"\"\"\n  def withdraw(account, amount) do\n    AgentMap.get_and_update(__MODULE__, account, fn\n      nil -\u003e\n        # no such account\n        {:error}\n\n      balance when balance \u003e amount -\u003e\n        balance = balance - amount\n        {{:ok, balance}, balance}\n\n      _balance -\u003e\n        # Returns `:error`, while not changing value.\n        {:error}\n    end)\n  end\n\n  @doc \"\"\"\n  Deposits money. Returns `{:ok, new balance}`.\n  \"\"\"\n  def deposit(account, amount) do\n    AgentMap.get_and_update(__MODULE__, account, fn\n      b -\u003e\n        {{:ok, b + amount}, b + amount}\n    end, initial: 0)\n  end\n\n  @doc \"\"\"\n  Trasfers money. Returns `:ok` or `:error`.\n  \"\"\"\n  def transfer(from, to, amount) do\n    AgentMap.Multi.get_and_update(__MODULE__, [from, to], fn\n      [nil, _] -\u003e {:error}\n\n      [_, nil] -\u003e {:error}\n\n      [b1, b2] when b1 \u003e= amount -\u003e\n        {:ok, [b1 - amount, b2 + amount]}\n\n      _ -\u003e {:error}\n    end)\n  end\n\n  @doc \"\"\"\n  Closes account. Returns `:ok` or `:error`.\n  \"\"\"\n  def close(account) do\n    AgentMap.pop(__MODULE__, account) \u0026\u0026 :ok || :error\n  end\n\n  @doc \"\"\"\n  Opens account. Returns `:ok` or `:error`.\n  \"\"\"\n  def open(account) do\n    AgentMap.get_and_update(__MODULE__, account, fn\n      nil -\u003e\n        # Sets balance to 0, while returning :ok.\n        {:ok, 0}\n\n      _balance -\u003e\n        # Returns :error, while not changing balance.\n        {:error}\n    end)\n  end\nend\n```\n\n## Installation\n\n`AgentMap` requires Elixir `v1.8` Add `:agent_map`to your list of dependencies\nin `mix.exs`:\n\n```elixir\ndef deps do\n    [{:agent_map, \"~\u003e 1.1\"}]\nend\n```\n\n## License\n\n[MIT](https://github.com/zergera/agent_map/blob/dev/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvolnyvolnyvolny%2Fagent_map","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvolnyvolnyvolny%2Fagent_map","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvolnyvolnyvolny%2Fagent_map/lists"}