If else pattern improvement

Consider the following if statement to check if a variable should be type casted.

foo = "123"

foo = if is_integer(foo) do
  Integer.to_string(foo)
end

if it is not an integer, foo will be nil, so to prevent this from happening, I have to do

foo = "123"

foo = if is_integer(foo) do
  Integer.to_string(foo)
else
  foo
end

I found myself doing this pattern a lot from time to time, is there a better way to express this pattern?

1 Like

Probably matching in the header and delegating after conversion is the most correct.

But otherwise in this specific case you can just replace the whole if thing with just foo = to_string(foo).

1 Like

To me, if you have to do this a lot inside your code, it is a sign that your code can be improved.

Your internal representation of your data should be clear and unambiguous. This avoids heaps of bugs and reduces code.

In general you should do all type validation and conversion when things enter your system (from external source, from APIs if you are writing a library). Then from there on you should trust the types are correct otherwise I’d recommend that you just use let it crash.

For APIs I usually function guards I’d do something like

def my_fun(x) when is_integer(x), do: Integer.to_string(x)
def my_fun(x), do: x
7 Likes

And do it fast?

i.e.

# file: demo.ex
defmodule Demo do

  def stringify(value) when is_integer(value) do
    Integer.to_string(value)
  end
  def stringify(value) when is_binary(value) do
    cond do
      String.valid?(value) ->
        value
      true ->
        raise ArgumentError, message: "cannot stringify value #{inspect value}"
    end
  end

end
iex(1)> c("demo.ex")
[Demo]
iex(2)> Demo.stringify(123)
"123"
iex(3)> Demo.stringify("123")
"123"
iex(4)> Demo.stringify(<<239, 191, 19>>)
** (ArgumentError) cannot stringify value <<239, 191, 19>>
    demo.ex:12: Demo.stringify/1
iex(4)> Demo.stringify(:some_atom)      
** (FunctionClauseError) no function clause matching in Demo.stringify/1    
    
    The following arguments were given to Demo.stringify/1:
    
        # 1
        :some_atom
    
    demo.ex:4: Demo.stringify/1
iex(4)> 

2 Likes

You could also use a combination of and (or &&) and ||, as in this example:

foo = is_integer(foo) and Integer.to_string(foo) || foo

3 Likes