Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elixir: How are default arguments that are not at the end treated?

Tags:

elixir

When I run the following Elixir script, it prints out {"K", "Y", "L"}.

defmodule MyMod do
  def make_tuple(a \\ "X", b \\ "Y", c) do
    {a, b, c}
  end
end

IO.inspect(MyMod.make_tuple("K", "L"))

Why doesn't it output {"X", "K", "L"}? What is the mechanism behind this behavior?

like image 791
Tsutomu Avatar asked Oct 23 '20 12:10

Tsutomu


People also ask

CAN default arguments be skipped?

It's not possible to skip it, but you can pass the default argument using ReflectionFunction .

Which is the correct condition for the default arguments?

Which is the correct condition for the default arguments? Explanation: The default arguments must be declared at last in the argument list. This is to ensure that the arguments doesn't create ambiguity.

How does default argument work?

A default argument is a value provided in a function declaration that is automatically assigned by the compiler if the caller of the function doesn't provide a value for the argument with a default value.

What are kotlin's default arguments?

Kotlin Default arguments – The arguments which need not specify explicitly while calling a function are called default arguments. If the function is called without passing arguments then the default arguments are used as function parameters.

Why does elixir pattern match the arguments of a function?

The reason for this behavior is that Elixir pattern-matches the arguments that a function is called with against the arity the function is defined with. Let’s think about how the data looks when it arrives to Greeter1.hello/1:

What is an anonymous function in Elixir?

In Elixir and many functional languages, functions are first class citizens. We will learn about the types of functions in Elixir, what makes them different, and how to use them. Just as the name implies, an anonymous function has no name. As we saw in the Enum lesson, these are frequently passed to other functions.

How are guards tested in Elixir?

Once Elixir has matched a function any existing guards will be tested. In the following example we have two functions with the same signature, we rely on guards to determine which to use based on the argument’s type:


2 Answers

You are correct: Elixir uses any "excess" arguments provided to override a function's default values for some or all of its parameters, filled from left to right. My understanding of this came from the Programming Elixir 1.6 book by Dave Thomas.

The trick is that for functions with default arguments, the compiler actually generates multiple functions. A function defined like this:

defmodule Example do 
  def func(p1, p2 \\ 2, p3 \\ 3, p4) do 
    IO.inspect [p1, p2, p3, p4]
  end 
end

Actually gets compiled down to a module that looks something like this:

defmodule Example do 
  def func(p1, p4) do
    func(p1, 2, 3, p4)
  end

  def func(p1, p2, p4) do
    func(p1, p2, 3, p4)
  end

  def func(p1, p2, p3, p4) do 
    IO.inspect [p1, p2, p3, p4]
  end 
end

By the way, you can see this by using a module's "magic" module_info/0 function, e.g. Example.module_info().

Calling your functions with some sample values will help you understand how they are interpreted, and there may be a few surprises.

Example.func("a", "b")           # => ["a",2,3,"b"]

Example.func("a", "b", "c")      # => ["a","b",3,"c"]  <--- !!!

Example.func("a", "b", "c", "d") # => ["a","b","c","d"]

It may be a bit easier to see this behavior with a function like this:

def func(p1 \\ 1, p2 \\ 2) do 
  IO.inspect [p1, p2]
end 

And calling it with only 1 argument provided. It will use the provided argument on the left side; the default on the right still gets used:

func("a")    #  =>  ["a", 2]

I agree that things do get confusing when a function has optional arguments that aren't at the end of the argument list.

like image 119
Everett Avatar answered Sep 26 '22 23:09

Everett


Just from experimenting with it a bit, it seems like it assigns the arguments from the left. So in this case:

defmodule MyMod do
  def make_tuple(a \\ "X", b, c \\ "Y", d) do
    {a, b, c, d}
  end
end

IO.inspect(MyMod.make_tuple(1, 2, 3))

You get {1, 2, "Y", 3} - this is a "left-most" interpretation of the arguments provided. This leads to a situation where if you add an argument "at the end" it changes the meaning of the previous arguments, and (I assume) is the reason why people usually avoid having optional arguments that are not at the end of the argument list.

like image 28
Paweł Obrok Avatar answered Sep 24 '22 23:09

Paweł Obrok