I'm curious about when to use `case` and `overload function`

for an example

  def do_something_with(stuff) do
    case stuff do
      'good' -> ....
      'bad' -> ...
    end
  end  

and

  def do_something_with(%{stuff: 'good'}), do: ...
  def do_something_with(%{stuff: 'bad'}), do: ...

I know, above 2 method give me a same result, except

  • I can handle a default case in 1st method easier and more readable than 2nd

Should be a good reason to decide which way to go in which condition

1 Like

The snippets you are showing are not equivalent, but at least they are close enough to assume you are checking for the value of the the key stuff as well in version 1 to make them equivalent.

I’d go definitively with the top level pattern match. It does read mutch better in my opinion. Also some ides do list them as different functions in the overview which makes finding the correct piece of code much easier.

Also I’m not quite sure what you meant with handling the default case. The underscore does work in both situations.

2 Likes

Function clauses in this case.
Easier to find the interesting bits.
And they are faster.

Cheers,
Torben

Addendum to my first post in this thread:

Even if there is a clear preference to use function clauses in the community, case has its right to exist as well. I’d use a case, when I can’t pattern match on the functions arguments directly, but have to do some calculations first as in the following contrived example.

def foo(a, b) do
  c = a - b
  case c do
    0 -> :equal
    _ -> :inequal
  end
end

Of course this particular example can be done with a pattern match as well, but I think you can see what I want to tell.

2 Likes

There’s no real difference between the two approaches, so it comes down to a specific case and personal preferences. Personally, I use case if the function body will be small enough even with case. If there are more branches and/or there’s more additional code in the function, I move branching into a multiclause to keep the “main” function code clear.

2 Likes

I don’t believe anyone answered your question about the default in the function head implementation. It would be like this:

def do_something_with(%{stuff:_}), do: ...

It would, of course, be the last clause.

oc

1 Like

I suppose I will use 2nd when you use recursion and you have edge cases.
Example:

defmodule Fib do 
  def fib(0) do 0 end
  def fib(1) do 1 end
  def fib(n) do fib(n-1) + fib(n-2) end
end
1 Like

As a newbie, I’m finding myself gravitating toward the second style because it helps me quickly spot which code-body is relevant to the case I’m thinking about. It seems more scannable because the multiple defs signal that there’s no code between the arguments and the case.

If I’ve to branch based on inputs, I’d do the overload function.

In a function, when handling outputs of other function, it reads better to use case than writing another function.

1 Like