Type variable can't be generalized?

no idea why this happens. Playground

type foo<'a> = [< #a(int) | #b(string)] as 'a
let bar: (foo<'a>) => foo<'a> = x => x
let test = bar(#a(1))
//  ^^^^ source of the error

The type of test can be [ #a(int) ] or it can be [ #a(int) | #b(string) ] because foo has >.

oh. surely there could be some default though?

This may actually be a bug in the version of the OCaml compiler that ReScript is based on. When I try it in a more recent version there is no error:

utop # type 'a foo = [< `a of int | `b of string] as 'a;;
type 'a foo = 'a constraint 'a = [< `a of int | `b of string ]

utop # let bar : 'a foo -> 'a foo = fun x -> x;;
val bar : ([< `a of int | `b of string ] as 'a) -> 'a = <fun>

utop # let test = bar (`a 1);;
val test : [< `a of int | `b of string > `a ] = `a 1

In any case, as the error message says, once you actually use the value, the error goes away.

My recommendation, it’s simpler to not have constraint bounds in polymorphic variants, and not annotate the functions, because the compiler can usually infer better types for you anyway. E.g. the type of this:

let bar = x => x

…Is automatically inferred as 'a => 'a, which is the most generic (and correct) possible type.

When you do need annotations, e.g. in the interface, rule of thumb is that parameters get < and return types get >, e.g.

type foo = [#a(int) | #b(string)]

module Bar: {
  let bar: [< foo] => [> foo]
} = {
  let bar = x => switch x {
    | #a(_) as a => a
    | #b(_) as b => b
  }
}

let test = Bar.bar(#a(1))
1 Like

Hi, @somebody , polymorphic variants used to generate difficult error messages. I would suggest you only use it for simple types, e.g, nullary types like #a, #b, or closed types like:

type foo = [#a(int) | #b(string)]

Hi,
this is not a bug, it is the limitation of value restrictions that it is hard to provide a polymorphic value instead of a function.
It could be generalized a little bit and I will have a look how hard it will be and see if it is worth it.

Please don’t use OCaml as a reference implementation since it will cause confusions for new users.

1 Like

i was hoping it could infer to just one type (when possible) so that i could potentially avoid having to check that the value is the right variant (?)

but i guess that’s just not possible in rescript?

i was hoping it could infer to just one type (when possible) so that i could potentially avoid having to check that the value is the right variant (?)

It is. The problem is that it generate a polymorphic value which fails due to value restrictions. You can aid the compiler a little bit. Add a non-polymorphic type and it works:

let test : [#a(int)]= bar(#a(1))

polymoprhic value (not polymorphic functions) are only allowed with some restrictions.

yea, seems like this will work well enough - in my case i can probably just define a type alias for whichever variant i want (type myT = t<[#a(int)]> or something?)