@deriving(abstract) and {..}

Hi. I want to use {..} as a field type with @deriving(abstract). But when I try to define it like so:

@deriving(abstract)
type config = {x: {..}}

I get this error: “A type variable is unbound in this type declaration. In field x: {…} as 'a the variable 'a is unbound”

I came up with this workaround:

type x

external makeX: {..} => x = "%identity"

@deriving(abstract)
type config = {x: x}

let myConfig = config(~x=makeX({"foo": 1}))

But is there a better way?

This is because {..} is indeed polymorphic as the compiler complains.

Try this instead:

@deriving(abstract)
type config<'a> = {x: {..} as 'a}
3 Likes

The other question would be why you need @derving(abstract) though :slight_smile:

Thanks I’ll try this.

I’m writing bindings for node-postgres and use deriving for the config object that has a lot of optional fields: https://node-postgres.com/api/client

The {..} is for the ssl field, BTW.

Is there something better than @derving(abstract) for this, than would not force users to explicitly set all optional fields to something like None?

config<'a> doesn’t quite work, because I also want this field to be optional. I get a error with this code:

@deriving(abstract)
type config<'a> = {@optional x: {..} as 'a}

// Works
let myConfig1 = config(~x={"foo": "bar"})

// Error:
//  This expression's type contains type variables that can't be generalized:
//    config<{_..}>
let myConfig2 = config()

yes you can use @obj like this:

type config

@obj external config: (~x: {..}=?, unit) => config = ""

Yes I think in this case you have to annotate your value:

let config: config<{.}> = config()

BTW we have a pretty complete set of bindings for Node-Postgres at work, it’s customized to our needs so not something we could really publish as is, but if you’re interested, it could be a good base for your own bindings. I’ll ask if we can share them.

3 Likes

@obj seem to work great so far, I don’t even need the type variable. Thank you!

@obj
external config: (~x: {..}=?, unit) => _ = ""

let myConfig1 = config(())

let myConfig2 = config(~x={"foo": 1}, ())

Yes, if you could share your bindings that would probably help me a lot. Would appreciate that!

Sorry yeah I’ll edit my solution, you’re supposed to keep your type opaque so no need for a type variable, indeed.

1 Like

There are changes coming in ReScript 10 that will make this sort of thing easier

4 Likes