kccarter

kccarter

When should you use function overloading vs control structures?

While browsing through the source code to Plug, I can across this block of code:

defp weekday_name(1), do: "Mon"
defp weekday_name(2), do: "Tue"
defp weekday_name(3), do: "Wed"
defp weekday_name(4), do: "Thu"
... snip ...

This pattern would raise a lot of red flags in most languages but given the popularity of Plug, I assume this is a reasonable pattern in Elixir.

Is this pattern preferred over using more “traditional” control structures like cond and case? If so, why and what benefits does it provide?

In Plug, there is a function for each weekday and each month. How far should you take such a design pattern when matching on simple values?

Most Liked

dimitarvp

dimitarvp

This is not function overloading at all. You can liken it to a multimethod maybe.

Using pattern matching in function heads does get compiled to case. It’s a readability improvement pattern that many have found useful because you can more cleanly isolate logic for separate input values.

There’s never an universal answer. That depends on whether you are reaping any readability benefits and/or if your team is happier with the code.

derpycoder

derpycoder

It’s the best pattern I came across and I love it.

Here are some of the benefits:

  1. No pyramid of doom.
  2. Each aspect belongs to its own function.
  3. Functions can be pipelined because of this approach and we can easily debug the things happening in a pipeline using dbg(), rather than putting print statements everywhere.
  4. Pattern Matching…

Here’s an example code I came across today, that is just awesome:


Here’s how I use it:

defmodule DerpyCoder.Photos.Policy do
  @moduledoc """
  Policy: Used to authorize user access
  """
  alias DerpyCoder.Accounts.User
  alias DerpyCoder.Photos.Photo

  @type entity :: struct()
  @type action :: :new | :index | :edit | :show | :delete

  @spec can?(User, action(), entity()) :: boolean()
  def can?(user, action, entity)

  # ==============================================================================
  # Super Admin - Can do anything
  # ==============================================================================
  def can?(%User{id: id, role: :super_admin}, _, _), do: DerpyCoder.Accounts.is_super_admin?(id)

  # ==============================================================================
  # Admin
  # ==============================================================================
  def can?(%User{role: :admin} = user, :new, _),
    do: FunWithFlags.enabled?(:new_photos, for: user)           # Admins must have the feature enabled

  def can?(%User{role: :admin} = user, :edit, _),
    do: FunWithFlags.enabled?(:edit_photos, for: user)          # By being part of photography group perhaps

  def can?(%User{role: :admin} = user, _, Photo), do: FunWithFlags.Group.in?(user, :photography)
  def can?(%User{role: :admin}, _, _), do: true

  # ==============================================================================
  # User
  # ==============================================================================
  def can?(%User{} = user, :new, Photo), do: FunWithFlags.enabled?(:new_photos, for: user)

  def can?(%User{id: id} = user, :edit, %Photo{user_id: id}),      # Can user edit photo? Yes, only if it's theirs
    do: FunWithFlags.enabled?(:edit_photos, for: user)             # And if feature is enabled

  def can?(%User{id: id} = user, :delete, %Photo{user_id: id}),    # Can user delete photo? Yes, only if it's their own
    do: FunWithFlags.enabled?(:delete_photos, for: user)           # And if the feature is enabled for the user

  def can?(_, _, _), do: false
end

defmodule DerpyCoder.Photos do
  @moduledoc """
  The Photos context.
  """

  defdelegate can?(user, action, entity), to: DerpyCoder.Photos.Policy
  ...
end

Seeing the above, can you tell what’s happening?

Is it more readable, perhaps not, but once you get used to it, it’s much more readable than an if-else-infested page.

And here’s the elegant usage of the above policy.

Photos.can?(@current_user, :new, Photo)      # Show new button
Photos.can?(@current_user, :delete, photo)   # Show delete button
Photos.can?(@current_user, :edit, photo)     # Show edit button

It’s because of this feature, handle_event, handle_info, plugs, and pipelines work the way they work.

D4no0

D4no0

Exactly, and the great thing is that these principles can be applied to other languages too, in some easier and in some you might require some libraries.

Golang is one of the languages that also embraces this principle, errors as data, handle the errors you want and ignore the rest, the only sad thing about this in golang is the lack of support structure for this style of programming, people tend to write coroutines that can handle/ignore almost all errors as to avoid crashing the entire application, witch in turn creates defensive programming, that is brittle, hard to test, hard to debug.

In my short professional career with golang, I never saw anyone not writing defensive code, thousands of lines for null pointers check, this was one of the reasons at that point I decided to leave that company and golang behind and focus on elixir.

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
RisingFromAshes
I've read in another post that it may be possible with a router helper - but I couldn't find an appropriate one, and tbh, I'm still just ...
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42842 311
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41454 115
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 53578 245
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52238 488
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "eq...
New
nobody
Hi! In PHP: $SERVER['SERVERADDR'] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 47849 226
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement