timbot

timbot

Hi there,

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.

Showing Posts 10 to 1

timbot

timbot OP

Thanks for the explanations, folks.

I have added a pull request to the documentation over at Github adding a clarifying sentence about this behavior of the Agent.

lucaong

lucaong

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.

kokolegorille

kokolegorille

That is the power of function as first class citizen, You can pass the implementation as an argument :slight_smile:

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

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:

iex(1)> key = :name            
:name
iex(2)> fun = &Map.get(&1, key)
#Function<7.91303403/1 in :erl_eval.expr/5>
iex(3)> map = %{name: "Ben", country: "USA"}
%{country: "USA", name: "Ben"}
iex(4)> fun.(map) 
"Ben"

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.

timbot

timbot OP

Oh! Now I see. The &1 is the Agent’s state, which is being passed from the Agent to the anonymous function.

Maybe its just me, but that seems to be a quite different behaviour for the &1 than it had in the other examples.

kokolegorille

kokolegorille

An agent store some state…

timbot

timbot OP

The state of the agent? Can you please elaborate?

kokolegorille

kokolegorille

It is the state of the Agent that get passed to the anonymous function.

timbot

timbot OP

Hello kokolegorille,

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
kokolegorille

kokolegorille

Hello and welcome,

& is a shortcut for defining anonymous function, and &1, &2… are the parameters to this function.

&Map.get(&1, key)

# is equal to

fn x -> Map.get(x, key) end

You might find version with two parameters, in particular with reduce…

For example.

iex> Enum.reduce [1, 2, 3], & &1 + &2
6

# is equal to

iex> Enum.reduce [1, 2, 3], fn x, acc -> x + acc end

More on this here

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews