Struct without fields

I just tried to create a struct without fields (because: why not?).
It’s not possible with defstruct, which insists on an argument.

Best it can do is

defstruct _: nil # nice

btw, this is not OK:

defstruct [_]

isn’t that inconsistent?

So I tried to figure out what defstruct does. From the docs:

To define a struct, a developer must define both __struct__/0 and __struct__/1 functions. defstruct/1 is a convenience macro which defines such functions with some conveniences.

Where are __struct__/x documented?

Structs are usually defined with the Kernel.defstruct/1 macro

What if I want to be unusual? How can I define a struct by hand, maybe even without fields?

…but what’s your goal?

1 Like

to create a struct without fields :upside_down_face:
Actually I really think I have a use case, but the main point is: just being curious.

Using an empty list as argument to defstruct/1works for me:

iex(1)> defmodule S do
...(1)>   defstruct []
...(1)> end
iex(2)> %S{}
%S{}
7 Likes

Like @Nobbz said, you can just do this:

defmodule X, do: defstruct []

The reason this doesn’t work is that you cannot assign _. The first example works because :_ is not _, so you can do defstruct [:_] which is equivalent to your first example.


This is the macro that’s called underneath when defining a new struct. Going over the definition should help you better understand manually defining a struct in a module.

1 Like

One way to define a struct is to use a simple map with the __struct__ key

Let’s say we have a module StructWithoutFields

we could call

iex> %{__struct__: StructWithoutFields} |> is_struct()
true

This will be a struct but we won’t be able to call %StructWithoutFields{}

The next step I’d suggest is looking into the macro implementation like @sheharyarn suggested.

I left a lot of parts out in the following example but this might give you an idea

defmodule StructWithoutFields do
  @struct %{__struct__: __MODULE__}

  def __struct__(kv) do
    Enum.reduce(kv, @struct, fn {key, val}, map ->
      Map.replace!(map, key, val)
    end)
  end

  def __struct__() do
    @struct
  end
end

With that module you will already be able to call

iex> %StructWithoutFields{} |> is_struct()
true

If you haven’t read it yet I can really recommend reading Metaprogramming Elixir: Write Less Code, Get More Done (and Have Fun!) by Chris McCord
It goes into a lot of detail on how macros work and how basically a lot of elixir is build upon macros

1 Like

thanks for the great advice, can’t fully understand the defstruct-macro yet, but I’ll take a look at the book. (feeling a little too stupid for that right now, not having thought about defstruct []).

1 Like

One legitimate reason would be to implement a protocol without data.

Use case: postgrex/parameters.ex at v0.16.5 · elixir-ecto/postgrex · GitHub

3 Likes