Agent.get_and_update/2 docs example doesn't return what I expect

After some months out of Elixir I am trying to refresh my knowledge on it, therefore I am going through the Elixir for Programmers 2 from @pragdave, because I have done the first edition and it was the one that made made my brain click in the functional programming paradigm.

So I am now at the Agent lesson and he shows the same example we can see in the docs:

iex(12)> Agent.get_and_update(counter, fn state -> {state, state + 1} end)
2
iex(13)> Agent.get(counter, fn state -> state end)                        
3

So, from the function name I would expect to have back the new state, but instead I got back the old state. I know I can control that from the anonymous function I pass, but my question is why the example in the docs is done this way and not this way:

Agent.get_and_update(counter, fn state -> state = state + 1; {state, state} end)    
4
iex(15)> Agent.get_and_update(counter, fn state -> state = state + 1; {state, state} end)
5
iex(16)> Agent.get_and_update(counter, fn state -> state = state + 1; {state, state} end)

I am not seeing immediately how useful it can be to update a state in the Agent and get back the old state, instead of the new state, but for sure it will exist some use cases for it.

Wouldn’t be better to have the official docs showing an example that returns the updated state?

2 Likes

I guess it would be called update_and_get ?! :slight_smile:

2 Likes

In the “counting” example, returning the old state is useful because it means the first value returned is the value that the Agent is initialized with.

The alternative implementation of “increment” that returns the new state would mean the function passed to Agent.start etc would need to be more complicated. Not much more in the case of simple incrementing, but not every “state update” can be run backwards…

Yes, I understand that, but I don’t see how useful that can be in real world applications, but it may be due to my lack of experience in using Elixir on real world projects.

In my opinion the use case off returning the new state after the update taking place may be the most common when this Agent function is used.

Now if the example is like that just to make it look simpler, then that is unfortunate from my point of view, but I would really like to understand its usefulness in real world projects as it his now documented.

I think what may not be clear at first is that get_and_update/3 gives you control over what is returned vs what is state. The control over what is returned is what is useful in real world projects.

iex(1)> {:ok, pid} = Agent.start_link(fn -> 42 end)                             
{:ok, #PID<0.113.0>}
iex(2)> Agent.get_and_update(pid, fn state -> {:ok, state + 1} end)             
:ok
iex(3)> Agent.get(pid, fn state -> state end)                                   
43
iex(4)> Agent.get_and_update(pid, fn state -> {"ok", state + 1} end)
"ok"
iex(5)> Agent.get(pid, fn state -> state end)                       
44
iex(6)> Agent.get_and_update(pid, fn state -> {{:ok, state + 1}, state + 1} end)
{:ok, 45}
iex(7)> Agent.get(pid, fn state -> state end)                                   
45

It keeps the state – in this case, only an integer – completely separate from what is returned (think pattern matching). It’s even useful with get/3.

iex(8)> Agent.get(pid, fn state -> {:ok, state} end)
{:ok, 45}
iex(9)> Agent.get(pid, fn state -> state end)       
45
iex(10)> Agent.get(pid, fn state -> %{my_value: state} end)
%{my_value: 45}
4 Likes

I completely understood that:

Thanks to make it clear with your examples :slight_smile: