But if you find yourself repeatedly passing around tuples of data that are isomorphic to your records, you might want to reconsider the design a bit? I.e., if you can construct a (book_name, chapter_number, verse_number, content) then you can just construct a verse directly.
You might find it useful to explain your problem space more concretely, as you might then get recommendation on idiomatic ways of addressing the problem.
I would wonder why you’re passing around the tuple, instead of just constructing the record early? Maybe there’s a good reason for it? But since a record is equally efficient to a tuple ……
You still get the problem. And labelled tuples suggested by @nojb will not help. Your database abstraction either exposes fixed tuples or constructor based column interfacing. You can’t have any labels here it wouldn’t be generic.
So even if your database abstraction allows to construct the record early (like rel or jsont which uses the same technique) you get the same problem: you need to write a constructor:
I think @dbuenzli surmised what I didn’t make precisely clear here, which is that I’m parsing tuples from Caqti to run database operations.
This would be more like what an ORM does, right?
When you say rel and jsont allow you to construct the record early, you’re saying that logically speaking, they take your object prior to hitting the database, and then they load the db rows into your object?
It’s not ORM. It’s converting the rows you are getting from any db request you make to the custom value you see fit without first going to a generic value (e.g. a fixed size tuple).
What it does is that given a row, the interface takes a constructor function for you custom value which has one argument per column and a (runtime) description of the type of the columns of the rows. Equipped with these, the db interface makes the requests and then on each row in the result gradually applies the row column data on your constructor until it yields your custom value.
If the record has exactly the same number of fields of the very same type, one could use Obj.magic in most cases I presume:
# type r = { a : int ; b : string };;
type r = { a : int; b : string; }
# type t = int * string;;
type t = int * string
# let a : t = 1, “uno”;;
val a : t = (1, “uno”)
# let b : r = Obj.magic a ;;
val b : r = {a = 1; b = “uno”}
(actually, I can’t think of a case where that would not work, but I’m sure others will, and that’s probably why nobody mentionned it earlier).
I remembered about the float array thing and actually did try that one, and it seemed to work:
# type r = { a: float; b: float };;
type r = { a : float; b : float; }
# type t = float * float;;
type t = float * float
# let a : t = 1., 2.;;
val a : t = (1., 2.)
# let b : r = Obj.magic a ;;
val b : r = {a = 1.; b = 2.}
# Gc.compact ();;
Gc.compact ();;
- : unit = ()
So I concluded that the same optimisation was applied on tuples, which made sense, and didn’t look further.