The Finnternet Like the Internet but with more me

Drink the Elixir: Using the With Construct

Coming from a background in imperative languages, I faced a few hiccups in my Elixir journey. One of the first logical differences I hit was a result of Elixir’s lack of early exits/returns.

Take the code below which is a common pattern in imperative code,

if (num <= 0)  {
	return "must be >0";
} 

if(num >= 5) {
	return "must be <5";
} 

return "valid input"

or alternatively using nested if/else

if (num <= 0)  {
	if(num >= 5) {
		return "valid input";
	} else {
		return "must be <5";
	}
} else {
	return "must be >0";
}

The first pattern cannot be done in Elixir and the second quickly leads to ugly, deeply nested code. You should instead use the with construct. As you can see in the functionally identical code below, there is a with block in which you declare multiple pattern matching clauses which if all passed, executes the do block and if any fail to match, executes the else block where pattern matching is done on the clauses there.

def inputTest(num) do
	with     {:ok}  <- gt_zero?(num),
		 {:ok}  <- lt_five?(num)
	do
		"valid input"
	else
		{:error, reason} -> reason
	end
end

def gt_zero?(num) do
	if num >= 0 , do: {:ok}, else: {:error, "must be >0"}
end

def lt_five?(num) do
	 if num <= 5, do: {:ok}, else: {:error, "must be <5"}
end

This pattern is flexible, reads well, and is a much better alternative to if/else craziness. I find myself using the with construct fairly often since it replaces the above patterns and I prefer its syntax over the pipe operator since you need to use a third party library to add error handling.