this repo has no description
at main 4.8 kB view raw
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: warning: redefining module Greeter (current version defined in memory) 144: iex:1: Greeter (module) 145: 146: "Hola, Sean, Steve"