Why do structs accept keyword lists as args and not maps?

Hi

I’ve been learning Elixir lately and there’s something that’s been confusing me.

An Elixir struct accepts a keyword list as it’s argument, yet it does not behave
like a keyword list. In fact it behaves almost identical to a map.

Why does a struct accept keyword lists as it’s argument if it’s got more in common
with a map?

1 Like

Structs are maps underneath, they just have an extra key :__struct__ that points to their module, and also keys are only allowed to be atoms.

But what do you mean by “An Elixir struct accepts a keyword list as it’s argument”? A struct does not take any arguments at all…

1 Like

The struct command does… but probably because it’s simpler to pass Keyword as function argument.

struct Page, title: "koko"
%CmsEx.Cms.Page{
  __meta__: #Ecto.Schema.Metadata<:built, "pages">,
  children: #Ecto.Association.NotLoaded<association :children is not loaded>,
  depth: nil,
  id: nil,
  inserted_at: nil,
  lft: nil,
  parent: #Ecto.Association.NotLoaded<association :parent is not loaded>,
  parent_id: nil,
  rgt: nil,
  title: "koko",
  updated_at: nil
}
1 Like

Yeah, struct/2 and defstruct/1 do so, because its easier to deal with in the AST and also a lot easier to write I assume.

Compare defstruct %{a: "", b: :c} vs. defstruct A: "", b: :c.

But at least for struct/2 any enumerable is allowed according to the typespecs. So struct Page, %{title: "koko"} is as valid as struct Page, title: "koko" is.

5 Likes

In order to create a struct you have to pass it a keyword list:

defmodule User do
  defstruct [name: "", age: nil]
end

Why do we do that and why don’t we pass in maps, since Structs are really maps.
For example is_map(%User{}) would return true. But then Structs accept keyword
lists instead of maps, which is kind of weird I think.

1 Like

Thanks that makes sense!

1 Like

You defining it (at compile time), not creating it (at runtime).

defmodule User do
  defstruct [name: "", age: nil] # defining struct at compile time
  
  def new(name, age \\ nil) do
    %User{age: age, name: name} # creating struct at run time
  end
end
3 Likes