rrmartins

rrmartins

Control flow structures vs Pattern Matching

Hello,

I came to make a provocation about a way of writing code.

I’m writing a small private project to shorten urls and I came across a change in the writing of a case.

I saw that when writing the case in my function I felt that the function seemed complex to understand and read. I thought about changing the case to use Pattern Matching of the functions.

leaving this code:

defp validate_url(changeset, field, opts \\ []) do
    validate_change(changeset, field, fn _, value ->
      case URI.parse(value) do
        %URI{scheme: nil} ->
          "is missing a scheme (e.g. https)"

        %URI{host: nil} ->
          "is missing a host"

        %URI{host: host} ->
          case :inet.gethostbyname(Kernel.to_charlist(host)) do
            {:ok, _} -> nil
            {:error, _} -> "invalid host"
          end
      end
      |> case do
        error when is_binary(error) -> [{field, Keyword.get(opts, :message, error)}]
        _ -> []
      end
    end)
  end

for this:

  defp validate_url(changeset, field, opts \\ []) do
    validate_change(changeset, field, fn _, value ->
      value
      |> URI.parse()
      |> handle_parse()
      |> handle_error_parse()
    end)
  end

  defp handle_error_parse(error) when is_binary(error), do: [{field, Keyword.get(opts, :message, error)}]
  defp handle_error_parse(_), do: []

  defp handle_parse(%URI{scheme: nil}), do: "is missing a scheme (e.g. https)"
  defp handle_parse(%URI{host: nil}), do: "is missing a host"

  defp handle_parse(%URI{host: host}) do
    host
    |> Kernel.to_charlist()
    |> :inet.gethostbyname()
    |> handle_hostbyname()
  end

  defp handle_hostbyname({:ok, _}), do: nil
  defp handle_hostbyname({:error, _}), do: "invalid host"

I’m preferring to use Pattern Matching instead of case and other control flow structures.

what do you think?

Most Liked

stefanchrobot

stefanchrobot

I personally prefer case over multiple function heads (sidenote: both use pattern matching!). But I’d do this:

defp validate_url_field(changeset, field, opts \\ []) do
  validate_change(changeset, field, fn _, value ->
    case validate_url(value) do
      :ok -> []
      {:error, message} -> [{field, Keyword.get(opts, :message, message)}]
    end
  end)
end

defp validate_url(string) do
  case URI.parse(string) do
    %URI{scheme: nil} ->
      {:error, "is missing a scheme (e.g. https)"}

    %URI{host: nil} ->
      {:error, "is missing a host"}

    %URI{host: host} ->
      case :inet.gethostbyname(Kernel.to_charlist(host)) do
        {:ok, _} -> :ok
        {:error, _} -> {:error, "invalid host"}
      end
  end
end
sodapopcan

sodapopcan

This is one of those cases of “Which do you prefer?” Your answer to that is the right one :smiley:

Personally I prefer multi-head functions whenever it’s possible, but that’s just me. I maybe wouldn’t start every function name with handle_ as it feels a bit noisy but, again, that’s just me.

sodapopcan

sodapopcan

PS, pattern matching essentially is control flow (among other things). It’s right there on the Erlang homepage :slight_smile:

Last Post!

cjbottaro

cjbottaro

I am on the fence on this one. Sometimes I think the code is cleaner looking with a single function head that has a single case statement… :thinking:

Especially if the function has other heads that deal with other things. Sometimes the combinatory explosion flatting is just too much.

Where Next?

Popular in Discussions Top

New
CharlesO
Erlang :list.nth simple, but 1 - based nth(1, [H|_]) -> H; nth(N, [_|T]) when N > 1 -> nth(N - 1, T). Elixir Enum.at … coo...
New
restack_oslo
Hello, Please pardon me for any faux paux. I am 46 and this is my first time on a forum of any kind. I wanted to to get answers from tho...
New
MarioFlach
Hello, I want to share a project I’ve been working on for a while: https://github.com/almightycouch/gitgud Background Some time ago I ...
New
chuck
Let me start by stating an assumption: Phoenix is a great approach to building REST APIs. There are many reasons for this, but I will ass...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
lucaong
Hello Elixir and Nerves community, I have been working for a while on an open-source embedded key-value database for Elixir, that I call...
230 14362 124
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54996 245
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New

We're in Beta

About us Mission Statement