I don’t feel like an expert on this topic, but I think I’m able to explain the issue:
I adapted your example in the playground to not use React.useState but a custom function having the same signature:
/* previous example given by Zeta611
let f = v => {
let x = ref(#b)
x := v
}
*/
module State = {
/*
naive implementation of usestate
mind that this implementation probably does not have the desired runtime behaviour,
but has the same type signature as React.useState
*/
let use: (unit => 'a) => ('a, ('a => 'a) => unit) = init => {
let x = ref(init())
let setX = f => x := f(x.contents)
(x.contents, setX)
}
}
/* infered as [> #b] => unit */
let f2 = v => {
let (_x, setX) = State.use(() => #b)
setX(_ => v)
}
let g = () => {
let (x, _setX) = State.use(() => #a) // x is infered as: [#a]
switch x {
| #a => f2(#a)
| #a => f2((x :> [#a | #b]))
/* following fails with:
| #a => f2(x)
This has type: [#a]
But this function argument is expecting: [> #b]
The first variant type does not allow tag(s) #b
*/
}
}
The Problem
-
setXinside of the functionf2infers as([> #b] => [> #b]) => unit:- init function sets
xjust to#b -
f2’s argumentvis inferred as any polymorphic variant constructor (due to x being a polymorphic variant) - hence the compiler will infer a lower bound for
x:[> #b]- “at least the constructor b”
- init function sets
-
xinside of the functionginfers as[#a](without any bounds):- init function sets
xjust to#a - there isn’t any possibility of
xbeing something else than#a - hence the compiler will infer just
#a- “x can only hold the constructor a”
- init function sets
-
the error message
This has type: [#a] But this function argument is expecting: [> #b] The first variant type does not allow tag(s) #b:- when you call
f2(x)in the functiong, you are saying “passx” (typed as[ #a ]) “to a functionf2” (typed as[> #b] => unit) - a value of type
[ #a ]can never have the shape of#b, while[> #b ]requires the expression to be “at least” possible to take on the shape of#b. which is not possible forxing- hence the error message
- when you call
Possible Solutions
I. One possible solution to this problem - although it might not be what you’re looking for exactly - is to call f2 with the same constructor you already pattern matched on. This way the newly created value will be inferred as [> #a | #b ] and that’s a type you are able to pass to f2.
II. Another possible solution would be to coerce the type of x inside of g like this: f2((x :> [#a | #b])) - “overruling” the inference.
III. Alternatively you should think about your use case, and if other thechniques would be a better fit.
It could be easier to recommend a fitting solution, knowing more about your actual use case:
- Who would consume the actual state you store in
f? - Why do you need polymorphic variants? What are you trying to achieve?