Named Arguments with Default Values in Elixir

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.

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-30"]
   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-15", name: "Sarah")  
# Hi my name is Sarah and I was born on 1985-12-15

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

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

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

Update: Thanks to Wendy Smoak for suggesting an alternative to version 3. It's more concise and doesn't need a map, check out her comment in the comment section below for the implementation

Update 2: Matt Widmann has created a Hex package that makes it easier to use named arguments in elixir.