chrisliaw

chrisliaw

Hi,

I’m wondering is it my thinking process or this is the norm among the Elixir developer for the use of Struct and accessor functions (get/set)?

For example:

defmodule Session do
    defstruct [:name]

    # this is just example as there might be 
    # more transformation before putting the value inside the struct
    def set_name(%Session{} = session, name) do
        %Session{session | name: String.upcase(name)}
    end
    
    # likewise there might be more transformation before returning the value
    def get_name(%Session{} = session) do
        String.downcase(session.name)
    end
end

The reason to create the accessor is somehow (probably OO habit die hard, or not?) along the argument of to isolate the user of the struct to access the key directly to allow future changes to the struct key without affecting the caller. For example later if the struct change the field to :customer_name, caller using the get_name() function would not aware the field has changes its name.

But this pretty much feels like OO thinking in me. Is that an alternative or is that a sensible way to design this structure?

Cons is now get/set is everywhere inside the struct module.

Thanks!

Regards

Showing Posts 1 to 10

Eiji

Eiji

There are lots of packages dedicated for structs:
Search results for: struct access @ hex.pm

Many of them adds support for Access behaviour, so you can use it with *_in functions like get_in.

That’s rather not about FP or OO paradigms, but about struct documentation. Some parts may be private, so if they are not well documented then the developer assumes that specific keys are private and should be not changed by hand. Documenting all fields would require a support for them in future as long as you would not hard deprecate them.

User-defined types | Typespecs reference @ Elixir documentation

Not really objective if you do not update the object … No worries for that. If it’s not a “simple struct” then you can follow Phoenix generators i.e. use context-based functions to fetch data from database and use struct (in ecto it’s called schema) module to define other helper functions like get_full_name etc.

dimitarvp

dimitarvp

If you’re asking if people regularly create struct getters / setters in Elixir then I’d say no.

There are legitimate exceptions when you need calculated values or you need to massage the value but they always were the very small minority in my work.

I’m seeing no reason to generate them, too. Elixir is quite terse; whenever you need a few of them, typing them out still looks like the fastest way of doing it.

chrisliaw

chrisliaw OP

Hi thanks for the answer.

But if we allowed directly accessing the key won’t that be a nightmare for code maintenance and refactoring later in the stage?

dimitarvp

dimitarvp

Why would it be a maintenance nightmare? Do you plan on changing those important structs every week?

Of course changes do happen, yes, but nobody has actually found a way to isolate the programmer from having to refactor and re-test, I believe.

If you find yourself in a situation where you have to modify important business code structs so often then that’s a symptom of other problems.

D4no0

D4no0

If you get out of your head the idea that tying data structures + behavior to modules is a good idea, you will understand that operating with data structures directly, without tying them to additional logic that is part of the same module, is easier to reason about in the code and more maintainable.

I honestly don’t like the fact that structs are tied to a module, there are technical reasons why it was done like this, however at the same time it sends a wrong message to people coming from OOP languages. The scope of the structs is to have something that resembles enforceable types, that can be checked easily by tools like dialyzer, tying behavior to those types is not the most optimal way to write code.

If you are just starting out, my advice is to just avoid using structs until you get a good hang on how to write elixir, use guards/pattern match to achieve the same guarantees structs offer. If you’ll use data structures that are not tied to any modules for some time, it will start to click in place the ideology where you design pipelines that transform data, instead of imperative logic tied to a module.

chrisliaw

chrisliaw OP

Thanks for the insight.

For example in use case like there are data that is belongs to a group and I don’t want to spin off a GenServer since there is no reason to spin up one. The logic can be a library that a group of functions working on a group of data that is similar in nature. For example I have a login, password, role, contact fields those belongs a particular user. If this is not structured inside a struct/map, how would this be stored under non GenServer approach?

I may have a function to check for login, another for password, yet another check on role. If a struct/map is not provided then we left for the caller to find a way to keep that related info at their side?

My usual use case for struct/map is to keep those related info in a single module so that I have a holistic view on what fields being passed around between the functions and to reduce the parameter need to pass into each functions. Is that a correct way to think about this?

D4no0

D4no0

GenServer is a runtime construct, completely different to how a class in languages like java works, each genserver is associated with a process. You use genservers when you have concurrency involved (think of them as analogy to green threads or coroutines), you shouldn’t use them for data encapsulation, as that is a clear misuse in most cases.

You don’t have to define all the contracts with structs, that is one way to do things, but not the only one. Since elixir is dynamic by nature, you can have dynamic data structures, for which you can enforce a shape when you need to. A very generic example:

credentials = %{username: "Daniel", password: "123123"}

def check_username(%{username: username}) do
...
end

def check_password(%{password: password}) do
  ...
end

The data structure is dynamic, however you will get an error instantly if you are missing one of those fields, this is partial validation and it’s used a lot in elixir. You could also enforce the structure completely:

def check_credentials(%{username: _username, password: _password} = creds) do
...
end

Elixir inherently doesn’t suffer from this problem, you can build your custom types on the fly and since you have pattern match, you don’t have to pay for structure deconstruction with imperative code like in many other languages:

tuple = {:this, "tuple"}
map = %{key1: "value", key2: "value2"}
list = [a, b, c]

# I want the first element of the list
def hello([head | _tail]) do

# I want all the list elements and I know the list has 3 elements
def hello([first, second, third]) do

# I have a well-defined tuple where I know the second element is a string
def hello({_first, second}) when is_binary(second) do

# I want to make sure the map has key1 present, don't care about other ones
def hello(%{key1: _value} = map) do

# Finally, you can pass all these values around without deconstruction
# if you don't care about their contents from context of that function
def hello(tuple, map, list) do

This might be fighting your static typing nature at first, however this is just as maintainable as having those structs specified in a module plus with the addition that is a lot more readable and makes for code that is easier to understand.

dimitarvp

dimitarvp

This now sounds like a bunch of Ecto.Schema modules to me. You can model them that way, especially if you have persistence coming down the road (databases).

But no don’t use GenServer for this, it’s something that “lives” – as in, runs continuously – and it’s not necessary for what you have in mind.

Structs are completely fine but you do indeed have to learn to do pipelines of transformations. Thinking in “methods” and “getters / setters” will not help. Think of what must happen.

Speaking of which – what’s your goal here? What code do you lack and what code do you want to have?

chrisliaw

chrisliaw OP

My doubt is how to structure the Elixir code so future maintenance is easier and is my current way to use struct is the way it should be done. Not really lacking anything just to verify the thinking / software design of how I approach Elixir. I’ve background in Java, DotNet, C & Ruby however, not sure I’m bringing the correct thinking/design methodology.

felix-starman

felix-starman

This reminds me of a Scope struct that a coworker wrote.

It is still just a data structure with keys for the current user struct, the user permissions/roles, and a few other things like the organization.

It used guards heavily for the “important parts” but after the guard matched in the function head or in the function body, digging into the keys wasn’t a big deal because the compiler would complain if you tried to access keys that shouldn’t exist.

Modifying that Scope struct when logging in, or authorizing them, was done with setters, but that was more because of the sensitive nature of the use case and consistency, not because of the an Elixir idiom.

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 92995 915
New
AstonJ
The obligatory hello world thread! Who are you and where are you from? :stuck_out_tongue:
4616 55835 594
New
caslu
I want to open this thread for you all to discuss and help those who really like Ash but are still hesitant to use it in a real project. ...
New
Herve37
We’re evaluating API mocking tools for OpenAPI-based projects and would love to hear what other teams are using. We’re particularly inte...
New
matt-savvy
Is there a word for the ~> symbol used in Version strings? Do you also just call it a Squiggle Arrow™ ?!
New
GES233
I’m posting this in response to Jose’s recent tweet (Cr. link) : People are sleeping on Elixir for a coding harness: Hot-code swappi...
New
nseaSeb
AcmeScript — Writing JS hooks as if I were still using Elixir I’ve been having fun building a little something over the last few days: Ac...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New
webofbits
Aludel - LLM Evaluation Workbench Aludel is an embeddable Phoenix LiveView dashboard for evaluating and comparing LLM prompts across mult...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews