# 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``