Question about Belt.MapString

How come this works:

let x = Belt.Map.String.empty

but trying to return an empty map from a function generates an error:

let fn = () => Belt.Map.String.empty  
/* or even having this as a body of the function
{ 
  let emptyMap = Belt.Map.String.empty
  emptyMap
}
*/
let y = fn()

This expression's type contains type variables that can't be generalized:
  Belt.Map.String.t<'_weak1>
  
  This happens when the type system senses there's a mutation/side-effect,
  in combination with a polymorphic value.
  Using or annotating that value usually solves it.

This is due to what is called the the value restriction.

3 Likes

Captain Obvious here! If one wants a more dumb answer, you should annotate your fn so that the type of Map elements is known to the compiler:

let fn = (): Belt.Map.String.t<string /* or whatever type */> =>
  Belt.Map.String.empty 

let y = fn()
1 Like

I think he knows how to workaround this, but why is this happening.

let x = Belt.Map.String.empty // fine
let fn = () => Belt.Map.String.empty  
let y = fn() // error

interestingly enough if you shadow y in the snippet above with another type the error disappears too.

let fn = () => Belt.Map.String.empty  
let y = fn() // fine
let y = 2 

For anyone curious as to why this happens, it’s because the type error is only triggered for values that are exported. When you shadow a binding, then the original binding is no longer exported.

3 Likes

The value restriction article gave a good explanation of the “more dumb answer” :clown_face:

I figured the error would clear itself up once I wrote the rest of my code but I was just curious as about what seemed like strange behavior.

The article also explains what @johnj noted that the error is only triggered on exported values.

Looks like until the ReScript docs get more mature, learning OCaml will be very beneficial to understanding the mysteries of ReScript.

3 Likes