Rukenshia

Rukenshia

Best Practice to get rid of if...else blocks?

I am used to early returns using the “return” keyword many languages provide.
As I am currently trying to learn elixir, I found it really hard to get a proper structure in my code and avoid if…else blocks which I have to nest very deep.

As an example, I wrote a small bot for Discord (don’t do it by the way, the discord_ex library doesn’t seem to work properly) and have to check several things when I receive a message.

As an example, here is one handler.

defp handle_command({ "msg", "remove-trigger", cmd }, { payload, state }) do
    if payload.data["author"]["id"] == 1234 do
      shortname = String.split(cmd, " ") |> List.first
      if !Messages.has shortname do
        send_message "unknown Message", { payload, state }
      else
        info = Messages.get(shortname)
        trigger = String.replace_leading(cmd, "#{shortname} ", "")
        if Enum.member?(info[:trigger], trigger) do
          Messages.remove_trigger shortname, trigger
          save_messages
        end

        send_message "trigger #{trigger} removed from #{shortname}", {payload, state}
      end
    end
  end

I am pretty sure that this can be way improved - what’s the proper design pattern for that in Elixir, how do I escape the if-block hell?

Most Liked

josevalim

josevalim

Creator of Elixir

To add to this, at some point the community, even Elixir codebase, settled on do_send_message as a convention but today we see it as a bad practice. We are trying our best to not use do_ prefixes in Elixir and use proper function names for them, even if it ends up being the original name with extra information, for example, check_shortname_and_send_message.

atimberlake

atimberlake

You can pattern match the handle_command function to drop the first if

defp handle_command({"msg", "remove-trigger", cmd}, {%{data: %{"author" => %{"id" => 1234}}}, state}) do
   # handle author 1234  
end
defp handle_command({"msg", "remove-trigger", cmd}, {payload, state}) do
  # No-op because you have no else
end

For the second if I would drop the call to Message.has/1 (assuming Message.get/1 returns nil for no message)

shortname = String.split(cmd, " ") |> List.first
do_send_message(Messages.get(shortname))

Then have a function deal with the presence or lack of message

defp do_send_message(nil) do
  send_message "unknown Message", {payload, state}
end
defp do_send_message(info) do
  trigger = …
end

Keep going by creating functions that handle each situation and try to come up with names that reveal the intent of each step (do_send_message is a terrible function name but because naming is hard, you get to solve that :slight_smile: )

Rukenshia

Rukenshia

Thanks a lot guys, this really helped me. It’s really easy for me to forget how powerful the pattern matching just is. I know Rust’s pattern matching which is pretty cool already, but this is way more powerful.

Where Next?

Popular in Questions Top

siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
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
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
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
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
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
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
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54120 245
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement