Invalid args for operator "in" used in guard expressions

Can someone please tell me what have I done wrong here ?

defmodule Calculus do
	
	#calculate(expression) takes a single-quoted string of the form of number [+-*/] number and does the calculation
	#ex : calculate('5*3') => 15

	def calculate(expression) when (is_list(expression) and ?+ in expression) do
		term1(expression) + term2(expression)
	end

	def calculate(expression) when (is_list(expression) and ?- in expression) do
		term1(expression) - term2(expression)
	end

	def calculate(expression) when (is_list(expression) and ?* in expression) do
		term1(expression) * term2(expression)
	end

	def calculate(expression) when (is_list(expression) and ?/ in expression) do
		div(term1(expression),  term2(expression))
	end


	# term1(expression) returns the first term and transforms it to integer 
	# ex : Calculate.term1('123+124') = '123'
	defp term1(expression) when is_list(expression) do
		List.to_integer(elem(split_terms(expression),0))	
	end

	# sign(expression) returns the sign
	# ex : Calculate.sign('123+124') = '+'
	defp sign(expression) when is_list(expression) do
		[hd(elem(split_terms(expression),1))]
	end

	# term2(expression) returns the second term transforms it to integer 
	# ex : Calculate.term1('123+124') = '124'
	defp term2(expression) when is_list(expression) do
		List.to_integer(tl(elem(split_terms(expression),1)))
	end

	# split-terms(expression) splits the expression to term1 and sign++term2
	# ex : Calculate.split_terms('123+124') = {'123', '+124'}
	defp split_terms(expression) do
		Enum.split_while(expression, &(&1 in ?0..?9))
	end

end

The full error message explains it:

** (ArgumentError) invalid args for operator “in”, it expects a compile-time list or compile-time range on the right side when used in guard expressions, got: expression

i.e. you are trying to use it on a runtime list which could take a long time to run Enum.member?/2 on.

To understand the constraints of guard sequences please see Erlang Reference Manual; Expressions: 8.25 Guard Sequences

Essentially only operations that have no side effects and are predictably “fast” are allowed in guards.

You are probably better off recursively scanning through expression producing an operand, operator and rest any time you encounter one of “±*/”.

2 Likes

Thank you, after reading the documentation I understood what expression can be used as guard.
I learned Regex and applied it in this example.
My program now works even when the argument contains spaces.

defmodule Calculus do
	def calculus(expression) when is_binary(expression) do
		expression |> _split_terms |> _terms_to_integer |> _operation
	end
	# operation executes the calculation depending on the operator 
	defp _operation([left,"+",right]) do
		left + right
	end
	defp _operation([left,"*",right]) do
		left * right
	end
	defp _operation([left,"-",right]) do
		left - right
	end
	defp _operation([left,"/",right]) do
		left / right
	end
	# Transforms the terms to integers 
	#ex : Calculus._terms_to_integer([" 128 + 50 ", "128", "+", "50"]) = [128, "+", 50]
	defp _terms_to_integer([_,left,operator,right]) do
		[String.to_integer(left),operator, String.to_integer(right)]
	end
	# Separate terms
	# ex : Calculus._split_terms(" 128 + 50 ") = [" 128 + 50 ", "128", "+", "50"]
	defp _split_terms(expression) do
		Regex.run(~r/\s*(\d+)\s*([+-\/*])\s*(\d+)\s*/,expression)
	end
end