case

case lets you test a value against a set of patterns, executes the code associated with the first pattern that matches, and returns the value of that code. The patterns may include guard clauses.

For example, the File.open function returns a two-element tuple. If the open is successful, it returns {:ok, file}, where file is an identifier for the open file. If the open fails, it returns {:error, reason}. We can use case to take the appropriate action when we open a file. (Here, the code opens its own source file.)

control/case.ex
 case​ File.open(​"​​case.ex"​) ​do
 { ​:ok​, file } ->
  IO.puts ​"​​First line: ​​#{​IO.read(file, ​:line​)​}​​"
 { ​:error​, reason } ->
  IO.puts ​"​​Failed to open file: ​​#{​reason​}​​"
 end

produces

 First line: case File.open("case.ex") do

If we change the file name to something that doesn’t exist and then rerun the code, we instead get Failed to open file: enoent.

We can use the full power of nested pattern matches:

control/case1.exs
 defmodule​ Users ​do
  dave = %{ ​name:​ ​"​​Dave"​, ​state:​ ​"​​TX"​, ​likes:​ ​"​​programming"​ }
 case​ dave ​do
  %{​state:​ some_state} = person ->
  IO.puts ​"​​#{​person.name​}​​ lives in ​​#{​some_state​}​​"
  _ ->
  IO.puts ​"​​No matches"
 end
 end

We’ve seen how to employ guard clauses to refine the pattern used when matching functions. We can do the same with case.

control/case2.exs
 dave = %{​name:​ ​"​​Dave"​, ​age:​ 27}
 case​ dave ​do
  person = %{​age:​ age} ​when​ is_number(age) ​and​ age >= 21 ->
  IO.puts ​"​​You are cleared to enter the Foo Bar, ​​#{​person.name​}​​"
  _ ->
  IO.puts ​"​​Sorry, no admission"
 end