Can you specify an object type as function parameter?

I’m working on a small abstraction type for mongodb that I can use as kind of a dependency injection that I can mock easier. Since the mongo query API is pretty complex, I’d like to just accept any {..} as a parameter for the query

type query<'a> = {
   find: ({..}) => promise<string>
}

I’m getting “In field find: ({…} as 'a) => promise the variable 'a is unbound” - It seems like the {..} syntax only works for external bindings. Is there another type I can use? An example of what I’m trying to produce:

let postWorkouts = async (q, workouts) => {
  let existing = await q.find({ 
    "userId": workouts.userId
  })
}

https://rescript-lang.org/try?version=v10.1.2&code=C4TwDgpgBAjgrhATiAPAcgIYD4oF4oDeAZgJYB2AJgFxRoBGAdFABQEMMC+UGAzrXQEo8OMIgD2AWxI8IKHsETkA5lg4AoNQBsIwKGDHyA6mMQBrMXGB98vEGQDGsYYTVRuAdwwldMBqUqsrm5QAERwMogAkhQhNCFEYmIhADRBHAJq6kA

1 Like

Woah, new syntax! Any explanation on how the 'b. works?

It’s an old one. Don’t know how it’s called. Probably related to GADT

…some prompts

Apologies for any confusion. The dot notation in 'b. ({..} as 'b) => promise<string> is a way of defining a locally abstract type in ReScript (and also in OCaml from which ReScript is derived).

In this case, 'b is the locally abstract type. The dot (.) basically means that the type 'b is abstract within the scope of this function type. It is not known outside of the function definition and can vary each time the function is used. The specific type that 'b will represent is determined by the caller of the function.

So, for the function find, its type is a function that takes any object (of some type we’re calling 'b) and returns a promise<string>. The specific type that 'b represents will be based on the object that is passed to find when it is called.

This makes find more flexible, because it can work with objects of any type, not just a specific, pre-defined type. This type abstraction makes your query<'a> type more flexible and reusable across different use-cases where the structure of the object passed to find might vary.

4 Likes

Now the documentation, does not even list the term “locally abstract type”, which by the way is not exactly the most inclusive term from the point of view of someone new to the language.

Since there’s no way to get around this – some cases more compelling than this specific example require it. Something should be documented somewhere, under advanced features or something.
How to explain this, and how to call it, is all open I think.

2 Likes

Here’s a first draft of documentation:

It can be turned into a PR for the documentation website.

3 Likes

Should probably be put in a “advanced” section. Also needs some mention on the syntax cheatsheet.

Done, including syntax cheatsheet!

4 Likes

Just tried the syntax for recursive functions like this:

let rec test: ('a. 'a) => unit = something => {
  ...
}

but it doesn’t work.
Is it somehow possible?

Seems like the parser has issues with the parentheses here. This one works:

let rec test: 'a. 'a => unit = something => {
  ...
}

EDIT: Made an issue in the compiler repo.

Oh you’re right. Thanks for creating the issue.

I tried it before with and without parentheses in a type definition and there it didn’t matter. Both doesn’t work.

type fn = ('a. 'a) => unit
type fn = 'a. 'a => unit

That is correct, because it will only work if you apply it directly to a let-binding. I probably should add this small caveat to the docs as well.

EDIT: Done!

1 Like