nojaf
May 8, 2025, 8:07pm
1
I’m writing bindings for a function that takes a string.
@send
external scene: (t, string, 'a => unit) => unit = "scene";
@send
external go: (t, string, ~data: 'a=?) => unit = "go";
Usage is as follows:
k->scene("one", () => {});
k->go("one");
These strings represent a limited set of values. One could model them with a variant:
type scenes = | One | Two;
Can I keep the type generic in my bindings while enforcing a relationship between the two functions?
@send
external scene: (t, 'scene, 'a => unit) => unit = "scene";
@send
external go: (t, 'scene, ~data: 'a=?) => unit = "go";
In this case, 'scene
can be a variant but should be the same for both functions. What can I do to achieve this? What would be idiomatic?
The best I can think of would be to use a module function, whose argument provides the underlying type. For example:
module MakeScene = (Specification: {type scenes}) => {
type t
external make: unit => t = "aFunction"
@send
external scene: (t, Specification.scenes, 'a => unit) => unit = "scene";
@send
external go: (t, Specification.scenes, ~data: 'a=?) => unit = "go";
}
module Spec1 = {
type scenes = | One | Two
}
module Scene1 = MakeScene(Spec1)
let scene1 = Scene1.make()
Scene1.scene(scene1, Spec1.One, val => ())
Scene1.go(scene1, Spec1.One)
module Spec2 = {
type scenes = | A | B
}
module Scene2 = MakeScene(Spec2)
let scene2 = Scene2.make()
Scene2.scene(scene2, Spec2.A, val => ())
Scene2.go(scene2, Spec2.A)
Overuse of module functions can over-complicate code, so do be judicious here.
2 Likes
nojaf
May 9, 2025, 6:33am
3
Thanks, was thinking along those lines as well.
One issue with that approach is that I can’t really restrict the Specification
to take in a variant that will produce a string. That is another requirement to ensure the runtime behaviour will match.
I suppose that can’t done.
You could use a type t = private string
, this way you’d make sure the parameter would be of the right type.
= private string
wow, never seen this syntax. Is it documented somewhere?
nojaf
May 9, 2025, 12:00pm
6
How would that work?
Doesn’t seem to capture this error: ReScript Playground
something like that:
module MakeScene = (
Specification: {
type scenes = private string
},
) => {
external scene: (Specification.scenes, 'a => unit) => unit = "scene"
external go: (Specification.scenes, ~data: 'a=?) => unit = "go"
}
module Spec1: {
type scenes = private string
let one: scenes
let two: scenes
} = {
type scenes = string
let one = "one"
let two = "two"
}
module Scene1 = MakeScene(Spec1)
Scene1.scene(Spec1.one, _val => ())
Scene1.go("other") // compiler error!
Playground link
1 Like