But I would like to not having to use a variant here… any idea ? Can I have a further explaination why it work with a “variant wrapper” but not without it ?
My understanding is that this is a limitation due to how ReScript distinguishes between “type aliases” and new types. A tuple type creates an alias: type rec t = (int, t) // ERROR: The type abbreviation t is cyclic. Here, the typechecker will try to expand t infinitely ((int, (int, (int, (int, ...) and fail. However, a variant is a new, unique type that can’t be expanded, so this is not an error: type rec t2 = A(int, t2).
(Someone else can probably explain it better than me. I mostly know this from reading this discussion. )
As for your specific code, one easier solution may be to use a record instead of a variant. (Records also create new types.) This compiles fine:
type rec func<'a, 'b, 'i> = 'i => funcReturn<'a, 'b, 'i>
and funcReturn<'a, 'b, 'i> = {b: 'b, f: func<'a, 'b, 'i>}
let rec f = i => {b: 1, f: f}
The difference here is mainly readability and personal preference. A variant with a single constructor is basically the same as using a tuple. You can construct and deconstruct it the same way: let Test(b, func) = func(1).