AgentMap v1.1.0-rc.1 AgentMap

AgentMap can be seen as a stateful Map that parallelize operations made on different keys. Basically, it can be used as a cache, memoization, computational framework and, sometimes, and as an alternative to GenServer. AgentMap supports operations made on a group of keys (“multi-key” calls).

Examples

Create and use it as an ordinary Map (new/0, new/1 and new/2):

iex> am = AgentMap.new(a: 42, b: 24)
iex> AgentMap.get(am, :a)
42
iex> AgentMap.keys(am)
[:a, :b]
iex> am
...> |> AgentMap.update(:a, & &1 + 1)
...> |> AgentMap.update(:b, & &1 - 1)
...> |> AgentMap.to_map()
%{a: 43, b: 23}
#
iex> Enum.count(am)
2

or in an Agent manner (start/2, start_link/2):

iex> {:ok, pid} = AgentMap.start_link()
iex> pid
...> |> AgentMap.put(:a, 1)
...> |> AgentMap.get(:a)
1

Struct %AgentMap{} allows to use Enumerable and Collectable protocols:

iex> {:ok, pid} = AgentMap.start_link()
iex> am = AgentMap.new(pid)
...> Enum.empty?(am)
true
#
iex> Enum.into([a: 1, b: 2], am)
iex> AgentMap.to_map(am)
%{a: 1, b: 2}

See README for memoization and accounting examples.

Priority (:!)

Most of the functions support !: priority option to make out-of-turn (“priority”) calls.

Priority can be given as a non-negative integer or alias. Aliases are: :min | :low = 0, :avg | :mid = 255, :max | :high = 65535. Relative value are also acceptable, for ex.: {:max, -1} = 65534.

iex> AgentMap.new(state: :ready)
...> |> sleep(:state, 10)                                       # 0 — creates a worker
...> |> cast(:state, fn :go!    -> :stop   end)                 # :   ↱ 3
...> |> cast(:state, fn :steady -> :go!    end, !: :max)        # : ↱ 2 :
...> |> cast(:state, fn :ready  -> :steady end, !: {:max, +1})  # ↳ 1   :
...> |> get(:state)                                             #       ↳ 4
:stop

Also, !: :now option can be given in get/4, get_lazy/4 or take/3 to instruct AgentMap to make execution using a separate Task and current values. Calls fetch!/3, fetch/3, values/2, to_map/2 and has_key?/3 use this option by default:

iex> am =
...>   AgentMap.new(key: 1)
iex> am
...> |> sleep(:key, 20)                 # 0        — creates a sleepy worker
...> |> put(:key, 42)                   # ↳ 1 ┐
...>                                    #     :
...> |> fetch(:key)                     # 0   :
{:ok, 1}                                #     :
iex> get(am, :key, & &1 * 99, !: :now)  # 0   :
99                                      #     :
iex> get(am, :key, & &1 * 99)           #     ↳ 2
4158

How it works

Underneath it’s a GenServer that holds a Map. When a state changing call (update/4, get_and_update/4, …) is first made for a key, a special temporary worker-process is spawned. All subsequent calls are forwarded to the message queue of this worker, which respects the order of incoming new calls. Worker executes them in a sequence, except for get/4 calls, which are processed as a parallel Tasks. A worker will die after ~20 ms of inactivity.

For example:

iex> am = AgentMap.new(a: 1)
iex> am
...> |> cast(:a, & &1 + 1)              # new worker is spawned for `:a` and …
...>                                    # callback `& &1 + 1` is added to its queue
...> |> get(:a)                         # callback `& &1` is added to the same queue
2                                       # now all callbacks are invoked
iex> sleep(40)                          # worker is inactive for > 20 ms…
...>                                    # …so it dies
iex> get(am, :a, & &1 + 1)              # `& &1 + 1` is executed in a Task process…
...>                                    # …no worker is spawned
3

or:

iex> am = AgentMap.new(a: 1)            # server   worker  queue
iex> am                                 # %{a: 1}  —       —
...> |> cast(:a, & &1 + 1)              # %{}    → 1       [& &1 + 1]
...> |> get(:a)                         # %{}      1       [& &1 + 1, get]
2                                       # %{}      2       []
                                        #
iex> sleep(40)                          # …worker dies
iex> get(am, :a, & &1 + 1)              # %{a: 2}  —       —
3

Sometimes, like in the above example, it’s more expensive to spawn a new worker and handle logic related to its death than to execute callback on server. For this particular case tiny: true option can be provided to get_and_update/4, update/4, update!/4, put_new_lazy/4 and cast/4 calls. When it’s given, callback is invoked inside GenServer’s loop and no additional workers are spawned:

iex> am = AgentMap.new(a: 1)
iex> am
...> |> cast(:a, & &1 + 1, tiny: true)  # callback `& &1 + 1` is executed by server
...> |> get(:a)                         # server returns the current value
2                                       #
iex> get(am, :a, & &1 + 1)              # `& &1 + 1` is executed in a Task process
3

Be aware, that when invoking callbacks, server could not handle any other requests. For example, this:

iex> am = AgentMap.new(a: 42)           # {:ok, pid} = Agent.start(fn -> %{a: 42} end)
iex> am                                 # pid
...> |> cast(:b, fn _ ->                # |> Agent.cast(fn _ ->
...>      sleep(50)                     #      sleep(50)
...>      24                            #      %{a: 42, b: 24}
...>    end, tiny: true)                #    end)
...> |> get(:a)                         #
42                                      #
iex> get(am, :b)                        # Agent.get(pid, & &1)
24                                      # %{a: 42, b: 24}

will be handled in around of 50 ms. In this case AgentMap behaves exactly like Agent with a map given as a state.

Other

AgentMap is bound to the same name registration rules as GenServers, see GenServer documentation for details.

Finally, note that use AgentMap defines a child_spec/1 function, allowing the defined module to be put under a supervision tree. The generated child_spec/1 can be customized with the following options:

  • :id - the child specification id, defauts to the current module;
  • :start - how to start the child process (defaults to calling __MODULE__.start_link/1);
  • :restart - when the child should be restarted, defaults to :permanent;
  • :shutdown - how to shut down the child.

For example:

use AgentMap, restart: :transient, shutdown: 10_000

See Supervisor docs.

Link to this section Summary

Types

AgentMap instance

Return values for start and start_link functions

Functions

Performs “fire and forget” update/4 call with GenServer.cast/2

Fetches the value for a specific key, erroring out if instance doesn’t contain key at the moment

Fetches the current value for a specific key

Returns the value for a specific key

Gets a value via the given fun

Gets the value for key and updates it, all in one pass

Returns whether the given key exists at the moment

Returns all keys

Returns a new instance of AgentMap

Starts an AgentMap via start_link/1 function

Creates an AgentMap instance from enumerable via the given transformation function

Removes and returns the value associated with key

Puts the given value under key, unless the entry already exists

Evaluates fun and puts the result under key, unless it is already present

Alters the value stored under key, but only if key already exists

Synchronously stops the AgentMap instance with the given reason

Returns a map with keys and current values

Returns a current Map representation

Updates known key with the given function

Updates key with the given fun

Returns current values stored by AgentMap instance

Link to this section Types

Link to this type alias()
alias() :: :low | :mix | :mid | :avg | :high | :max
Link to this type am()
am() :: pid() | {atom(), node()} | name() | %AgentMap{pid: term()}

AgentMap instance

Link to this type default()
default() :: initial()
Link to this type delta()
delta() :: integer()
Link to this type initial()
initial() :: value()
Link to this type key()
key() :: term()
Link to this type name()
name() :: atom() | {:global, term()} | {:via, module(), term()}
Link to this type on_start()
on_start() ::
  {:ok, pid()} | {:error, {:already_started, pid()} | [{key(), reason()}]}

Return values for start and start_link functions

Link to this type priority()
priority() :: alias() | {alias(), delta()} | :now | non_neg_integer()
Link to this type reason()
reason() :: term()
Link to this type value()
value() :: term()

Link to this section Functions

Link to this function cast(am, key, fun, opts \\ [!: :avg])
cast(am(), key(), (value() | initial() -> value()), keyword()) :: am()

Performs “fire and forget” update/4 call with GenServer.cast/2.

Returns without waiting for the actual update to finish.

Options

  • :initial, nil — value for key if it’s missing;

  • !: priority, :avg;

  • tiny: true — to execute fun on server if it’s possible.

Examples

iex> AgentMap.new(a: 1)
...> |> sleep(:a, 20)                     # 0 — creates a worker
...> |> cast(:a, fn 2 -> 3 end)           # : ↱ 2
...> |> cast(:a, fn 1 -> 2 end, !: :max)  # ↳ 1 :
...> |> cast(:a, fn 3 -> 4 end, !: :min)  #     ↳ 3
...> |> get(:a)                           #       ↳ 4
4
Link to this function delete(am, key, opts \\ [cast: true, !: :max])
delete(am(), key(), keyword()) :: am()

Deletes entry for key.

Returns without waiting for the actual delete to occur.

Default priority for this call is :max.

Options

  • cast: false — to return only after the actual removal;

  • !: priority, :max;

  • :timeout, 5000.

Examples

iex> %{a: 1, b: 2}
...> |> AgentMap.new()
...> |> delete(:a)
...> |> to_map()
%{b: 2}
Link to this function drop(am, keys, opts \\ [cast: true])
drop(am(), Enumerable.t(), keyword()) :: am()

Drops given keys.

Returns without waiting for the actual drop to occur.

This call has a fixed priority {:avg, +1}.

Options

  • cast: false — to return only after delete is finished for all keys;

  • :timeout, 5000.

Examples

iex> %{a: 1, b: 2, c: 3}
...> |> AgentMap.new()
...> |> drop([:b, :d], cast: false)
...> |> to_map()
%{a: 1, c: 3}
Link to this function fetch!(am, key, opts \\ [!: :now])
fetch!(am(), key(), keyword() | timeout()) :: value() | no_return()

Fetches the value for a specific key, erroring out if instance doesn’t contain key at the moment.

Returns current value or raises a KeyError.

See fetch/3.

Options

  • !: priority, :now — to return only when calls with higher priorities are finished for this key;

  • :timeout, 5000.

Examples

iex> am = AgentMap.new(a: 1)
iex> fetch!(am, :a)
1
iex> fetch!(am, :b)
** (KeyError) key :b not found
Link to this function fetch(am, key, opts \\ [!: :now])
fetch(am(), key(), keyword() | timeout()) :: {:ok, value()} | :error

Fetches the current value for a specific key.

Returns {:ok, value} or :error if key is not present.

Options

  • !: priority, :now — to return only when calls with higher priorities are finished for this key;

  • :timeout, 5000.

Examples

iex> am = AgentMap.new(a: 1)
iex> fetch(am, :a)
{:ok, 1}
iex> fetch(am, :b)
:error

By default, this call returns the current key value. As so, any pending operations will have no effect:

iex> am = AgentMap.new()
iex> am
...> |> sleep(:a, 20)        # 0        — creates a worker for this key
...> |> put(:a, 42)          # ↳ 1 ┐    — …pending
...>                         #     :
...> |> fetch(:a)            # 0   :
:error                       #     :
#                                  :
iex> fetch(am, :a, !: :avg)  #     ↳ 2
{:ok, 42}
Link to this function get(am, key, opts \\ [!: :min])
get(am(), key(), keyword()) :: value() | default()

Returns the value for a specific key.

This call has the :min priority. As so, the value is retrived only after all other calls for key are completed.

See get/4, AgentMap.Multi.get/3.

Options

  • :default, nil — value to return if key is missing;

  • !: priority, :min;

  • :timeout, 5000.

Examples

iex> am = AgentMap.new(Alice: 42)
iex> get(am, :Alice)
42
iex> get(am, :Bob)
nil

By default, get/4 has the :min priority:

iex> AgentMap.new(a: 42)
...> |> sleep(:a, 20)           # 0      — creates a worker
...> |> put(:a, 0)              # : ↱ 2  — !: :max
...> |> get(:a, !: {:max, +1})  # ↳ 1
42
Link to this function get(am, key, fun, opts)
get(am(), key(), (value() | default() -> get), keyword() | timeout()) :: get
when get: var

Gets a value via the given fun.

A callback fun is sent to an instance that invokes it, passing as an argument the value associated with key. The result of an invocation is returned from this function. This call does not change value, and so, workers execute a series of get-calls as a parallel Tasks.

Options

  • :default, nil — value for key if it’s missing;

  • !: priority, :avg;

  • !: :now — to execute call in a separate Task (passing current value), spawned from server:

    iex> am = AgentMap.new()
    iex> sleep(am, :key, 40)
    iex> for _ <- 1..100 do
    ...>   Task.async(fn ->
    ...>     get(am, :key, fn _ -> sleep(40) end, !: :now)
    ...>   end)
    ...> end
    iex> sleep(10)
    iex> get_prop(am, :processes)
    102
  • :timeout, 5000.

Examples

iex> am = AgentMap.new()
iex> get(am, :Alice, & &1)
nil
iex> am
...> |> put(:Alice, 42)
...> |> get(:Alice, & &1 + 1)
43
iex> get(am, :Bob, & &1 + 1, default: 0)
1
Link to this function get_and_update(am, key, fun, opts \\ [!: :avg])
get_and_update(
  am(),
  key(),
  (value() | initial() -> {ret} | {ret, value()} | :pop | :id),
  keyword() | timeout()
) :: ret | value() | initial()
when ret: var

Gets the value for key and updates it, all in one pass.

The fun is sent to an AgentMap that invokes it, passing the value for key. A callback can return:

  • {ret, new value} — to set new value and retrive “ret”;
  • {ret} — to retrive “ret” value;
  • :pop — to retrive current value and remove key;
  • :id — to just retrive current value.

For example, get_and_update(account, :Alice, &{&1, &1 + 1_000_000}) returns the balance of :Alice and makes the deposit, while get_and_update(account, :Alice, &{&1}) just returns the balance.

This call creates a temporary worker that is responsible for holding queue of calls awaiting execution for key. If such a worker exists, call is added to the end of its queue. Priority can be given (:!), to process call out of turn.

See Map.get_and_update/3.

Options

  • :initial, nil — value for key if it’s missing;

  • tiny: true — to execute fun on server if it’s possible;

  • !: priority, :avg;

  • :timeout, 5000.

Examples

iex> am = AgentMap.new(a: 42)
...>
iex> get_and_update(am, :a, &{&1, &1 + 1})
42
iex> get(am, :a)
43
iex> get_and_update(am, :a, fn _ -> :pop end)
43
iex> has_key?(am, :a)
false
iex> get_and_update(am, :a, fn _ -> :id end)
nil
iex> has_key?(am, :a)
false
iex> get_and_update(am, :a, &{&1, &1})
nil
iex> has_key?(am, :a)
true
iex> get_and_update(am, :b, &{&1, &1}, initial: 42)
42
iex> has_key?(am, :b)
true
Link to this function has_key?(am, key, opts \\ [!: :now])
has_key?(am(), key(), keyword() | timeout()) :: boolean()

Returns whether the given key exists at the moment.

See fetch/3.

Options

  • !: priority, :now;

  • :timeout, 5000.

Examples

iex> am = AgentMap.new(a: 1)
iex> has_key?(am, :a)
true
iex> has_key?(am, :b)
false
iex> am
...> |> delete(:a)
...> |> has_key?(:a)
false

By default, this call returns a key value at the moment. As so, any pending operations will have no effect:

iex> AgentMap.new(a: 1)
...> |> sleep(:a, 20)          # — creates a worker for this key
...> |> delete(:a)             # — …pending
...> |> has_key?(:a)
true

use :! option to bypass:

iex> AgentMap.new(a: 1)
...> |> sleep(:a, 20)          # 0
...> |> delete(:a)             # ↳ 1
...> |> has_key?(:a, !: :min)  #   ↳ 2
false
Link to this function keys(am)
keys(am()) :: [key()]

Returns all keys.

Examples

iex> %{a: 1, b: nil, c: 3}
...> |> AgentMap.new()
...> |> keys()
[:a, :b, :c]
Link to this function new()
new() :: am()

Returns a new instance of AgentMap.

Examples

iex> AgentMap.new()
...> |> Enum.empty?()
true
Link to this function new(enumerable)
new(Enumerable.t() | am()) :: am()

Starts an AgentMap via start_link/1 function.

As an argument, enumerable with keys and values may be provided or the PID of an already started AgentMap.

Returns a new instance of AgentMap wrapped in a %AgentMap{}. This struct allows to use Enumerable and Collectable protocols.

Examples

iex> am = AgentMap.new(a: 42, b: 24)
iex> get(am, :a)
42
iex> keys(am)
[:a, :b]

iex> {:ok, pid}
...>   = AgentMap.start_link()
iex> pid
...> |> AgentMap.new()
...> |> put(:a, 1)
...> |> get(:a)
1

iex> {:ok, pid}
...>   = AgentMap.start_link()
iex> pid
...> |> AgentMap.new()
...> |> Enum.empty?()
true

iex> {:ok, pid}
...>   = AgentMap.start_link()
...>
iex> %{a: 2, b: 3}
...> |> Enum.into(AgentMap.new(pid))
...> |> to_map()
%{a: 2, b: 3}
Link to this function new(enumerable, transform)
new(Enumerable.t(), (term() -> {key(), value()})) :: am()

Creates an AgentMap instance from enumerable via the given transformation function.

Duplicated keys are removed; the latest one prevails.

Examples

iex> [:a, :b]
...> |> AgentMap.new(&{&1, to_string(&1)})
...> |> to_map()
%{a: "a", b: "b"}
Link to this function pop(am, key, default \\ nil, opts \\ [!: :avg])
pop(am(), key(), default(), keyword() | timeout()) :: value() | default()

Removes and returns the value associated with key.

If there is no such key, default is returned (nil).

Options

  • !: priority, :avg;

  • :timeout, 5000.

Examples

iex> am =
...>   AgentMap.new(a: 42, b: nil)
...>
iex> pop(am, :a)
42
iex> pop(am, :a)
nil
iex> pop(am, :a, :error)
:error
iex> pop(am, :b, :error)
nil
iex> pop(am, :b, :error)
:error
iex> Enum.empty?(am)
true
Link to this function put(am, key, value, opts \\ [cast: true, !: :max])
put(am(), key(), value(), keyword()) :: am()

Puts the given value under key.

Returns without waiting for the actual put.

Default priority for this call is :max.

Options

  • cast: false — to return after the actual put;

  • !: priority, :max;

  • :timeout, 5000.

Examples

iex> AgentMap.new()
...> |> put(:a, 42)
...> |> put(:b, 42)
...> |> to_map()
%{a: 42, b: 42}

By default, put/4 has the :max priority:

iex> AgentMap.new(a: 42)     #                 server    worker  queue
...> |> sleep(:a, 20)        #                 %{a: 42}  —       —
...>                         #
...> |> put(:a, 1, !: :min)  #     ↱ 3a ┐      %{}       42      [min]
...> |> put(:a, 2, !: :avg)  #  ↱ 2a    :      %{}       42      [avg, min]
...> |> put(:a, 3)           # 1a       :      %{}       42      [max, avg, min]
...> |> get(:a)              #          ↳ 4a   %{}       42      [max, avg, min, get]
1                            #                 %{}       1       []
                             #
                             #                 the worker dies after ~20 ms:
                             #                 %{a: 1}   —       —

but:

iex> AgentMap.new()          #                 %{}       —       —
...> |> put(:a, 1, !: :min)  # 1               %{a: 1}   —       —
...> |> put(:a, 2, !: :avg)  # ↳ 2             %{a: 2}   —       —
...> |> put(:a, 3)           #   ↳ 3           %{a: 3}   —       —
...> |> get(:a)              #     ↳ 4         %{a: 3}   —       —
3
Link to this function put_new(am, key, value, opts \\ [cast: true, !: :max])
put_new(am(), key(), value(), keyword()) :: am()

Puts the given value under key, unless the entry already exists.

Returns without waiting for the actual put.

Default priority for this call is :max.

See put/4.

Options

  • cast: false — to return after the actual put;

  • !: priority, :max;

  • :timeout, 5000.

Examples

iex> %{a: 1}
...> |> AgentMap.new()
...> |> put_new(:a, 42)
...> |> put_new(:b, 42)
...> |> to_map()
%{a: 1, b: 42}
Link to this function put_new_lazy(am, key, fun, opts \\ [cast: true, !: :max])
put_new_lazy(am(), key(), (() -> value()), keyword()) :: am()

Evaluates fun and puts the result under key, unless it is already present.

Returns without waiting for the actual put.

This function is useful in case you want to compute the value to put under key only if it is not already present (e.g., the value is expensive to calculate or generally difficult to setup and teardown again).

Default priority for this call is :max.

See put_new/4.

Options

  • cast: false — to return after the actual put;

  • !: priority, :max;

  • tiny: true — to execute fun on server if it’s possible;

  • :timeout, 5000.

Examples

iex> fun = fn ->
...>   # some expensive operation
...>   42
...> end
...>
iex> %{a: 1}
...> |> AgentMap.new()
...> |> put_new_lazy(:a, fun, cast: false)
...> |> put_new_lazy(:b, fun, cast: false)
...> |> to_map()
%{a: 1, b: 42}
Link to this function replace!(am, key, value, opts \\ [!: :avg])
replace!(am(), key(), value(), keyword() | timeout()) :: am()

Alters the value stored under key, but only if key already exists.

If key is not present, a KeyError exception is raised.

See update!/4.

Options

  • !: priority, :avg;

  • :timeout, 5000.

Examples

iex> am = AgentMap.new(a: 1, b: 2)
iex> am
...> |> replace!(:a, 3)
...> |> values()
[3, 2]
iex> replace!(am, :c, 2)
** (KeyError) key :c not found
Link to this function start(funs \\ [], opts \\ [max_processes: 5000])
start([{key(), (() -> any())}], GenServer.options()) :: on_start()

Starts an unlinked AgentMap instance.

See start_link/2 for details.

Examples

iex> err =
...>   AgentMap.start([a: 42,
...>                   b: fn -> sleep(:infinity) end,
...>                   c: fn -> raise "oops" end],
...>                   timeout: 10)
...>
iex> {:error, a: :badfun, b: :timeout, c: {e, _st}} = err
iex> e
%RuntimeError{message: "oops"}
Link to this function start_link(funs \\ [], opts \\ [max_processes: 5000])
start_link([{key(), (() -> any())}], GenServer.options()) :: on_start()

Starts a linked AgentMap instance.

Argument funs is a keyword that contains pairs {key, fun/0}. Each fun is executed in a separate Task and return an initial value for key.

Options

  • name: term — is used for registration as described in the module documentation;

  • :debug — is used to invoke the corresponding function in :sys module;

  • :spawn_opt — is passed as options to the underlying process as in Process.spawn/4;

  • :timeout, 5000AgentMap is allowed to spend at most the given number of milliseconds on the whole process of initialization or it will be terminated;

  • max_processes: pos_integer | :infinity | {pos_integer, pos_integer}, 5000 — a maximum number of processes instance can have. Limit can be “soft” — if it is exceeded, optimizations are applied; or “hard” — if it’s exceeded and no optimization can be made to spawn workers, all requests are handled in a server process, one by one. For example, max_processes: 100max_processes: {100, :infinity} mean 99 processes can be spawned before optimizations are made, and no hard-limitation is given.

Return values

If an instance is successfully created and initialized, the function returns {:ok, pid}, where pid is the PID of the server. If a server with the specified name already exists, the function returns {:error, {:already_started, pid}} with the PID of that process.

If one of the callbacks fails, the function returns {:error, [{key, reason}]}, where reason is :timeout, :badfun, :badarity, {:exit, reason} or an arbitrary exception.

Examples

iex> {:ok, pid} =
...>   AgentMap.start_link(k: fn -> 42 end)
iex> get(pid, :k)
42
iex> get_prop(pid, :max_processes)
{5000, :infinity}

— starts server with a predefined single key :k.

iex> AgentMap.start(k: 3)
{:error, k: :badfun}
iex> AgentMap.start(k: & &1)
{:error, k: :badarity}
iex> {:error, k: {e, _st}} =
...>   AgentMap.start(k: fn -> raise "oops" end)
iex> e
%RuntimeError{message: "oops"}

#

iex> AgentMap.start([], name: Account)
iex> Account
...> |> put(:a, 42)
...> |> get(:a)
42
Link to this function stop(am, reason \\ :normal, timeout \\ :infinity)
stop(am(), reason :: term(), timeout()) :: :ok

Synchronously stops the AgentMap instance with the given reason.

Returns :ok if terminated with the given reason. If it terminates with another reason, the call will exit.

This function keeps OTP semantics regarding error reporting. If the reason is any other than :normal, :shutdown or {:shutdown, _}, an error report will be logged.

Examples

iex> {:ok, pid} = AgentMap.start_link()
iex> AgentMap.stop(pid)
:ok
Link to this function take(am, keys, opts \\ [!: :now])
take(am(), [key()], keyword() | timeout()) :: map()

Returns a map with keys and current values.

Options

  • !: priority, :now;

  • :timeout, 5000.

Examples

iex> %{a: 1, b: 2, c: 3}
...> |> AgentMap.new()
...> |> put(:a, 42)
...> |> put(:b, 42)
...> |> take([:a, :b, :d])
%{a: 42, b: 42}

This call returns a representation at the moment, so any pending operations will have no effect:

iex> am =
...>   AgentMap.new(a: 1, b: 2, c: 3)
iex> am
...> |> sleep(:a, 20)             # 0a         — spawns a worker for the :a key
...> |> put(:a, 42)               #  ↳ 1a ┐      …pending
...>                              #       :
...> |> put(:b, 42)               # 0b    :    — is performed on the server
...> |> take([:a, :b])            # 0     :
%{a: 1, b: 42}                    #       :
#                                         :
iex> take(am, [:a, :b], !: :avg)  #       ↳ 2
%{a: 42, b: 42}
Link to this function to_map(am)
to_map(am()) :: %{required(key()) => value()}

Returns a current Map representation.

Examples

iex> %{a: 1, b: 2, c: nil}
...> |> AgentMap.new()
iex> |> to_map()
%{a: 1, b: 2, c: nil}

This call returns a representation at the moment, so any pending operations will have no effect:

iex> %{a: 1, b: 2, c: nil}
...> |> AgentMap.new()
...> |> sleep(:a, 20)         # 0a     — spawns a worker for the :ay
...> |> put(:a, 42, !: :avg)  #  ↳ 1a  — delayed for 20 ms
...>                          #
...> |> to_map()              # 0
%{a: 1, b: 2, c: nil}
Link to this function update!(am, key, fun, opts \\ [!: :avg])
update!(am(), key(), (value() -> value()), keyword() | timeout()) ::
  am() | no_return()

Updates known key with the given function.

If key is present, fun is invoked with value as argument and its result is used as the new value. Otherwise, a KeyError exception is raised.

See update/4.

Options

  • !: priority, :avg;

  • tiny: true — to execute fun on server if it’s possible;

  • :timeout, 5000.

Examples

iex> AgentMap.new(a: 1)
...> |> sleep(:a, 20)                              # 0a
...> |> put(:a, 3)                                 # : ↱ 2a ┐
...> |> update!(:a, fn 1 -> 2 end, !: {:max, +1})  # ↳ 1a   ↓
...> |> update!(:a, fn 3 -> 4 end)                 #        3a
...> |> update!(:b, & &1)                          # 0b
** (KeyError) key :b not found
Link to this function update(am, key, fun, opts \\ [!: :avg])
update(am(), key(), (value() | initial() -> value()), keyword() | timeout()) ::
  am()

Updates key with the given fun.

See get_and_update/4.

Options

  • :initial, nil — value for key if it’s missing;

  • tiny: true — to execute fun on server if it’s possible;

  • !: priority, :avg;

  • :timeout, 5000.

Examples

iex> AgentMap.new(Alice: 24)
...> |> update(:Alice, & &1 + 1_000)
...> |> get(:Alice)
1024

iex> AgentMap.new()
...> |> update(:a, fn nil -> 42 end)
...> |> get(:a)
42

iex> AgentMap.new()
...> |> update(:a, & &1 + 18, initial: 24)
...> |> get(:a)
42
Link to this function values(am)
values(am()) :: [value()]

Returns current values stored by AgentMap instance.

Examples

iex> %{a: 1, b: 2, c: 3}
...> |> AgentMap.new()
...> |> values()
[1, 2, 3]

This call returns values as they are at the moment, so any pending operations will have no effect:

iex> AgentMap.new(a: 1, b: 2, c: 3)
...> |> sleep(:a, 20)
...> |> put(:a, 0)                   # pending
...> |> values()
[1, 2, 3]