maxim

maxim

Hi folks,

I’ve written custom types before, but this specific case gives me pause, and I wonder if you could explain your reasoning here when deciding how to split this logic between cast and dump.

I have a list of base-3 integers that I store in the database as a :binary (postgres bytea).

The value that I assign looks like this (up to 127 digits):

[0,2,1,0,2,2,1]

The value database stores looks like this

[0,2,1,0,2,2,1] |> Enum.reverse |> Integer.undigits(3) |> :binary.encode_unsigned
# <<5, 112>>

However, I have to run 3 checks:

  1. Is this a list?
  2. Is length in 0..127
  3. Are all digits in 0..2?

What part of this goes into cast, and what goes into dump?

Here’s what I think, correct me if I’m wrong:

  • Cast runs the above 3 checks, but keeps it a list.
  • Dump ALSO runs the 3 checks (in case value was assigned without cast) but converts it into a binary.

However, those 3 things are quite a bit of code. Between 2 guards and an Enum.any? they traverse the whole list a couple of times. If this is correct, am I supposed ignore the fact that I’m running the same logic twice, once for cast and once for dump?

Showing Posts 1 to 9

NobbZ

NobbZ

I’m not sure where to put the checks, but at least I can tell you that you only need to iterate once, roughly like this:

def valid_custom_type?(list) do
  list
  |> Enum.reduce_while(0, fn
    _d, l when l > 127 ->
      {:halt, :invalid}
    d, _l when d not in 0..2 ->
      {:halt, :invalid}
    _d, l ->
      {:cont, l + 1}
  end)
  |> is_integer()
end

This will iterate not more than once and not more than the first 128 digits of the list.

Due to the early return it should not become a bottleneck that soon.

maxim

maxim OP

Thanks for a useful tip, will definitely combine count and validation. That said I’m still interested in cast vs dump reasoning. :slight_smile:

LostKobrakai

LostKobrakai

Ecto.Types handle 3 different data representations. Outside data (most often from form inputs, or json fields in the db), runtime data (how your data is put into your structs by ecto and used at runtime) and the database representations as db column.

cast goes from outside data → runtime data
dump goes from anything (outside data or runtime data) → database column data
load goes from database column data → runtime data

For json (map) fields there’s also
json encoding from anything → database json value
cast from database json value → runtime value

The reason why cast/json encoding need to deal with “anything” is because there’s nobody stopping you from inserting data via Repo.insert_all or manipulating a struct manually and there’s no casting involved. So any validation of correct data needs to be done in both cast and dump, where one converts to runtime data and the other to database column data.

maxim

maxim OP

That’s a great explanation, and from what I gather, for the question of “Should we be ok with doing the same work twice, once in cast and once in dump” the answer is “yes”.

And since outside data can also be formatted consistently with runtime data, would it be more correct to say that

cast goes from anything to runtime data
dump goes from anything to database column data

With this in mind, it’s almost always ok to actually call cast function from your dump function, does this sound right? (I know it depends, but I’ve noticed I could do that in most custom types I wrote).

ream88

ream88

Omg, can this answer please be included in the official Elixir docs? Maybe as a cheatsheet. I’ve never read a better explanation than this one! :blush:

axelson

axelson

Scenic Core Team

I like visuals so I made a visual to represent @LostKobrakai’s awesome explanation:

And one thing that sometimes confuses (me at least) is that sometimes the “Outside data” and “Runtime data” have the same representation, for example for Ecto.UUID. Seeing it in this visual form makes the reason behind that easier for me to understand/remember:

10
Post #6
dimitarvp

dimitarvp

Awesome visualizations. Instant bookmark.

gregvaughn

gregvaughn

That would be awesome in a documentation PR!

LostKobrakai

LostKobrakai

Already merged:

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
psy-q
I’m trying to set up Emacs with elixir-ls via lsp-mode and credo via Flycheck. This should mostly be preconfigured as Flycheck picks up c...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
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
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
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
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews