Alexandre Roba
Alexandre Roba

Reputation: 107

'Case X do' always take the first option

I'm looping on list of values and in the list I have a case to choose what should be returned. The code loop code looks like:

value = code &&& key
IO.puts "+++++"
IO.puts "key:#{key},code:#{code},code &&& key:#{value},value == key:#{value == key}"
case value do
  key -> IO.puts "value is true"
  _ -> IO.puts "value is false"
end

The log output of those calls is:

+++++
key:1,code:1,code &&& key:1,value == key:true
value is true
+++++
key:2,code:1,code &&& key:0,value == key:false
value is true
+++++
key:4,code:1,code &&& key:0,value == key:false
value is true
+++++
key:8,code:1,code &&& key:0,value == key:false
value is true
+++++
key:16,code:1,code &&& key:0,value == key:false
value is true

I'm getting always the "value is true" and we can clearly see the case value is false. What happens here?

Thanks for any help.

Upvotes: 2

Views: 80

Answers (2)

Adam Millerchip
Adam Millerchip

Reputation: 23091

You are printing value is true, but that's not actually the case. You should put the expected value to the left of the ->.

one = 1
case one do
  true -> IO.puts("value is true")
  false -> IO.puts("value is false")
end

This produces: ** (CaseClauseError) no case clause matching: 1

The problem with using variables on the left is that the variables are rebound to that scope. If there is already a variable of that name, it will get re-bound during the case statement:

one = 1
two = 2
case one do
  two -> IO.puts("value matches itself (#{two})")
  _ -> throw("neven happens")
end

This prints value matches itself (1), because one is assigned to two and always matches itself.

In the case where the expected value is in a variable, you need to use the pin operator as Justin Wood pointed out:

one = 1
two = 2

case one do
  ^two -> IO.puts("value matches the variable in two")
  _ -> IO.puts("#{one} doesn't match #{two}")

This prints 1 doesn't match 2.

By the way, the reason for this behaviour is so that you can do pattern matching with the left hand side and capture the variable for use on the right:

map = %{a: "a", b: "b"}

case map do
  %{a: a} -> IO.puts("map's a key contains a value of #{a}")
  _ -> IO.puts("map doesn't contain key a")
end

Upvotes: 0

Justin Wood
Justin Wood

Reputation: 10061

This is exactly how a case expression should be working. You put the value of value into the key variable. If you want to match value against the current value of the key variable instead of shadowing it, you will need to use the pin operator.

case value do
  ^key -> ...
  _ -> ...
end

Upvotes: 5

Related Questions