Which is more idiomatic Elixir construct

Let’s say I have a Plug (the question is not limited to Plugs of course) where call/2 takes conn, processes it conditionally and returns it (processed or not) in the end. Now what would be more “correct”/idomatic approach:

def call(conn, _) do
	if(some_condition) do
		[...]
		do_important_stuff(conn)
	else
		conn
	end
end

or

def call(conn, _) do
	conn = if(some_conditions) do
		[...]
		do_important_stuff(conn)
	end

	conn
end

or maybe something still different?

It’s not equivalent…

The second will return nil unless some_condition.

As a personal preference, I would do…

defp do_important_stuff(conn, true) do
  ...
end
defp do_important_stuff(conn, _), do: conn
5 Likes

Yeah, correct, thanks - it was written “out of head”, not an example of actual code. It’s more about “how does one write this kind of constructs in idiomatic Elixir”

My preference is to replace first condition with pattern match on function head.

Agree. If I spot an if i think twice.
Alternative would be to use a guard on call if possible.

4 Likes

Everything but the if…

If is not available in Erlang

2 Likes

And then always call the private func with evaluated condition, right? Something like:

def call(conn, _)
    conn |> do_important_stuff(some_conditions)
end

?

Yes, and I would call this function maybe_do_important_stuff :slight_smile:

I am also getting used to this being not so kosher - exactly the reason for opening this thread :-))

Coming from other languages I usually try to avoid unnecessary functions/methods calls as they can be far more expensive than an if. How is that in Elixir? I mean not in a trivial example like here, where I assume it’d be negligible either way. But in general? Especially in some tight loops of many iterations? Is this somehow optimised by the compiler/VM?

:slight_smile: Right, that’s another convention I am slowly getting used to - tnx.

You should use the way You like the most… If is appropriate too :slight_smile:

But if You read a little bit of Erlang code, You will see a different style.

Seconded. I use if only for side effects. If I care about the value I use either case or cond

1 Like

Don’t count out if.

List.wrap(if ... do ... end) is suuuuuper useful pattern.

2 Likes

never used wrap and now I’m curious. Why is it so useful?

if may be getting a bad rep from imperative languages as it’s rarely an expression there, so using it in Elixir might feel a bit weird initially.

I think that if is a good fit for situations where you branch-off based on a boolean or truthy/falsy condition (no destructuring is involved). Have a look at this piece of code from the standard lib:

defp do_normalize(left..right//step, state) do
  left = do_normalize(left, state)
  right = do_normalize(right, state)
  meta = meta_line(state)

  if step == 1 do
    {:.., meta, [left, right]}
  else
    step = do_normalize(step, state)
    {:"..//", meta, [left, right, step]}
  end
end

I think that using case here would not convey the intentions as well as if does. Writing a separate function just to match the step in the head would seem like adding needless noise.

if is a macro, so there’s no runtime penalty for using it.

4 Likes

For an Elixir-newbie (or better: pattern-matching-newbie) I think its the best to avoid if completely. Later (eg when commiting to stdlib) you can use it again. :slight_smile:

Crazy talk. Use if when it makes sense.

3 Likes

It gives you empty list on nil. One place I use it often (in fact right after I posted the comment) is in Enum.flat_map, much cleaner than doing like enum.map then enum.filter

Another place is in building a single option keyword.

2 Likes

I see. I normally do

[:a] ++ [:b] ++ [] ++ [:c]

instead of

filter([:a, :b, nil, :c], ...)

which is inefficient.

So I’ll look into that.

Well I just didn’t know when it makes sense when I started. But I also did a lot of C where half of the words you have in you code is if.