Another cast vs dump question for a custom Ecto type

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?