Named Arguments with Default Values in Elixir

May 11, 2016

2 min read

Since Ruby 2.0 you can easily have methods that have named parameters with default values. Today I wanted to find the best way to do this in elixir. Below is what I came up with. In the comments please tell me if you have any suggestions and what your favourite version is.

Ruby Version

First here is what I am talking about in Ruby

def introduction(name: 'Sarah', birthday: "1985-12-30")
  puts "Hi my name is #{name} and I was born on #{birthday}"
end

This great because you can call it in many different ways:

## use only the default parameters
irb> introduction()
# Hi my name is Sarah and I was born on 1985-12-30

## override one of the parameters
irb> introduction(name: "George")
# Hi my name is George and I was born on 1985-12-30

## pass in parameters out of order
irb> introduction(birthday: "1990-02-09", name: "George")
# Hi my name is George and I was born on 1990-02-09

Elixir Version One - The Simplest

defmodule Talk do
  def introduction(name: name, birthday: bday) do
    IO.puts "Hi my name is #{name} and I was born on #{birthday}"
  end
end

This version is very simple, looks good and works decently well. However it does have a few drawbacks. You can’t have any defaults arguments and if you pass arguments in the wrong order (birthday before name) you will get a pattern match error.

Elixir Version Two - Using a Map

defmodule Talk do
  def introduction(options) do
	%{name: name, birthday: bday} = Enum.into(options, %{})

	IO.puts "Hi my name is #{name} and I was born on #{bday}"
  end
end

This version is similar to the first one except we make a map out of the passed in keyword list and we do the pattern matching in the function body instead of in the function head. This solves one of our problems, we can now pass in the arguments in any order we wish.

iex> Talk.introduction(name: "Sarah", birthday: "1985-12-30")
# Hi my name is Sarah and I was born on 1985-12-30

iex> Talk.introduction(birthday: "1985-12-30", name: "Sarah")
# Hi my name is Sarah and I was born on 1985-12-30

Elixir Version Three - Map with Defaults

defmodule Talk do
  def introduction(options \\ []) do
   defaults = [name: "Sarah", birthday: "1985-12-15"]
   options = Keyword.merge(defaults, options) |> Enum.into(%{})
    %{name: name, birthday: bday} = options

	IO.puts "Hi my name is #{name} and I was born on #{bday}"
  end
end

This final version is the exact same as the one before but we merge a defaults keyword list with the keyword list that is passed in.

Quick note: when merging two lists using Keyword.merge/2 the keys and values from the second list overrides the ones in the first, this allows us to have default values which are easily overridable.

iex> Talk.introduction
# Hi my name is Sarah and I was born on 1985-12-30

iex> Talk.introduction(birthday: "1985-12-30", name: "Sarah")
# Hi my name is Sarah and I was born on 1985-12-30

iex> Talk.introduction(name: "George", birthday: "1990-12-30")
# Hi my name is George and I was born on 1990-12-30

iex> Talk.introduction(birthday: "1990-12-30")
# Hi my name is Sarah and I was born on 1990-12-30

Hope you enjoyed this post, let me know if you have a better way of doing this.