Single element tuple

I need to define type of an external function that returns an array with a single element. How can I do so?
Here is an example of such a function:

function example(x) {
  return [x + 1]
}

If I define it like so:

@val external example: (int) => (int) = "example"

The return type will be int, but I need a tuple of int:

// No compile time error, but at runtime we do [2] + 1
let test: int = example(1) + 1

Hi @rpominov

Tuples require 2 or more elements.

I believe that for your example you’ll need to make the return type an array.

Maybe you can provide some more context about why you need a type that represents an array with one element?

Hey @kevanstannard

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.

1 Like

I agree, that is basically what I have concluded as well after trying to come up with a solution for the couple days.

Still a bit annoying that a single value array can’t be modeled nicely in ReScript type system.

1 Like

Note you can make record encode as an array using attributes, so you can encode single element tuple as below:

type t = { @as("0") val : int}
let x = {val = 3}
5 Likes

Thanks @Hongbo

Just to clarify, it looks like this array encoding is triggered when:

[1] The keys are integers
[2] The keys are sequential
[3] The keys start from 0

And in other cases it’s encoded as an object.

Is that correct?

1 Like

yes, that is correct

1 Like

Nice! Thank you @Hongbo , I might use that