Skip to content

Instantly share code, notes, and snippets.

@fschuindt
Created November 20, 2019 21:24
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fschuindt/d1174ac8f490bfdef8abf9629fa8ecaf to your computer and use it in GitHub Desktop.
Save fschuindt/d1174ac8f490bfdef8abf9629fa8ecaf to your computer and use it in GitHub Desktop.

This is a example on pattern matching.

Given the Map/Hash input:

input = %{name: "Alice Foo", points: 100, email: "alicefoo@myemail.com"}

The goal is to determine the user rank, by its points:

  • 0..50 -> Starter
  • 50..200 -> Member
  • >200 -> Veteran

The code in Elixir using pattern matching:

defmodule Rank do
  @moduledoc """
  Offers means to perform user ranking computations.
  """

  @typedoc """
  A user rank.
  """
  @type rank :: :starter | :member | :veteran

  @doc """
  Finds the rank of a given user.
  """
  @spec find(%{points: integer()}) :: rank()
  def find(%{points: n}) when n <= 50, do: :starter
  def find(%{points: n}) when n <= 200, do: :member
  def find(_user), do: :veteran
end

Here's how you use it:

iex(1)> Rank.find(input)
:member

The same code in Ruby:

class Rank
  # Offer means to perform ranking computations.

  # Finds the rank of a given user.
  def self.find(user)
    case user[:points]
    when 0..50
      :starter
    when 50..200
      :member
    else
      :veteran
    end
  end
end

And running it:

irb(main):001:0> Rank.find(input)
=> :member

Patter matching makes the code more elegant, expressive and easier to read.

@cjbottaro
Copy link

cjbottaro commented Aug 29, 2022

This is simply a bad example to demonstrate the power of pattern matching in Elixir.

Here's an example of argument overloading...

# Ruby

def get_name(user_or_post_or_id)
  user = if user_or_post_or_id.kind_of?(User)
    user_or_post_or_id
  elsif user_or_post_or_id.kind_of?(Post)
    User.find(user_or_post_or_id.user_id)
  elsif user_or_post_or_id.kind_of?(Integer)
    User.find(user_or_post_or_id)
  else
    raise ArgumentError
  end

  user.name
end
# Elixir

def get_name(%User{name: name}), do: name

def get_name(%Post{user_id: user_id}), do: get_name(user_id)

def get_name(user_id) when is_integer(user_id) do
  User.find(user_id) |> get_name()
end

Pattern matching gets even better when you want to assert the "shape" of a deeply nested object/struct, whereas in Ruby, you have to do it with multiple if statements with (maybe complex) boolean logic.

Most people I know, including myself, say the thing they miss most when going back to Ruby is pattern matching. It's absolutely amazing and elegant once you get used to it.

Further, in the Elixir example, you get some kind of static analysis from that code; you'll get warnings if you call get_name with something other than a User, Post, or integer (usually displayed right in your editor of choice as you type it). You get exactly zero of that with Ruby.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment