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.)
| 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:
| 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.
| 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 |