Fair warning I come from an OOP backround so I might be missing something very obvious here.
Hierarchical Selection of value
Lately I have been trying to go over my elixir project and try to identify potential patterns in the code. One such pattern that I “identified” from my own code when I was working with developing custom components like for example a LiveSearchSelect component etc is the need to get a value through a hierarchical structure like for example get the field value from
The form params (for example to get the values of the users latest changes)
but if this is nil
The changeset(For example if the form param are empty due to an update triggered through the changes happened from a javascript trigger interceptor which also triggers re render of the custom component)
but if this is also nil
Maybe some case that exist to save user progress
but if this is also nil
“empty string”
So I found this as a let’s say re occurring problem and I came up with a name in order to standardize a solution on this kind of problem for the context of my project at least.
The solution I found to be the most elegant and easy to maintain is the following
So I wanted to share this mainly to ask whether other developers can relate ?? and if you have also “project level” patterns with standardized solutions on common problems of either your domain or in general when working with Elixir PhoenixLiveView and relevant technologies.
The reason for the case do structure was that no matter the amount of layers there is not going to be any nesting, which would lead to code that is easy to test and maintain in general. Although I hear the argument about not needing to get all the values, in most cases this is very cheap operation. So in order for the with to make sense, it would need to not make any sacrifices in the “complexity/nesting” side of things, so how would you approach the same problem if you used with instead ?
About the first part I understand that you still need to define other outcomes for example what happens if you end up getting a result from the “get_second_val” then you still need to match that in the else statement making the hierarchy of the code less apparent (the way I see it) and when it comes to the second approach it’s for sure the most elegant but for me it did not work on the debug phase.
But in general the point of the thread was not to argue about the particular peace of code but rather open a discussion of less common design patterns that might be somewhat domain specific on the context of Phoenix LiveView does this make sense ?
This is pretty much the canonical with use-case as mentioned above, but for times when you already have all of the values a cond can also be used for nil-handling.
cond do
first_value -> whatever(first_value)
second_value -> second_value
true -> default
end
Which is equivalent to this (but easier to read if there are many cases):
cond do
first = get_first() when not is_nil(first) -> ...
second = get_second() when not is_nil(second) -> ...
third = get_third() when not is_nil(third) -> ...
true -> whatever
end
if you see not is_nil(value) getting repeated a lot, i’d define a guard in this module:
Fully agreed on cond. It seems many people forget about it and I actually know people who actively avoid it (which I don’t fully understand). It’s probably the least used construct but really useful when it is
Otherwise, for specific LiveView stuff, I would need to see more code as there may be cleaner ways of structuring it. For example (and this keeps coming up lately), I very very rarely look at params—I would pass them right to the changeset then check if changes is empty. Otherwise, if a whole key is missing, I would maybe handle these in different handle_event matches. But again, very hard to say without seeing more explicit code.
PS lol at your new avatar, @cevado
EDIT: It may have been a glitch but your avatar has changed again.
I personally like cond too instead of the generic nested case or if/else, but I am not a big fan of assignments in either cond/if, it’s a little bit too much magic, it’s completely confusing for folk that didn’t write elixir and even for newcomers.
I do this constantly. I feel understanding that everything is an expression is required knowledge if you’re going to be writing Elixir. A bit off topic, though.
I think I disagree here, a lot of languages allow assignment in if/for, and it’s much more intuitive in Elixir because variables are scoped much more carefully (the values must be returned from the cond block).
There are no variables in elixir, and = is a match operator, not assignment as in many other languages. When you start to see people go to town with more advanced matches inside of these flow conditions, it becomes really hard to reason about, even if the intent of that code was simple to begin with.
I think this is one of the reasons why people prefer working with erlang, heard it from multiple people in the last few years, that doesn’t have this kind of magic.
Both the Elixir and Erlang syntax references call them variables, but I see what you’re getting at. What I meant was that in another language, say JS, this is confusing:
let x = 0;
if (x =1) { x = 2; }
What is x here? What if we definedx inside the condition? And so on.
But this can’t happen in Elixir because the assignment in the if condition is properly scoped and those scopes are very consistent because the language is designed well.
But certainly I can see a point in Elixir where this could start to get ugly, like this:
cond do
({:ok, {x, _y, _z}} = do_thing()) && x -> x
end
wait what?
i changed the old one(cover of an album from a brazilian band called papangu) to the current one(a frame of the video for “freak of the week” from freak kitchen, a swedish band).
I believe it was a browser glitch on my end. It wasn’t rendering properly (even though all others were) and made it look like ones of those avatars people use where it looks like there is dirt on your screen so you try and wipe it
To answer the question in the title of the thread: Yes, every project I make accumulates little patterns and representations that I then reuse in other projects.
These build up very quickly, at least one new pattern per week. It’s like I’m building a dialect of idioms. (Of course, I prefer to use existing idioms when possible to preserve a common dialect.)
I don’t really have any specific examples to share since the applications are pretty niche until you actually need them, but I have found through trial and error what others are saying about using || to prevent expensive operations. e.g. use some.thing || some_expensive_operation() instead of Map.get(some, :thing, some_expensive_operation()) to avoid calling the function unless it is absolutely necessary.