arashm

arashm

When to use Struct?

I read the “Programming Elixir” book, here is quote from the book:

At some point, the old object-orientation neurons still active in the nether regions of your brain might burst into life and you might think, “Hey, this is a bit like a class definition.” And you’d be right. You can write something akin to object-oriented code using structs (or maps) and modules.
This is a bad idea. Not because objects are intrinsically bad, but because you’ll be mixing paradigms and diluting the benefits a functional approach gives you.

Having this in the back of my mind, today I’m making a simple app which relies on data comes from a JSON API. The data structure is deeply nested and the API on different end-points uses different keys for same objects (imagine in one it returns name in another it returns title). So I thought I’d convert those JSON data to different structs and define a to_normalized_struct function in those to convert them to a struct that provides a same interface to be used through out the app.

But then… I thought maybe I’m coding OO here and may face wrath of functional programming gods later. So when is right to use structs? How would you implement the code for above problem?

Most Liked

slashdotdash

slashdotdash

A useful comparison of structs and maps is made in When to Use Structs, String-keyed Maps, and Atom-keyed Maps.

It lists six rules governing the best practice usage of these data structures.

  1. Always Use String-Keyed Maps for External Data.
  2. Convert External Data to Structs ASAP.
  3. Use Structs in All Other Code.
  4. Use Structs for Output Data.
  5. Avoid Using Atom-keyed Maps That Aren’t Structs.
  6. Use Keyword Lists Only for Function Arguments
bbense

bbense

My 2 cents. The way out of the OO trap is to think data and transformations.

I can’t answer your specific question w/o knowing the larger context, it might make sense to simply create a transform function for each JSON type to a standardized data form. Or it might make sense
to avoid the central form and just make transforms that extract the data you need.

The way to answer that question is to think about any transforms you need for the resulting data. What data structure makes the most “sense” in your problem? Rather than modeling “things”[1], start thinking about transforms and the inputs and outputs they need.

The Struct construct handy in that it provides both an easy to read access syntax and it constrains your transforms to a specific set of keys in a Map. However, the problem with Struct is the dual of it strengths, you lose the ability to dynamically change the structure of the data as the problem evolves. Struct requires that you “model” the problem in advance. This is not a terrible thing; in fact the strong type people would claim this is a very good thing. It does take away some of the flexibility and power of a dynamic language.

So as a practical guide, I’d suggest that if you are either constantly adding fields to your Struct or creating many slightly different, but almost the same structs, then you probably don’t understand the problem well enough yet to use a struct. The other thing to be aware of is that Struct only constrains the keys, not the values. If you find yourself putting wildly different values under the same struct key, then you’re probably in the weeds.

[1]- Experience suggests this is also the source of most problems with OO design as well.

AstonJ

AstonJ

I am just going through the Elixir and Phoenix Bootcamp course and in my notes I have written:

Structs

Just like Maps but with 2 advantages:

  • Can be assigned default values
  • Additional compile-time checking of properties

If we know the properties we are going to be working with we will use a Struct instead of a Map.

peerreynders

peerreynders

elixir-lang: Structs

Structs are extensions built on top of maps that provide compile-time checks and default values.

Maps aren’t necessarily less OO than Structs - it’s all about how you use them.

OO on the other hand will often manage and encapsulate “state” within object instances - so if most of your “transformations” look like they are simply advancing the state of a thing that is represented by a single key-value collection then there may be cause for concern because the ultimate judgement is context sensitive. For example it is accepted practice that a single key-value collection can represent the state of an Elixir process.

I think sloppy terminology isn’t helping in this case. While it’s called “JavaScript Object Notation” JSON doesn’t contain objects in the OO sense because an OO object collocates data and the functions that operate on it - JSON isn’t allowed to contain any functions - so you are left with pure data. Also does “name” and “title” actually return data that is identical or do they merely return data that has the same shape - i.e. given that “name” and “title” fulfill a similar role do they simply use the same data type?

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New

We're in Beta

About us Mission Statement