Closures in Elixir

Arpan Ghoshal
4 min readNov 28, 2020

--

We often hear about scopes and closures in many languages, like in javascript closures are used extensively and there are lots of resources explaining how they work.

For elixir, closures are also extremely common, and often people who are new to the language get confused as to how they work.

So, in this blog, I will explain with some examples which will hopefully make things clearer as to how function scopes and closures work in elixir.

In elixir, every assignment you do within a function is scoped inside that function.

Let's see this with a very simple example…

defmodule ClosuresDemystified do  def hi do
name = "Sam"
changeName()
IO.puts name
end
def changeName do
name = "Bob"
end
end
ClosuresDemystified.hi() # Prints Sam

In the above example, we try to change the value of our name variable but since the variable name="Bob" is scoped under the changeName() function so it does not affect our name variable in the hi() function.

A more interesting example that you could encounter would be this...

defmodule ClosuresDemystified do  def calculate_sum do   
sum = 0
Enum.each(1..10, fn element ->
sum = sum + element
IO.puts "Sum is #{sum}"
end)

IO.puts "Final sum = #{sum}"
end
end
ClosuresDemystified.calculate_sum()

This code outputs the following:

Sum is 1
Sum is 2
Sum is 3
Sum is 4
Sum is 5
Sum is 6
Sum is 7
Sum is 8
Sum is 9
Sum is 10
Final sum = 0

Our sum code did not work as expected, and this was because every time the anonymous function that was passed to Enum.each/2 got executed it did not retain the previous value it had assigned to the sum variable so every time the sum was equal to the element at that iteration.

To avoid this we could use the Enum.reduce/2 function which uses an accumulator variable to pass the sum computed in an iteration to the anonymous function in the next iteration as an argument.

In the anonymous function passed to Enum.reduce/2 we must always return the sum we computed in that iteration.

To preserve a value calculated in a function we must return that value from that function.

Using Enum.reduce/2 we could compute the sum like…

sum = Enum.reduce(1..10, fn sum, element -> sum + element end)

# Or even better like...

sum = Enum.reduce(1..10, &+/2)

In the shorter version, we passed the Kernel.+/2 inbuild function instead of our own anonymous function to calculate the sum.

Yes + is actually a function that is defined in the Kernal module, here.

You can invoke Kernel functions and macros anywhere in Elixir code without the use of the Kernel. prefix since they have all been automatically imported.

Finally, an example to wrap things up…

x = 1# An anonymous function
anon = fn ->
IO.puts(x);
x = 0;
end
anon.() # Outputs 1IO.puts(x) # Outputs 1x = 5anon.() # Outputs 1

Here, we defined an anonymous function anon where we print a variable x defined in the outer scope. We then try to reassign the variable.

When we invoke our anonymous function anon.() we find it does not affect the x we defined in the outerscope, since when we do x = 0 inside our anonymous function, we create a new variable x scoped within that function which no longer exists after that function ends.

So, within a function, we cannot change a variable defined in the outer scope.

Next, we try to reassign the variable x = 5 and then call anon.() again.

But surprisingly our anonymous functions till outputs 1 but why?

This is because an anonymous function when defined captures the values of the variables present in its lexical scope at that time, and will always refer to those values even if we later change the values of those variables.

So, in our example the anonymous function anon captured the value x = 0 when it was defined and will always have x = 0 even if we change the value of x later.

Summary

So in summary the concepts we learned are…

  • All variables assigned within a function will go out of scope once the function ends. We must return the value from a function to use it later.
  • An anonymous function can access a variable defined in its outer scope however it CANNOT reassign and change its value.
  • If we try to reassign a value defined in the outer scope from within an anonymous function we will end up defining a new local variable that will be scoped within our anonymous function and will shadow the variable defined in the outer scope having the same name.
  • Finally, an anonymous function in elixir is a closure, that is when declared it will capture the values of the variables defined in the outer scope at that point and will always retain those captured values even if we change the values of those variables later.

I hope this blog helps you with your journey in the elixir world 😊

Thanks, gave a good day

--

--

Arpan Ghoshal

I like to build new stuff and I have passion for coding