You cannot view this unit as you're not logged in yet. Go to Account to login.
15 Comments
McKinnin Lloydon January 27, 2020 at 10:04 pm
I have a question. Why in the function get_name it will throw an error if you do %User{name: name} and not %{name: name}, but in the create_greeting function it is opposite. In that function it wants you to do %User{name: name}?
That’s a good question! In the get_name example, we want the pattern to be less specific. By not specifying the struct type, we match on any map data type (including structs) but only if they have a key called :name. For get_name, it would also be valid to write this:
def get_name(%Customer{name: name}), do: {:ok, name}
def get_name(%User{name: name}), do: {:ok, name}
def get_name(_other), do: {:error, "Doesn't have a name"}
This is more specific. We give an explicit pattern match on a customer and user struct. However, the results of the functions are identical. It doesn’t actually matter that it’s a Customer or a User struct type. It is better to match more generally to only the data that we actually care about for getting the :name from the data.
Then in the create_greeting function we want to be more specific with our match because we want a different result based on the data type.
Another way to describe the behavior in get_name is like this: “if the data has a key called :name then return the value”. It doesn’t care about what kind of struct. Then create_greeting is saying “if it is a customer then greet this way. If it is a user then greet this other way.” The best way to tell them apart is by their struct types.
I am really struggling with Q1. Your solution renders an error, and requires me to offer the more comprehensive solution employing User & Customer. When I do this, (as above), I get another error:
“error: PatternMatching.User.__struct__/0 is undefined, cannot expand struct PatternMatching.User. Make sure the struct name is correct. If the struct name exists and is correct but it still cannot be found, you likely have cyclic module usage in your code
│
18 │ def get_name(%User{name: name}), do: {:ok, name}
│ ^
│
└─ structs.ex:18:16: PatternMatching.Structs.get_name/1
** (CompileError) structs.ex: cannot compile module PatternMatching.Structs (errors have been logged)
structs.ex:18: (module)”
The error `PatternMatching.User.__struct__/0 is undefined` usually means you are missing an `alias` statement. The compiler is saying, “I don’t know what struct you’re talking about.” The statement is `alias PatternMatching.User`.
defmodule PatternMatching.Structs do
@moduledoc “””
Fix or complete the code to make the tests pass.
“””
alias PatternMatching.Customer
alias PatternMatching.User
def get_name(%{name: name}), do: {:ok, name}
def get_name(_other), do: {:error, “Doesn’t have a name”}
def create_greeting(_value) do
end
def deactivate_user(_user) do
end
end
warning: unused alias Customer
│
13 │ alias PatternMatching.Customer
│ ~
│
└─ structs.ex:13:3
warning: unused alias User
│
14 │ alias PatternMatching.User
│ ~
│
└─ structs.ex:14:3
Mark Ericksenon June 29, 2024 at 10:51 am
Hi Marcus! I’m not sure what the problem is. Your solution here is correct and the tests in Exercise 1 pass correctly. I’m not seeing a problem. The compiler warnings are just that the aliases weren’t used, which is fine at this point in the exercise.
Is there something else that you’re seeing?
romenigldon December 14, 2020 at 7:42 am
I loved this example.
It’s like when you will refactor the code.
You match on any map data type (including structs) and has less lines of code to write and understand.
Thank’s!
For the colors, on this page it’s just text using a web syntax highlighter for the Elixir language. So it didn’t come out of my IEx terminal looking like that. Sorry!
When the key is an atom, it can use the %{key: value} form. When the key is a string or other data structure, it uses the “arrow” form. It is also valid to write %{:key => 1}. When you write that in IEx, you will see it returned as %{key: 1}. So it’s really “syntactic sugar” that makes maps more readable when the key is an atom. It was initially covered here: https://thinkingelixir.com/course/pattern-matching/module-2/map/
Takudzwa Gwindingwion November 2, 2021 at 11:03 am
Loving this course. Also enjoying reading the comments. Could you kindly explain how we were using the Customer and User structs in the structs.ex when they are defined in their respective files.
I think you are asking how the structs can be referenced without something like an “include” or “require”? That’s at least the question I’ll try to answer. In Elixir, all modules and types are available globally. There is no need to “require” or “import” or anything like that. What keeps them separate and organized is their namespace. So you could reference a struct using it’s full name like this: %PatternMatching.Customer{} at anytime anywhere in your code. However, that can be awkward and verbose. If you have a file that repeatedly deals with that struct, it’s common to “alias” it at the top of the file. This only creates a shortcut to the name you want to reference inside the module you are writing.
In the code and test files, it looks like this: alias PatternMatching.{Customer, User}. Aliasing multiple things at a time like this (using the {}) is discouraged. It’s considered a bad practice because it’s confusing and doing a Find/Replace doesn’t work well.
I had forgotten that the examples used this approach and I should update them to be a better example. It should instead be like this:
alias PatternMatching.Customer
alias PatternMatching.User
I hope that answered the question you were asking!
I have a question. Why in the function get_name it will throw an error if you do %User{name: name} and not %{name: name}, but in the create_greeting function it is opposite. In that function it wants you to do %User{name: name}?
That’s a good question! In the
get_name
example, we want the pattern to be less specific. By not specifying the struct type, we match on any map data type (including structs) but only if they have a key called:name
. Forget_name
, it would also be valid to write this:def get_name(%Customer{name: name}), do: {:ok, name}
def get_name(%User{name: name}), do: {:ok, name}
def get_name(_other), do: {:error, "Doesn't have a name"}
This is more specific. We give an explicit pattern match on a customer and user struct. However, the results of the functions are identical. It doesn’t actually matter that it’s a Customer or a User struct type. It is better to match more generally to only the data that we actually care about for getting the
:name
from the data.Then in the
create_greeting
function we want to be more specific with our match because we want a different result based on the data type.Another way to describe the behavior in
get_name
is like this: “if the data has a key called:name
then return the value”. It doesn’t care about what kind of struct. Thencreate_greeting
is saying “if it is a customer then greet this way. If it is a user then greet this other way.” The best way to tell them apart is by their struct types.Did I answer your question?
I am really struggling with Q1. Your solution renders an error, and requires me to offer the more comprehensive solution employing User & Customer. When I do this, (as above), I get another error:
“error: PatternMatching.User.__struct__/0 is undefined, cannot expand struct PatternMatching.User. Make sure the struct name is correct. If the struct name exists and is correct but it still cannot be found, you likely have cyclic module usage in your code
│
18 │ def get_name(%User{name: name}), do: {:ok, name}
│ ^
│
└─ structs.ex:18:16: PatternMatching.Structs.get_name/1
** (CompileError) structs.ex: cannot compile module PatternMatching.Structs (errors have been logged)
structs.ex:18: (module)”
The error `PatternMatching.User.__struct__/0 is undefined` usually means you are missing an `alias` statement. The compiler is saying, “I don’t know what struct you’re talking about.” The statement is `alias PatternMatching.User`.
Hi Mark. I am not seeing that.
defmodule PatternMatching.Structs do
@moduledoc “””
Fix or complete the code to make the tests pass.
“””
alias PatternMatching.Customer
alias PatternMatching.User
def get_name(%{name: name}), do: {:ok, name}
def get_name(_other), do: {:error, “Doesn’t have a name”}
def create_greeting(_value) do
end
def deactivate_user(_user) do
end
end
warning: unused alias Customer
│
13 │ alias PatternMatching.Customer
│ ~
│
└─ structs.ex:13:3
warning: unused alias User
│
14 │ alias PatternMatching.User
│ ~
│
└─ structs.ex:14:3
Hi Marcus! I’m not sure what the problem is. Your solution here is correct and the tests in Exercise 1 pass correctly. I’m not seeing a problem. The compiler warnings are just that the aliases weren’t used, which is fine at this point in the exercise.
Is there something else that you’re seeing?
I loved this example.
It’s like when you will refactor the code.
You match on any map data type (including structs) and has less lines of code to write and understand.
Thank’s!
Another really great exercise. Thank you for taking the effort to do this for all of us trying to learn
Great lessons – Thanks! I’m really internalising the Elixir way thanks to these!
Could I ask you how you got the great colorization of IEX as seen in the first code example on this page?
For the colors, on this page it’s just text using a web syntax highlighter for the Elixir language. So it didn’t come out of my IEx terminal looking like that. Sorry!
Hi ! What is the main difference between using
%{key: value}
and%{key => value}
? . Thanks for the great lessons.When the key is an
atom
, it can use the%{key: value}
form. When the key is a string or other data structure, it uses the “arrow” form. It is also valid to write%{:key => 1}
. When you write that in IEx, you will see it returned as%{key: 1}
. So it’s really “syntactic sugar” that makes maps more readable when the key is an atom. It was initially covered here: https://thinkingelixir.com/course/pattern-matching/module-2/map/Loving this course. Also enjoying reading the comments. Could you kindly explain how we were using the Customer and User structs in the structs.ex when they are defined in their respective files.
I think you are asking how the structs can be referenced without something like an “include” or “require”? That’s at least the question I’ll try to answer. In Elixir, all modules and types are available globally. There is no need to “require” or “import” or anything like that. What keeps them separate and organized is their namespace. So you could reference a struct using it’s full name like this:
%PatternMatching.Customer{}
at anytime anywhere in your code. However, that can be awkward and verbose. If you have a file that repeatedly deals with that struct, it’s common to “alias” it at the top of the file. This only creates a shortcut to the name you want to reference inside the module you are writing.In the code and test files, it looks like this:
alias PatternMatching.{Customer, User}
. Aliasing multiple things at a time like this (using the{}
) is discouraged. It’s considered a bad practice because it’s confusing and doing a Find/Replace doesn’t work well.I had forgotten that the examples used this approach and I should update them to be a better example. It should instead be like this:
I hope that answered the question you were asking!
Thanks Mark! These’re great explanations of Struct. I like the way of studying through examples ⛰️