I have a function, that returns another function, say
let registerSW = (opts) => () => ()
the return type of the registerWS is a function to update service worker, let’s call it updateSW. In the opts I’m passing a React Element, that renders a button, which calls the updateSW on click.
So the function is kind of recursive but it refers to itself in a function argument, not in it’s body, and it does that via another callback - the onClick handler. Something perfectly valid in JS is invalid in Rescript. The rec keyword does not help for there is no function declaration, but function invocation. The function is external, co I cannot declare it as rec because again, bindings are not function declaration. And because I refer to the return type in function argument, it is not a recursion in a classic sense. I’m currently using %raw to escape the error, but I am curious if there is a way to write this in a valid Rescript?
Can you give the full example of what you are trying to do? Is just the registerSW function external or updateSW as well? You are not using any external keyword here.
If the signature of “registerSW” is like you wrote then the update function can be written like this:
type opts = {foo: React.element}
@val
external registerSW: opts => unit => unit = "registerSW"
let rec updateSW = () =>
registerSW({foo: <button onClick={_ => updateSW()} />})->ignore
The updateSW is a return of the registerSW, so it is intrinsically external as the registerSW is external. I’m loading it from the Vite PWA plugin, the binding is like
@module("virtual:pwa-register")
external registerSW: registerSWOptions => unit => unit = "registerSW"
and let’s say the registerSWOptions has the type
type registerSWOptions = {foo: React.element}
for simplicity.
I name the return type () => () a updateSW but it is only my assignment.
Your example would not work, as the updateSW calls registerSW in it’s body, but it should not do that - the updateSW is simply and external function returned by the registerSW.
That helps, thanks! I did not suspected an immutability to be a problem, since I never reassigned the let updateSW. I suspected the type system to be a cause, as it encountered a cycle while resolving the expression and it generally don’t handle that without rec keyword which only works for function declaration - that is why I used the term “recursion” in the title.
Using ref(ignore) is another interesting trick, I would assume that the type of the ref would be locked to ignore but it later allows storing a function there, so ref(ignore) is like a void assignment that just prepares the mutable variable. I tried assign the updateSW to the ref without the ref(ignore) preparation but it did not work, apparently for the same reason mentioned above - that the type system was unable tho resolve a type of the function which points to its own result in it’s argument.
Long story short, this surprised me and it is worth documenting! I will make a PR to documentation when I will find some spare time! Meantime, please help me understand what is going on when you assign ref(ignore), this part is still little bit blurry to me.
So ignore has the following signature: 'a => unit, it will only work for functions that have one arbitrary parameter and return unit. In your case, updateSW has the signature unit => unit which can be unified by the type checker without errors.