I am currently learning Elixir by going through the Mix and OTP guide on elixir-lang.org. I have read through the Getting Started guide already.
I am having trouble getting my head around the use of placeholder arguments when capturing and partially applying named functions. The specific examples that are confusing me are on this page here https://elixir-lang.org/getting-started/mix-otp/agent.html
def get(bucket, key) do
Agent.get(bucket, &Map.get(&1, key))
end
def put(bucket, key, value) do
Agent.update(bucket, &Map.put(&1, key, value))
end
If I understand correctly, the & in &Map.get and &Map.put is capturing the named functions so that they can be passed as arguments to the Agent.get and Agent.update functions. This makes sense to me. But the &1 is causing confusion.
I think the &1 is evaluating to bucket in each of these examples. I don’t see what else could be happening here. But if this is the case, then why can’t we just write bucket instead of &1? Wouldn’t that be a whole lot clearer?
I guess I could just carry on with the tutorial without properly understanding this point, but it seems like this is a crucial enough concept to ask an embarrassingly noob question on these forums.
Thanks in advance for helping me to understand this.
Thanks for your reply and the explanation of how the ampersand & works. In general, it makes sense to me, like in the following example:
iex> fun = &(&1 + 1)
#Function<6.71889879/1 in :erl_eval.expr/5>
iex> fun.(1)
2
I have no problems with that.
However, I am still confused by the examples of code in my original question, though, so let me try to ask more specifically about what I’m confused about.
What is the value in the placeholder &1 being passed to the function &Map.get in the following example?
def get(bucket, key) do
Agent.get(bucket, &Map.get(&1, key))
end
There is nothing magic about how &1 is working here. Rather, it’s the Agent.get function that is doing the work. &Map.get(&1, key) is just an anonymous function that takes a map and grabs a key from it. For example:
In this example, &1 becomes whatever you pass to it, in my case map
When you do:
Agent.get(bucket, &Map.get(&1, key))
This is exactly equivalent to just doing:
fun = &Map.get(&1, key)
Agent.get(bucket, fun)
where fun in that example is 100% identical to the fun in my example.
SO how does it get the process state? Well, that’s the job of Agent.get. It takes that anonymous function sends it to the process, the process calls fun with its state, and sends the result back to you.
The capture operator is just a different way to write anonymous functions, but it is perfectly equivalent to the fn ... -> ... end form. The &1, &2, etc. are nothing else that the first argument, the second argument, etc.:
# This:
fun = fn a, b ->
a + b
end
# Is the same as this:
fun = &(&1 + &2)
The &1, &2, etc. have no direct relation with the context around the function. They are merely the arguments explicitly passed to the anonymous function when it is called:
fun(46, 58) # &1 = 46, &2 = 58
#=> 104
That said, I do think that the capture operator often makes code more obscure, so I personally prefer to use the extended fn ... -> ... end form, even if that is slightly longer.