this repo has no description
1* Functions and Pattern Mathching
2
3Behind the scenes, functions are patter-matching the arguments that they're called with.
4Say we needed a function to accept a map but we're only interested in using a particular key.
5We can pattern-match the argument on the presences of that key like this:
6
7#+NAME: Greeter1
8#+begin_src elixir
9defmodule Greeter1 do
10 def hello(%{name: person_name}) do
11 IO.puts "Hello, " <> person_name
12 end
13end
14#+end_src
15
16#+begin_src elixir
17fred = %{name: "Fred", age: "95", favorite_color: "Taupe"}
18
19Greeter1.hello(fred)
20#+end_src
21
22#+RESULTS:
23: Hello, Fred
24: :ok
25
26What happens when we call the function with a map that doesn't contain the =:name= key?
27
28#+begin_src elixir
29Greeter1.hello(%{age: "95", favorite_color: "Taupe"})
30#+end_src
31
32#+RESULTS:
33: ** (FunctionClauseError) no function clause matching in Greeter1.hello/1
34:
35: The following arguments were given to Greeter1.hello/1:
36:
37: # 1
38: %{age: "95", favorite_color: "Taupe"}
39:
40: iex:2: Greeter1.hello/1
41: iex:2: (file)
42
43The reason for this behavior is that Elixir pattern-matches the arguments that a function is called with against the arity the function is defined with.
44
45In =Greeter1.hello/1=, the map we pass (=fred=) is evaluated against our argument (=%{name: person_name}=):
46
47#+begin_src elixir
48%{name: person_name} = ${name: "Fred", age: "95", favorite_color: "Taupe"}
49#+end_src
50
51It finds that there is a key that corresponds to =name= in the incoming map. We have a match!
52And as a result of this successful match, the value of the =:name= key in the map on the right (i.e. the =fred= map) is bound to the variable on the left (=person_name=).
53
54Now, what if we still wanted to assign Fred's name to =person_name= but we ALSO want to retain awareness of the entire person map?
55Let's say we want to =IO.inspect(fred)= after we greet him.
56At this point, because we only pattern-matched the =:name= key of our map, thus only binding the value of that key to a varaible, the function doen't have knowledge of the rest of Fred.
57
58In order to retain in, we need to assign that entire map to its own variable for us to be able to use it.
59
60Let's start a new function:
61#+begin_src elixir
62defmodule Greeter2 do
63 def hello(%{name: person_name} = person) do
64 IO.puts "Hello, " <> person_name
65 IO.inspect person
66 end
67end
68#+end_src
69
70#+RESULTS:
71: {:module, Greeter2,
72: <<70, 79, 82, 49, 0, 0, 6, 168, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 218,
73: 0, 0, 0, 24, 15, 69, 108, 105, 120, 105, 114, 46, 71, 114, 101, 101, 116,
74: 101, 114, 50, 8, 95, 95, 105, 110, 102, 111, ...>>, {:hello, 1}}
75
76Remember that Elixir will pattern match the argument as it comes in.
77Therefore in this case, eacm side will pattern match against the incoming argument and bind to whatever it matches with.
78Let's take the right side first:
79
80#+begin_src elixir
81person = %{name: "Fred", age: "95", favorite_color: "Taupe"}
82#+end_src
83
84Now, =person= has been evaluated and bound to the entire fred-map.
85We move on to the next pattern-match:
86
87#+begin_src elixir
88%{name: person_name} = %{name: "Fred", age: "95", favorite_color: "Taupe"}
89#+end_src
90
91Now this is the same as our original =Greeter1= function where we pattern matched the map and only retained Fred's name.
92What we've achieved is two variables we can use instead of one:
93
941. =person=, referring to =%{name: "Fred", age: "95", favorite_color: "Taupe"}=
952. =person_name=, referring to ="Fred"=
96
97So now when we call =Greeter2.hello/1=, we can use all of Fred's information:
98
99#+begin_src elixir
100Greeter2.hello(fred)
101
102Greeter2.hello(%{name: "Fred"})
103#+end_src
104
105#+RESULTS:
106: Hello, Fred
107: %{age: "95", favorite_color: "Taupe", name: "Fred"}
108: Hello, Fred
109: %{name: "Fred"}
110
111So we've seen that Elixir pattern-matches ata multiple depths because each argument matches tha incoming data independently,
112leaving us with the variables to call them by inside our function.
113
114*Summary*: Functions pattern-match the data passed in to each of its arguments independentry.
115We can use this to bind values to separate variables within the function.
116* Default arguments
117
118If we want a default value for an argument we use the =argument \\ value= syntax:
119
120#+begin_src elixir
121defmodule Greeter do
122 def hello(names, language_code \\ "en")
123
124 def hello(names, language_code) when is_list(names) do
125 names = Enum.join(names, ", ")
126
127 hello(names, language_code)
128 end
129
130 def hello(name, language_code) when is_binary(name) do
131 phrase(language_code) <> name
132 end
133
134 defp phrase("en"), do: "Hello, "
135 defp phrase("es"), do: "Hola, "
136end
137
138Greeter.hello ["Sean", "Steve"]
139Greeter.hello ["Sean", "Steve"], "es"
140#+end_src
141
142#+RESULTS:
143: [33mwarning: [0mredefining module Greeter (current version defined in memory)
144: iex:1: Greeter (module)
145:
146: "Hola, Sean, Steve"