d4mr
If structs are just maps underneath then how does pattern matching to ensure struct type work?
For example:
defmodule Attendee do
defstruct name: "", paid: false, over_18: true
def may_attend_after_party(attendee = %Attendee{}) do
attendee.paid && attendee.over_18
end
def may_attend_after_party_without_struct(attendee = %{name: "", paid: false, over_18: true}) do
attendee.paid && attendee.over_18
end
end
iex(1)> Attendee.may_attend_after_party(%Attendee{name: "John"})
false
iex(2)> Attendee.may_attend_after_party(%Attendee{name: "John", paid: true})
true
iex(3)> Attendee.may_attend_after_party_without_struct(%Attendee{name: "John", paid: true})
** (FunctionClauseError) no function clause matching in Attendee.may_attend_after_party_without_struct/1
The following arguments were given to Attendee.may_attend_after_party_without_struct/1:
# 1
%Attendee{name: "John", over_18: true, paid: true}
structwtf.exs:8: Attendee.may_attend_after_party_without_struct/1
iex(4)>
%Attendee{} evaluates to %Attendee{name: "", over_18: true, paid: false}, and if structs are just maps underneath, then shouldn’t the behaviour remain similar?
Trending in Questions
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
Hello,
I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind.
However, when I launch mix phx.server, I get an error...
New
Hi everyone,
I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding.
I sta...
New
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
I’m working on a small exercise involving update_in/3, and I came up with this solution:
data = %{
name: "Periodic Table",
category:...
New
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication):
toke...
New
Other Trending Topics
Edit: 2026 May 15 - This post is archived.
Mob is alive!!
Main docs: mob v0.7.11 — Documentation
A bit of explanation for the slightly c...
New
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hobbes is a low-level distributed database for the Elixir programming language.
Hobbes provides a simple, safe, and scalable storage lay...
New
Hi everyone!
The first release candidate for the Expert language server project is now available!
We’ve published a press release detai...
New
A little off-topic, but I feel like people here have a good head on their shoulders.
I used to be quite good at making software. Was luc...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ai
- #ecto-query
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #metaprogramming
- #hex










Showing Posts 1 to 8- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
al2o3cr
Your example code omits the definition of
Attendee.may_attend_after_party_without_struct/1; I trieddef may_attend_after_party_without_struct(attendee = %{}) doand it worked, so you’ll need to provide more information about what behavior you’re expecting.d4mr
My bad, updated the example
ityonemo
Structs have a magic hidden key
__struct__which gets implicitly matched on when you prepend the struct module in between % and {. The default key/values are not filled in for matches, just the struct key.Eiji
In fact they are!
What happen here? First of all the
structis not valid only because “some map” have__struct__key even if value is valid. The map needs to have all keys available for suchstruct. The rest is sugar when inspecting.The
%{}syntax or more precisely%special form is just handled differently. Just to imagine:The only difference between
add/2andadd_alt/2is that first returns just the result and second wraps it in “ok-tuple” which is useful for error handling.If you check how it’s
ASTyou would see that’s almost the same as in my example:So inside
%special form simple map (%{}) is used. The special form simply adds to yourmapastructkeys with their default values ornilif default value is not specified.For more information see: %/2 special form
IloSophiep
I’m not certain, but i think the way you pattern match in your function “without struct” is not what you intend to do. What you have is:
That means only maps are “matched”, that
I think what you actually want is just matching the existence of the keys, as in the following
See how i tell the function head that i want the map to contain those three keys (
:name,:paidand:over_18), but i discard the value of those keys - that way i “allow” any value.Is this what you were hoping for?
d4mr
I understand your pattern matching snippet, and in fact that is what I would expect to work.
However, because structs are matched in this manner:
and because
%Attendee{}evaluates to%Attendee{name: "", over_18: true, paid: false}I expected the other behaviour to work.However @ityonemo has given a very concise answer about why this works and @Eiji has provided an amazing demonstration of the fact.
Thanks a lot! This was my first interaction with the Elixir community, and makes me very happy to be a part of it
IloSophiep
Do you mean those are the default values, if you create an empty Attendee struct? Because as with maps - at least i think - the pattern matching for “apparently empty stuff” is maybe not instantly obvious:
As you can see, the “empty map”
%{}doe not only match empty maps, but any map. That tends to be very useful for the typical use. But that also means your usage ofdoes not mean you are trying to match an empty attendee or a default attendee. It means you are matching any attendee struct, no matter the contents.
I hope that was clear - does it make sense to you, the way i described it?
al2o3cr
“Evaluates to” is the gotcha here, the arguments of a function are always a “match context” which has special properties - this:
and this:
both result in the same function that matches a map with a
single_valuekey and bindswhole_argandv.This is not the same
=that you get when writing those statements alone:I suspect this is why it’s a common convention to write pattern-matches on structs on the left-hand side:
as it’s clearly distinguishable from the “evaluation”
=.