Fare Thee Well Conditional Logic – Hello Pattern Matching!

I’ve heard this conversation many times over the last few years.

Question: What are you liking about Elixir?

Answer: Oh, I like …, and …, and I LOVE pattern matching!

Pattern matching is almost always one of the features I hear in the response, and the reasoning is typically around how much conditional logic pattern matching “eliminates”. It wasn’t until I actually started using Elixir that I started to understand what that actually meant. I’d say pattern matching allows us to express the conditional aspects of our system in a simpler, more explicit way. Rather than using a number of if
and else if
statements, we can express the conditions in other ways, like our function heads.

Let’s consider a simple example where we’d like to discern the type of a triangle based on the length of its sides. In essence, we’d want something like the following to pass:

test "equilateral triangles have equal sides" do
  assert Triangle.kind(2, 2, 2) == :equilateral
  assert Triangle.kind(3, 4, 4) == :isosceles
  assert Triangle.kind(4, 3, 4) == :isosceles
  assert Triangle.kind(4, 4, 3) == :isosceles
  assert Triangle.kind(3, 4, 5) == :scalene
end

In a typical language that doesn’t have patten matching, we’d likely have code that started out looking something like this:

class Triangle
  def self.kind(a, b, c)
    if (a == b) && (a == c)
      :equilateral
    elsif (a == b) || (a == c) || (b == c)
      :isosceles
    else
      :scalene
    end
  end
end

In Elixir, we can solve the same problem like this:

defmodule Triangle do
  def kind(a, a, a), do: :equilateral
  def kind(a, a, _), do: :isosceles
  def kind(a, _, a), do: :isosceles
  def kind(_, b, b), do: :isosceles
  def kind(_, _, _), do: :scalene
end

It means we often have more functions, but each of them is typically smaller and easier to parse since we didn’t need to use the standard conditional constructs.

Let’s consider another example where we want to process an incoming hash based on some of its keys and values. A starting point for such code in a language without pattern matching looks like this:

def process(hash) do
  if hash[:bucket] == "transactions"
    #...
  elsif hash[:bucket] == "gateways"
    #...
    if hash[:gateway_type] == "test"
      #...
    else
      #...
    end
  elsif hash[:bucket] == "id/organizations"
    #...
  else
    #...
  end
end

In Elixir though, we can approach the problem differently:

def process(%{bucket: "transactions"}) do
  ...
end

def process(%{bucket: "gateways", gateway_type: "test"}) do
  ...
end

def process(%{bucket: "gateways"}) do
  ...
end

def process(%{bucket: "id/organizations"}) do
  ...
end

def process(_) do
  ...
end

Each conditional essentially becomes its own extracted function, and the first one that matches gets called. So simple!

When a language like Elixir offers intuitive ways to structure the code such that the functions can remain small and simple, that’s exciting to me. When adding additional functionality often involves simply adding another function head with some new behavior, the system starts to feel quite manageable and extendible. Each conditional is a first-class concern within its module, making it easier to read and scan.

Pattern matching has been such a pleasure to use. It’s difficult for me to imagine building a large system without it.

If you’re considering using Elixir in your next project, Stephen Ball’s blog post
is a reminder that things may be simpler than you might imagine.

稿源:Spreedly Engineering (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 营销策划 » Fare Thee Well Conditional Logic – Hello Pattern Matching!

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录