Rob1
Have you ever run ETS, Elixir’s famous in-memory caching mechanism?
If not, I have prepared this short code sample you can clone and run as a single file to demonstrate it in use, just start it from the command line with elixir cloudcomputing.exs
I am just learning ETS myself, so any feedback about my file is welcome for those who have run it.
Trending in Discussions
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...
New
Hey there,
It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Quite interesting article Google brought me. Didn’t find any mentions about it here.
What do you think in general? Would you use togethe...
New
Hi everyone!
The first release candidate for the Expert language server project is now available!
We’ve published a press release detai...
New
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New
What IDE or editor are you using for Elixir development?
Personally, I use Zed, and I really like it, but sometimes I wish there were a ...
New
Other Trending Topics
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
tj0
Nice. FYI, there’s an Elixir wrapper for ETS.
I’ve never used Elixir for scripting, so I’m sure there’s a way to add dependencies, but not sure offhand.
kokolegorille
I can see some possible enhancement…
currently You use the table as if it was private… but You set it as public.
I prefer protected
it’s a good place to use trap exit, and manage task death in the genserver
Just some quick things I would do
I also prefer Process.send_after instead of :timer.sleep
If I can I would avoid using :timer
schneebyte
Well for match specs i might use a wrapper/library, but otherwise i try to just use the
:erlangstuff directly, no unnecessary abstraction.~3k LOC just so i can write
ETSinstead of:ets.And its missing :ets.lookup_element/4.
I would replace all those
:ets.tab2list(:sensor_data) |> Enum.filterwith
:ets.match/match_delete/select/select_deleteRob1
Thanks for the tip! I’ve made this replacementt.
Here’s the explanation of the the $1 and $2 there, which I wasn’t familiar with:
"
The code snippet you’re asking about uses a feature of Erlang’s ETS (Erlang Term Storage) that’s also available in Elixir, as Elixir is built on top of the Erlang VM. The syntax with
:"$1",:"$2", etc., is specific to ETS and represents a pattern matching and guard expression used in ETS select and delete operations. Let’s break down the specific line::ets.select_delete/2: This is an ETS function used to delete entries from an ETS table based on a match specification. The first argument is the name of the ETS table (:sensor_datain your case), and the second argument is a match specification that determines which records to delete.[{{:"$1", :"$2"}, [{:<, :"$1", oldest_allowed_time}], [true]}]is a list that describes how to match and delete the records. Let’s dissect this:{{:"$1", :"$2"}}: This part is the pattern. It matches tuples where the first element is bound to:"$1"and the second element to:"$2". In ETS match specifications,:"$1",:"$2", etc., are placeholders that correspond to the elements of the tuples stored in the ETS table. In your case, each tuple in the:sensor_datatable is{time, simulated_temp}, so:"$1"matchestimeand:"$2"matchessimulated_temp.[{:<, :"$1", oldest_allowed_time}]: This is the guard sequence. It applies additional conditions to the matched records. Here, it checks if the time (:"$1") is less than (:<)oldest_allowed_time. Only records that satisfy this condition will be considered for deletion.[true]: This is the result list. It indicates that for records that match the pattern and pass the guard sequence, the actiontrue(which means delete) should be applied.So, in simpler terms, this line of code tells ETS to delete all records from the
:sensor_datatable where the timestamp is older thanoldest_allowed_time.This kind of operation is both powerful and efficient, as it allows complex match and guard conditions to be executed directly within the ETS table, avoiding the overhead of pulling data into process memory for filtering and processing. The syntax might seem unusual at first, especially if you’re primarily used to Elixir’s syntax, but it’s a direct use of Erlang’s powerful pattern matching capabilities within ETS.
"
What do you think of this explanation?
Rob1
Thanks for these tips! You mentioned “just some quick things”, if it doesn’t take you long could you make a pull request? For me, I am not confident in appplying your three changes, I am still learning Elixir and had help generating the code to begin with. (Specifically: I haven’t used handle_continue and am not totally clear about where to add it, I am not completely clear on the private/public/protected distinction you mention, and I am not confident applying your spawn changes.
It sounds like you know all three of your suggestions (four with send_after) and could do it quickly, so if it will improve the demonstration code I would merge your PR to improve the example.
Otherwise I would have to wait until I learn the concepts you’ve mentioned in a bit more detail.
So far this is still a learning exercise for me too
I appreciate the feedback.
kokolegorille
Your code…
In FP functions returns something… It’s nice to write pure functions fn input → output
end
You are writing methods, probably with side effects, we don’t know the input, we don’t know the output
This would be my init
Just above init, I would write the API, You just wrote the callbacks
Something like this
Your code…
Do You know it returns a ref?
This ref could be stored in the server state
Because some day, You might want to cancel it
Your code…
My code… with some modifications. I would keep the ref in the state
If the state is a struct, with ref defined, I would then return the modified state with
%{state | ref: ref}
Be careful with this syntax… It works only if state is a struct with ref as field
But You get the idea… the function takes an input, and output a modified version of the state
Your code… it’s too low level to use spawn, or spawn link
Prefer the task module
Also… it should be at least explicative of what it returns, even if You don’t use pid
_pid = spawn(fn → analyze_temperature() end)
Your code reflects something You would write in other languages
OTP is a delightful piece of software, but can be tricky to write, and Functional Programming can be tricky too. In particular if You are an experienced OOP programmer
kokolegorille
Also I would try not to mix the gen_server code with the business logic code.
I would prefer having a Sensor context, with only pure functions, separated from time concern…
…and use the genserver only to call these context module functions, when time comes.
Rob1
Great observation, and I like your style such as ending a function on a line like:
ref = Process.send_after(self(), :read_sensor, 1)
or your other example
_pid = spawn(fn → analyze_temperature() end)
I think that makes it very clear what the function is returning (a reference and pid in these two cases, and you also show when you are not using it via the underscore.)
I think that aids in writing very literate programming, so I will start using this style in my code when I can.
Where you write:
They couldn’t really be pure functions, though, since reading the sensors will not always return the same value.
Thank you again for the code review, as I get more advanced I will practice more of those techniques. I’ll try to incorporate some of these changes now, I’ll post the updates.
Rob1
I’ve now made some of your suggested changes here, I didn’t totally rework the structure but I see what you mean about a separate API and changing the init to return
{:ok, %{}, {:continue, :load_data}}and then have adef handle_continue(:load_data, state).The reason I didn’t make the change totally is that you only provided part of the code here and I am still just getting used to dealing with passing data around like this, so I preferred to keep the application in a working state. But I see what you were referring to.
kokolegorille
You should look at how Ecto deals with external data. You could have a SensorApi providing data. This data is casted and validated to valid Sensor data
Once casted and validated, You can have a pure Core
I don’t know what You retrieve, but You could have pure functions doing Celsius ↔ Fahrenheit conversion
Of course it’s overkill for simple project, but I would not mind using multiple modules