If, else, and logical operators are probably the most common similarity between the popular languages, and unsurprisingly Elixir has them, too. The if
, case
, and cond
macros are provided for giving us control flow structure.
For pattern matching, we mentioned before that using case
provides matches against any pattern iteratively:
iex> checkUser = 'simon' 'simon' iex> case {checkUser} do ...> {'simon'} -> ...> 'User Match - Simon' ...> {'mary'} -> ...> 'User Match - Mary' ...> _ -> ...> 'This will match any value.. use as a catch-all' ...> end 'User Match - Simon'
Worth noticing here is the _
case, which is essentially a default catch-all case. The case can be also used with an atom
or any variable type like so.
iex> case {:user} do ...> {:user} -> ...> 'User Match' ...> _ -> ...> 'No match' ...> end 'User Match'
Guard Clause Expressions
Elixir provides many operators to check in our expression as a guard against catching the wrong data. By default, the following are supported:
- comparison operators (
==
,!=
,===
,!==
,>
,>=
,<
,<=
) - boolean operators (
and
,or
,not
) - arithmetic operations (
+
,-
,*
,/
) - arithmetic unary operators (
+
,-
) - the binary concatenation operator
<>
- the
in
operator as long as the right side is a range or a list - all the following type check functions:
is_atom/1
is_binary/1
is_bitstring/1
is_boolean/1
is_float/1
is_function/1
is_function/2
is_integer/1
is_list/1
is_map/1
is_nil/1
is_number/1
is_pid/1
is_port/1
is_reference/1
is_tuple/1
- plus these functions:
-
abs(number)
binary_part(binary, start, length)
bit_size(bitstring)
byte_size(bitstring)
div(integer, integer)
elem(tuple, n)
hd(list)
length(list)
map_size(map)
node()
node(pid | ref | port)
rem(integer, integer)
round(number)
self()
tl(list)
trunc(number)
tuple_size(tuple)
We can use them like so:
iex> case 1 do ...> x when is_number(x) -> "Number #{x}" ...> x when is_boolean(x) -> "Boolean #{x}" ...> end "Number 1"
Additionally, an anonymous function can have multiple guards. For example, to calculate a pivot point from a financial market data using the high, low, and close values, we can do this:
iex> pivot = fn ...> h, l, c when h < l -> "Error" ...> h, l, c -> (h + l + c) / 3 ...> end iex> pivot.(1233, 1212, 1226) # Usage: High, Low, Close.. 1223.6666666666667
Here, if the high value is less than low, the pivot anonymous function will return an Error message. This one-line fashion of writing guards is phenomenally powerful.
Cond
Like a switch
statement, Elixir's cond
is where we can perform a string of if else like blocks and execute on the match.
iex> cond do ...> 2 + 2 == 5 -> ...> "This is never true" ...> 2 * 2 == 3 -> ...> "Nor this" ...> true -> ...> "This is always true (equivalent to else)" ...> end "This is always true (equivalent to else)
Inside a cond
block, everything is evaluating to true except nil
or false
. That's all numerical values and strings included.
For example, we can check the current status for various possibilities with a cond
block:
iex> cond do ...> status == 'available' -> ...> "User is available" ...> status == 'busy' -> ...> "User is busy" ...> true -> ...> "No input provided" ...> end
When the value of status is set to a string, it will evaluate; otherwise, an error message will be output by default via the last true
case.
If and Unless
Elixir also provides us with unless
, which is a default part of the if else block and can be demonstrated as so:
iex> if true do ...> 'works' ...> end 'works' iex> unless false do ...> 'other case' ...> end 'other case'
When the if
case evaluates as true, unless
will allow you to have access to the opposite. As demonstrated, we can catch the other side of the conditional this way.
In a real-life example, this can be used for displaying conditional information during a conditional check, such as a reminder or hint.
iex> if t do ...> 'Process information here...' ...> end nil iex> unless false do ...> 'Hint to show user when information is not right' ...> end 'Hint to show user when information is not right'
Do/End Blocks
Throughout these examples, we have seen the usage of the do
and end
macros. Let's have a closer look at them now before we conclude this section:
iex> if true, do: 1 + 2 3
The comma after true
is for Elixir's regular syntax, where each argument is comma-separated. This syntax uses keyword lists, and when used in conjunction with the else
macro it will look like this:
iex> if false, do: :this, else: :that :that
To make this nicer for development, we can use the do/end
block, which does not require the comma for a direct approach:
iex> if true do ...> a = 1 ...> a + 10 ...> end 11 iex> if true, do: ( ...> a = 1 ...> a + 10 ...> ) 11
Conclusion
Elixir has the familiar control flow structures if/else
and case
, and it also has unless
and the do/end
block.
We can achieve any logic via manipulation of control flow with these structures. When you use them in conjunction with macros, anonymous functions, and modules, you have a great supply of options to get the work done.
For continued reading on the topic, the manual provides more insights into some of the further caveats of using the do/end
block, such as function scope limitations.
No comments:
Post a Comment