# Structs in erlang

**URL:** <https://erlangforums.com/t/structs-in-erlang/1811>\
**Category:** Chat / Discussions\
**Tags:** erlang-otp\
**Created:** [September 4, 2022, 6:28pm UTC](https://erlangforums.com/t/structs-in-erlang/1811 "2022-09-04T18:28:48Z")\
**Posts on this page:** 14\
**Page:** 2

<div class="post-metadata">

**Author:** ![starbelly](https://erlangforums.com/user_avatar/erlangforums.com/starbelly/32/209_2.png) [@starbelly](https://erlangforums.com/u/starbelly)\
**Post date:** [September 6, 2022, 1:54pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/21 "2022-09-06T13:54:36Z")

</div>

Yeah, but I’ve also experienced the inverse with just plain ole maps too. Except with plain ole maps the situation can be made worse in that some one can alter the shape by injecting arbitrary key/values, which can lead to strange yet serious problems in a distributed system.

I think ultimately there’s only one solution to these problems, and even they can be circumvented, but they are neither here nor now ya know?

---

<div class="post-metadata">

**Author:** ![josevalim](https://erlangforums.com/user_avatar/erlangforums.com/josevalim/32/353_2.png) [@josevalim](https://erlangforums.com/u/josevalim)\
**Post date:** [September 6, 2022, 1:57pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/22 "2022-09-06T13:57:42Z")

</div>

> [@starbelly](#):
>
> Quite curious what you think of dangerous here in regards to structs in Elixir.

It is not an issue with structs per-se. The issue is to assume that two fields with the same name share the same meaning. The same can happen when using maps. The same would be possible in the frames proposal.

In fact, I would say this thread misrepresents structs in Elixir. The desired functionality is actually achievable with maps. However, many resist migrating from records to maps because you lose compile-time checking of field names. That’s why structs come up, but that’s not why they exist. See next.

* * *

> [@rvirding](#):
>
> I personally feel that if we add something to Erlang called structs then they should be the same as Elixir structs otherwise it will create an enormous confusion. If we do add something else then give it another name. Then we can start arguing about what want and don’t want.

I actually think structs are “overused” in Elixir. The main goal of structs in Elixir is to be nominal, which is used both for protocol dispatching and the exception handling system. Given Erlang’s exception system won’t/shouldn’t change (especially as of EEP 54), I don’t see a reason for structs without protocols. 🙂

However, a lot of people use structs because it provides validation of the field names at compile-time. If Erlang/Elixir had other reliable mechanisms for validating the field names at compile-time when creating/matching maps (such as a type system), structs would be used less frequently because “verified maps” would be enough, so I would focus on tackling that instead.

* * *

As per the issue in this thread, I agree with @nzok. Defining an accessor function is the way to go:

```erlang
temperature(#frobnitz{temperature = X}) → X;
temperature(#snarkle{temperature = X}) → X.

```

However, what if you want to allow other records to be added here? You can’t without modifying the original module. That’s the goal behind structs+protocols in Elixir: you can define an accessor (actually, any behaviour) for any struct, at any time.

---

<div class="post-metadata">

**Author:** ![starbelly](https://erlangforums.com/user_avatar/erlangforums.com/starbelly/32/209_2.png) [@starbelly](https://erlangforums.com/u/starbelly)\
**Post date:** [September 6, 2022, 2:00pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/23 "2022-09-06T14:00:13Z")

</div>

> [@josevalim](#):
>
> I actually think structs are “overused” in Elixir.

I do agree with this, but people will abuse anything 🙂

> [@josevalim](#):
>
> However, a lot of people use structs because it provides validation of the field names at compile-time. If Erlang/Elixir had other reliable mechanisms for validating the field names at compile-time when creating/matching maps (such as a type system), structs would be used less frequently because “verified maps” would be enough, so I would focus on tackling that instead.

Well yes, if I that existed or could exist, I wouldn’t have bothered with discussing this 😉

---

<div class="post-metadata">

**Author:** ![cevado](https://erlangforums.com/user_avatar/erlangforums.com/cevado/32/3176_2.png) [@cevado](https://erlangforums.com/u/cevado)\
**Post date:** [September 6, 2022, 2:06pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/24 "2022-09-06T14:06:21Z")

</div>

> [@josevalim](#):
>
> It is not an issue with structs per-se. The issue is to assume that two fields with the same name share the same meaning. The same can happen when using maps. The same would be possible in the frames proposal.

that’s why I wanted named spaced atoms, just like qualified keywords in clojure 🙃

---

<div class="post-metadata">

**Author:** ![josevalim](https://erlangforums.com/user_avatar/erlangforums.com/josevalim/32/353_2.png) [@josevalim](https://erlangforums.com/u/josevalim)\
**Post date:** [September 6, 2022, 2:23pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/25 "2022-09-06T14:23:17Z")

</div>

> [@starbelly](#):
>
> Well yes, if I that existed or could exist, I wouldn’t have bothered with discussing this 😉

Haha. That’s what I mean though: we don’t want structs, we want “verified maps”. 🙂

> [@cevado](#):
>
> I see structs in Elixir as a dangerous solution because it’s error prone, in my opinion at least.  
> It gives you a sense of security where there is none, pattern matching over a struct name doesn’t ensure presence of all keys, clustered nodes with different definitions of the same struct can break stuff with bad data.

It is important to highlight that structs are nominal. With that in mind, I disagree with your conclusion.

For data versioning, a potential issue with nominal systems is that the name can refer to different structures over time. However, I would say this is a flaw in the data versioning, not in the names: **you shouldn’t make destructive changes to your schema**. This is the lesson everyone running a database in production or any over-the-wire serialization library will tell.

> [@cevado](#):
>
> that’s why I wanted named spaced atoms, just like qualified keywords in clojure 🙃

Unfortunately it still does not solve the problem that leads to structs being overused, which is that folks want compile-time validation of those keys, namespaced or not.

---

<div class="post-metadata">

**Author:** ![cevado](https://erlangforums.com/user_avatar/erlangforums.com/cevado/32/3176_2.png) [@cevado](https://erlangforums.com/u/cevado)\
**Post date:** [September 6, 2022, 2:39pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/26 "2022-09-06T14:39:01Z")

</div>

> [@josevalim](#):
>
> For data versioning, a potential issue with nominal systems is that the name can refer to different structures over time. However, I would say this is a flaw in the data versioning, not in the names: **you shouldn’t make destructive changes to your schema**. This is the lesson everyone running a database in production or any over-the-wire serialization library will tell.

I agree, my point is more on what people expects to work.  
let’s say i’m working with pure maps, although a key with the same name can mean different things overtime, if i’m pattern matching exclusively on a set of particular keys, i’m explicitly telling what shape of data I expect. I think overall data shapes are better than named entities.

> [@josevalim](#):
>
> Unfortunately it still does not solve the problem that leads to structs being overused, which is that folks want compile-time validation of those keys, namespaced or not.

Sure, but named spaced atoms would help dealing with a `key` having a singular meaning through your app. That can make nominal systems a less interesting solution. It would have another set of issues when abused, for sure. there are no consequenceless choices on programming 😔

---

<div class="post-metadata">

**Author:** ![Maria-12648430](https://erlangforums.com/user_avatar/erlangforums.com/maria-12648430/32/604_2.png) [@Maria-12648430](https://erlangforums.com/u/Maria-12648430)\
**Post date:** [September 6, 2022, 2:57pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/27 "2022-09-06T14:57:33Z")

</div>

> [@starbelly](#):
>
> people will abuse anything 🙂

So true 🙄  
But that is precisely why we should think twice before opening new avenues for abuse, or adding more things to be abused 😉

---

<div class="post-metadata">

**Author:** ![starbelly](https://erlangforums.com/user_avatar/erlangforums.com/starbelly/32/209_2.png) [@starbelly](https://erlangforums.com/u/starbelly)\
**Post date:** [September 6, 2022, 3:10pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/28 "2022-09-06T15:10:32Z")

</div>

That is a fair point. That said, I value all the conversation in this thread. I didn’t necessarily think this would result in consensus or an EEP, but sometimes a weird idea leads to a useful conversation and a good idea. I like the sound of verified maps, have no idea where’d we even begin to tackle that 🤔

---

<div class="post-metadata">

**Author:** ![Led](https://erlangforums.com/user_avatar/erlangforums.com/led/32/1594_2.png) [@Led](https://erlangforums.com/u/Led)\
**Post date:** [September 6, 2022, 9:41pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/29 "2022-09-06T21:41:32Z")

</div>

> I like the sound of verified maps, have no idea where’d we even begin to tackle that

Maybe from strict specs/typing and dialyzer?

---

<div class="post-metadata">

**Author:** ![starbelly](https://erlangforums.com/user_avatar/erlangforums.com/starbelly/32/209_2.png) [@starbelly](https://erlangforums.com/u/starbelly)\
**Post date:** [September 7, 2022, 3:34am UTC](https://erlangforums.com/t/structs-in-erlang/1811/30 "2022-09-07T03:34:25Z")

</div>

I’m not sure dialyzer is the place for that. I’m not sure it isn’t either 😄

```erlang
-module(hmm).

-type this() :: #{this => atom(), that => atom()}.

-export([eh/0, foo/1, bar/0]).

-spec eh() -> this().
eh() ->
  #{this => that, that => that}.

bar() ->
    foo(eh()).

foo(This) ->
    This#{foo => bar}.

```

I guess the question is does tracking the the instance of `this()` without spec hints go out of scope for dialyzer? My gut says yes, but I would defer to type theorists and the like to answer that question.

Edit:

To note… this is a case where bar and foo should have specs, but let your imagination run wild as to why a module in some code base may have ended up like this 🥳

---

<div class="post-metadata">

**Author:** ![rvirding](https://erlangforums.com/user_avatar/erlangforums.com/rvirding/32/178_2.png) [@rvirding](https://erlangforums.com/u/rvirding)\
**Post date:** [September 7, 2022, 10:56am UTC](https://erlangforums.com/t/structs-in-erlang/1811/31 "2022-09-07T10:56:04Z")

</div>

Just to clarify what I meant:

- If we add a new structure to Erlang then **DON’T** call them `structs`, it will just cause confusion.
- Add completely compatible Elixir structs to Erlang to make interfacing between the languages easier. They may be “overused” but they are still there. It is not difficult to do.

---

<div class="post-metadata">

**Author:** ![starbelly](https://erlangforums.com/user_avatar/erlangforums.com/starbelly/32/209_2.png) [@starbelly](https://erlangforums.com/u/starbelly)\
**Post date:** [September 7, 2022, 2:42pm UTC](https://erlangforums.com/t/structs-in-erlang/1811/32 "2022-09-07T14:42:38Z")

</div>

Makes sense. A new data structure I think would not be great with every other thing going on, tough sell 🙂 Thus why I was originally thinking about how to build a table on top of a table, for good or bad.

---

<div class="post-metadata">

**Author:** ![RMorisset](https://erlangforums.com/user_avatar/erlangforums.com/rmorisset/32/1601_2.png) [@RMorisset](https://erlangforums.com/u/RMorisset)\
**Post date:** [September 8, 2022, 11:05am UTC](https://erlangforums.com/t/structs-in-erlang/1811/33 "2022-09-08T11:05:10Z")

</div>

I don’t know Elixir, so I may be misunderstanding things.

From the discussion above, the main two things missing in maps for this usecase are performance and having the keys checked at compile-time.

EqWAlizer (which was recently open-sourced: [GitHub - WhatsApp/eqwalizer: A type-checker for Erlang](https://github.com/WhatsApp/eqwalizer)) can track the shapes of maps, which as far as I can tell would solve most of the compile-time checking desires listed above.

As for performance, inline caching is the traditional answer to having fast maps in a dynamic language. The idea is to have as much sharing as possible of the Keys tuple, so that {x =\> 1, y =\> 2} and {x =\> 3, y =\> 4} both point to the same {x, y} tuple. Then record for each code location that access a map the last value of that tuple pointer seen, and the corresponding offset. And finally, whenever accessing a map, first check whether the Keys tuple pointer match the recorded one, and if it does then go straight to the right offset without searching through the map. As a bonus, with an optimizing JIT it is possible to eliminate redundant checks of that keys pointer.  
It may sound complicated (and it is much easier to explain on a whiteboard), but it works exceptionally well even for languages like JS where every object is effectively a map.

So I believe that as long as typechecking existing maps is good enough, this new type is not required. What could make a new type required is if a nominal type is wanted (such that maps that have the same keys by accident don’t have the same type).

---

<div class="post-metadata">

**Author:** ![starbelly](https://erlangforums.com/user_avatar/erlangforums.com/starbelly/32/209_2.png) [@starbelly](https://erlangforums.com/u/starbelly)\
**Post date:** [September 9, 2022, 12:55am UTC](https://erlangforums.com/t/structs-in-erlang/1811/34 "2022-09-09T00:55:03Z")

</div>

> [@RMorisset](#):
>
> From the discussion above, the main two things missing in maps for this usecase are performance and having the keys checked at compile-time.

Dialyzer and eqwalizer will both currently catch this in general and do support most of what’s sought after (so it’s really already there in some respects), the rub is cases like the one posted above. The nice thing about structs in elixir is the check does not depend on an external tool and will catch such a case (unless I go really out of my way to evade it, which I can, but it has to be quite intentional).

> [@RMorisset](#):
>
> EqWAlizer (which was recently open-sourced: [GitHub - WhatsApp/eqwalizer: A type-checker for Erlang](https://github.com/WhatsApp/eqwalizer)) can track the shapes of maps, which as far as I can tell would solve most of the compile-time checking desires listed above.

Yes, seems like it would, but if you look at the example I posted, it like dialyzer does not handle such a case, **yet** 😄

> [@RMorisset](#):
>
> As for performance, inline caching is the traditional answer to having fast maps in a dynamic language. The idea is to have as much sharing as possible of the Keys tuple, so that {x =\> 1, y =\> 2} and {x =\> 3, y =\> 4} both point to the same {x, y} tuple. Then record for each code location that access a map the last value of that tuple pointer seen, and the corresponding offset. And finally, whenever accessing a map, first check whether the Keys tuple pointer match the recorded one, and if it does then go straight to the right offset without searching through the map. As a bonus, with an optimizing JIT it is possible to eliminate redundant checks of that keys pointer.  
> It may sound complicated (and it is much easier to explain on a whiteboard), but it works exceptionally well even for languages like JS where every object is effectively a map.

Yup, makes sense. I suppose what you’re suggesting builds upon the value sharing that’s already in place? : [https://www.erlang.org/doc/efficiency\_guide/maps.html](https://www.erlang.org/doc/efficiency_guide/maps.html)

Also, wouldn’t a key difference between maps in erlang and “maps” in other languages be the underlying datastructure. As an example, in perl a hash is backed by a hash table, whereas in erlang it’s a sorted list up until we hit \> 32 keys, at which point it becomes a HAMT… ?

[Previous page](https://erlangforums.com/t/structs-in-erlang/1811.md?page=1)
