That article you linked is very good and explains why you can’t just give it that type (in OCaml at least, but most applies to rescript as well).
The main problem is that rescript (and ocaml) allow mutability.
Upon partial application, the f
you show will construct a pure function. And that could in theory be given the type 'a => int
.
let f: ('x, 'y) => 'x = (x, _y) => x
// Pretend then rescript will allow this.
let g: 'a => int = f(3)
let _ = g(1234) // will return 3
let _ = g("yo") // will return 3
Okay that looks fine. But let’s write a function that when partially applied will return a function with that same type signature as your original g
('a => int
). (Again pretend rescript would allow it to happen.)
let f: (unit, 'x, 'y) => 'x = () => {
let vals = []
(x, y) => {
Array.push(vals, y)
x
}
}
// Again, just pretend rescript will allow this and it's a polymorphic function.
let g': 'a => int = f'((), 3)
let _ = g'(1234) // returns 3, and puts 1234 into the array
let _ = g'("yo") // returns 3, and puts "yo" into the array...BOOM!
If rescript allowed that type to really be 'a => int
and not put a weak type in there, then as you see, I can easily violate the type of the array (ie that it is a thing filled with one type of value). And you can do lot’s of other zany stuff as well (that linked article has some more fun examples). But luckily it can’t happen like that, and we can live in type-safe land even with mutability because the typechecker gives it the weak type.
Let me just put a nice quote from that article here for reference:
The result of a function application is a runtime value called a closure. Since this value will be created only at runtime, the typechecker can’t inspect it, so it is left beyond its reach. The typechecker needs to be very pessimistic (if not paranoid!) and must assume that the closure may create arbitrary references and could use them to breach type soundness.
(Of course, OCaml and Rescript don’t share the same runtime…so somebody more familiar with the compiler/typechecker will have to say if that above doesn’t quite apply the same way in rescript…but the above type issues are still there.)
By the way, your original example doesn’t compile in OCaml either. I’m guessing you probably ran that in utop, or another ocaml toplevel, in which you can sort of get it…
# let f x y = x ;;
val f : 'a -> 'b -> 'a = <fun>
# let g = f 3;;
val g : '_weak1 -> int = <fun>
But if you’re compiling the source code, it will give an error.
$ cat yo.ml
let f x y = x
let g = f 3
$ ocamlc yo.ml
File "yo.ml", line 2, characters 4-5:
2 | let g = f 3
^
Error: The type of this expression, '_weak1 -> int,
contains type variables that cannot be generalized