Right, array<int> is one of the options, and another one that I consider is (int, ()). Both are not ideal because they will be confusing to the users of my library, and may require extra boilerplate on the user end.
I need this for a library that generates types for SQL queries. For example for SELECT 1, 2; the rows type will be array<(int, int)>. But in the case of SELECT 1; I have to do something like array<array<int>> or array<(int, ())>.
What do the library function calls look like that generate the SQL? For example, what do the calls look like to generate SELECT 1; and generate SELECT 1, 2;?
Also, do you need to cater for different column types?
It will generate ReScript types for arbitrary SQL queries that the user provides. Basically turns an .sql file into a .res file. Here is an example of kind of code that it will produce:
Taking a look at the code, it seems that your run() function is no problem since it’s returning a record type.
However the runArray() function is the challenge since that’s where you’re using a tuple to model the array that’s returned from the database (and provide a type for each array element).
Just a few thoughts:
It might be worth considering if you need to support the array API. Since your target audience for this generated code is ReScript developers, then maybe the record type is all that’s needed and avoid the awkwardness of an array that contains different types.
If you feel it’s useful to support the array/tuple, then, from what I’ve seen in ReScript code, it’s common to just return a single value immediately when only one value is needed, but return a tuple when 2 or more values are needed. For the single value version you’ll need to transform the array provided by the database into the single value.