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?
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).
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
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)>
You could also use a combination of and (or &&) and ||, as in this example:
foo = is_integer(foo) and Integer.to_string(foo) || foo