agent (ex_stdlib v0.2.0)
View SourceAgents are a simple abstraction around state.
Often in Erlang there is a need to share or store state that must be accessed from different processes or by the same process at different points in time.
The agent module provides a basic server implementation that allows state to be retrieved and updated via a simple API.
Examples
For example, the following agent implements a counter:
-module(counter).
-export([start_link/1, value/0, increment/0]).
start_link(InitialValue) ->
agent:start_link(fun() -> InitialValue end, [{name, ?MODULE}]).
value() ->
agent:get(?MODULE, fun(State) -> State end).
increment() ->
agent:update(?MODULE, fun(State) -> State + 1 end).Usage would be:
{ok, Pid} = counter:start_link(0).
0 = counter:value().
ok = counter:increment().
ok = counter:increment().
2 = counter:value().Thanks to the agent server process, the counter can be safely incremented concurrently.
Agents provide a segregation between the client and server APIs (similar to gen_servers). In particular, the functions passed as arguments to the calls to agent functions are invoked inside the agent (the server). This distinction is important because you may want to avoid expensive operations inside the agent, as they will effectively block the agent until the request is fulfilled.
Consider these two examples:
%% Compute in the agent/server
get_something(Agent) ->
agent:get(Agent, fun(State) -> do_something_expensive(State) end).
%% Compute in the agent/client
get_something(Agent) ->
State = agent:get(Agent, fun(State) -> State end),
do_something_expensive(State).The first function blocks the agent. The second function copies all the state to the client and then executes the operation in the client. One aspect to consider is whether the data is large enough to require processing in the server, at least initially, or small enough to be sent to the client cheaply. Another factor is whether the data needs to be processed atomically: getting the state and calling do_something_expensive(State) outside of the agent means that the agent's state can be updated in the meantime. This is specially important in case of updates as computing the new state in the client rather than in the server can lead to race conditions if multiple clients are trying to update the same state to different values.
Summary
Functions
Performs a cast (fire and forget) operation on the agent state.
Performs a cast (fire and forget) operation on the agent state.
Gets an agent value via the given anonymous function.
Gets an agent value via the given function.
Gets and updates the agent state in one operation via the given anonymous function.
Gets and updates the agent state in one operation via the given function.
Starts an agent process without links (outside of a supervision tree).
Starts an agent without links with the given module, function, and arguments.
Starts an agent linked to the current process with the given function.
Starts an agent linked to the current process.
Synchronously stops the agent with the given Reason.
Updates the agent state via the given anonymous function.
Updates the agent state via the given function.
Types
-type init_fun() :: fun(() -> state()).
-type state() :: term().
-type timeout_ms() :: non_neg_integer() | infinity.
Functions
-spec cast(agent(), update_fun()) -> ok.
Performs a cast (fire and forget) operation on the agent state.
The function Fun is sent to the Agent which invokes the function passing the agent state. The return value of Fun becomes the new state of the agent.
Note that cast returns ok immediately, regardless of whether Agent (or the node it should live on) exists.
Performs a cast (fire and forget) operation on the agent state.
Same as cast/2 but a module, function, and arguments are expected instead of an anonymous function. The state is added as first argument to the given list of arguments.
Gets an agent value via the given anonymous function.
The function Fun is sent to the Agent which invokes the function passing the agent state. The result of the function invocation is returned from this function.
Timeout is an integer greater than zero which specifies how many milliseconds are allowed before the agent executes the function and returns the result value, or the atom infinity to wait indefinitely. If no result is received within the specified time, the function call fails and the caller exits.
-spec get(agent(), get_fun(), timeout_ms()) -> term().
Gets an agent value via the given function.
Same as get/3 but a module, function, and arguments are expected instead of an anonymous function. The state is added as first argument to the given list of arguments.
-spec get(agent(), module(), atom(), [term()], timeout_ms(), timeout_ms()) -> term().
-spec get_and_update(agent(), get_and_update_fun()) -> term().
Gets and updates the agent state in one operation via the given anonymous function.
The function Fun is sent to the Agent which invokes the function passing the agent state. The function must return a tuple with two elements, the first being the value to return (that is, the "get" value) and the second one being the new state of the agent.
Timeout is an integer greater than zero which specifies how many milliseconds are allowed before the agent executes the function and returns the result value, or the atom infinity to wait indefinitely. If no result is received within the specified time, the function call fails and the caller exits.
-spec get_and_update(agent(), get_and_update_fun(), timeout_ms()) -> term().
Gets and updates the agent state in one operation via the given function.
Same as get_and_update/3 but a module, function, and arguments are expected instead of an anonymous function. The state is added as first argument to the given list of arguments.
-spec get_and_update(agent(), module(), atom(), [term()], timeout_ms(), timeout_ms()) -> term().
Starts an agent process without links (outside of a supervision tree).
See start_link/2 for more information.
-spec start(init_fun(), [gen_server:start_opt()]) -> on_start().
-spec start(module(), atom(), [term()], [gen_server:start_opt()]) -> on_start().
Starts an agent without links with the given module, function, and arguments.
See start_link/4 for more information.
Starts an agent linked to the current process with the given function.
This is often used to start the agent as part of a supervision tree.
Once the agent is spawned, the given function Fun is invoked in the server process, and should return the initial agent state. Note that start_link/2 does not return until the given function has returned.
Options
The name option is used for registration. Valid values are: - {name, LocalName} where LocalName is an atom - {name, {global, GlobalName}} where GlobalName is any term - {name, {via, Module, ViaName}} for custom registration
If the timeout option is present, the agent is allowed to spend at most the given number of milliseconds on initialization or it will be terminated and the start function will return {error, timeout}.
If the debug option is present, the corresponding function in the sys module will be invoked.
If the spawn_opt option is present, its value will be passed as options to the underlying process.
Return values
If the server is successfully created and initialized, the function returns {ok, Pid}, where Pid is the PID of the server. If an agent with the specified name already exists, the function returns {error, {already_started, Pid}} with the PID of that process.
If the given function callback fails, the function returns {error, Reason}.
-spec start_link(init_fun(), [gen_server:start_opt()]) -> on_start().
-spec start_link(module(), atom(), [term()], [gen_server:start_opt()]) -> on_start().
Starts an agent linked to the current process.
Same as start_link/2 but a module, function, and arguments are expected instead of an anonymous function; Fun in Module will be called with the given arguments Args to initialize the state.
-spec stop(agent()) -> ok.
Synchronously stops the agent with the given Reason.
It returns ok if the agent terminates with the given reason. If the agent 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.
-spec stop(agent(), term(), timeout_ms()) -> ok.
-spec update(agent(), update_fun()) -> ok.
Updates the agent state via the given anonymous function.
The function Fun is sent to the Agent which invokes the function passing the agent state. The return value of Fun becomes the new state of the agent.
This function always returns ok.
Timeout is an integer greater than zero which specifies how many milliseconds are allowed before the agent executes the function and returns the result value, or the atom infinity to wait indefinitely. If no result is received within the specified time, the function call fails and the caller exits.
-spec update(agent(), update_fun(), timeout_ms()) -> ok.
-spec update(agent(), module(), atom(), [term()], timeout_ms()) -> ok.
Updates the agent state via the given function.
Same as update/3 but a module, function, and arguments are expected instead of an anonymous function. The state is added as first argument to the given list of arguments.
-spec update(agent(), module(), atom(), [term()], timeout_ms(), timeout_ms()) -> ok.