1

I can write an anonymous function in the interactive shell directly like below.

iex> total_bottles_milk = fn total -> total * 2 end  
iex> total_bottles_milk.(2)

However, if I write in an external file and run in the interactive shell, it shows a Compile Error.

My file name and directory path is lib/expense.ex

Below is my code

defmodule Expense do

    total_bread_slices = fn total -> (total * 10) / 100 end
    total_bottles_milk = fn total -> total * 2 end
    total_cakes = fn total -> total * 15 end

    def total_expense(bread_slices, bottles_of_milk, cakes) do
        total_bread_slices.(bread_slices) + total_bottles_milk.(bottles_of_milk) + total_cakes.(cakes)
    end

end

When I go into the folder path and run iex -S mix to run my Expense module, terminal shows Compilation error.
I'm wondering only I can run anonymous function directly into the interactive shell and not from external sources and compile it. I want to write my function as a first-class citizen. And if there is a way, how can I do it?

2
  • 1
    What do you mean by "first-class citizen" here? You can define a named function like def total_cokes(total), do: total * 15 and then create an anonymous function by doing &total_cokes/1. Returning anonymous functions from within normal functions (like your answer) is not idiomatic.
    – Dogbert
    Commented Jul 10, 2018 at 15:27
  • Ah...I got it now!! Thanks for ur guide. Commented Jul 11, 2018 at 7:30

4 Answers 4

5

You cannot create "variables" like this in elixir (see EDIT 1 and Edit 2 below). The error you're seeing is normal.

You can put your anonymous functions into named functions and call them from there which would give you the same result:

defmodule Expense do

  def total_expense(bread_slices, bottles_of_milk, cakes) do
    total_bread_slices().(bread_slices) + total_bottles_milk().(bottles_of_milk) + total_cakes().(cakes)
  end

  defp total_bread_slices, do: fn total -> (total * 10) / 100 end
  defp total_bottles_milk, do: fn total -> total * 2 end
  defp total_cakes, do: fn total -> total * 15 end
end

This way you're calling the named function which will return the anonymous function which then you pass the arguments to.

EDIT 1

You cannot create variables like that INSIDE modules. This works in iex because it's an interactive environment. However, the x = y syntax in invalid outside of a function in an elixir module.

EDIT 2 Thanks to a correction from @Dogbert. You can actually create variables inside modules and out of functions, but you cannot use them inside def.

6
  • Thanks for your answer. But I want to write my function as a First-Class Citizen. So, it means I can only write like that in interactive shell? Commented Jul 10, 2018 at 7:43
  • 1
    It depends on what you mean by "first-class citizens". The problem is not with defining functions and using them. It's mainly because you cannot create variables like that in elixir (outside of functions). For example, you can have a function return a function and store that into a variable. Please check the following gist Commented Jul 10, 2018 at 8:02
  • Ah..now got it. Thanks for your time. Commented Jul 10, 2018 at 8:19
  • 2
    You can definitely create variables in modules / outside functions. You just can't access them from within a def.
    – Dogbert
    Commented Jul 10, 2018 at 15:25
  • 1
    @Dogbert thank you. I just knew this was possible. Never used or thought about it and for some reason assumed it was not valid. Updated answer accordingly. Commented Jul 11, 2018 at 4:50
0

Thanks to @Abdullah Esmail, I can write my function like this

defmodule Expense do

    def total_bread_slices do
        fn total -> (total * 10) / 100 end
    end 

    def total_bottles_milk do
        fn total -> total * 2 end
    end 

    def total_cakes do
        fn total -> total * 15 end
    end 

    def total_expense(bread_slices, bottles_of_milk, no_of_cakes) do

        bread = total_bread_slices()
        milk = total_bottles_milk()
        cakes = total_cakes()

        bread.(bread_slices) + milk.(bottles_of_milk) + cakes.(no_of_cakes)
    end

end
0

Thanks to @Dogbert, here is another method if I want to use the function as a value.

Firstly, I define a named function and then using Elixir's function capturing operator &, I can more easily use a named function as a value.

In this way, using the & operator to capture a reference to the function and the = operator to bind to the variable, I can bind the named function to a variable.

defmodule Expense do

    defp total_bread_slices(total) do
        (total * 10) / 100
    end 

    defp total_bottles_milk(total) do
        total * 2 
    end 

    defp total_cakes(total) do
        total * 15 
    end 

    def total_expense(bread_slices, bottles_of_milk, no_of_cakes) do

        bread = &total_bread_slices/1
        milk = &total_bottles_milk/1
        cakes = &total_cakes/1

        bread.(bread_slices) + milk.(bottles_of_milk) + cakes.(no_of_cakes)

    end

end
0

I had the same problem during the running examples from Elixir In Action book, second edition. To avoid retyping lambda function examples in iex, I saved them in a file. I tried to load them in iex, e.g:

iex solutions/ch05.ex

but when I called lambda function:

a_lambda.("something")

I got a compilation error in iex.

Now I copy/paste lambda code from the file directly to Iex, and it works.

Not the answer you're looking for? Browse other questions tagged or ask your own question.