Weak polymorphism in OCaml

I am a bit confused about the weak polymorphism in OCaml.

See the following snippet where I define the remember function:

 let remember x = let cache = ref None in match !cache with | Some y -> y | None -> cache := Some x; x ;; 

The compiler can infer the polymorphic type 'a -> 'a , and cache used locally.

But when I change the above code to

 let remember = let cache = ref None in (fun x -> match !cache with | Some y -> y | None -> cache := Some x; x) ;; 

the compiler displays the weakly polymorphic type '_a -> '_a , it also seems that the cache is shared between remember calls.

Why does the compiler enclose a weakly polymorphic type here and why cache shared?

Moreover, if I change the code again

 let remember x = let cache = ref None in (fun z -> match !cache with | Some y -> z | None -> cache := Some x; x) ;; 

the compiler infers the polymorphic type 'a -> 'a -> 'a and cache becomes locally used. Why is this so?

+7
functional-programming ocaml ml value-restriction
source share
3 answers
 let remember = let cache = ref None in (fun x -> match !cache with | Some y -> y | None -> cache := Some x; x) ;; 

Here cache closed by the returned function. But at that moment when we declare a cache , we have no information about what type will be; it will be determined by any type of x and cache created by the remember definition.

But since this is a closure, we can do something like this:

 > remember 1 1 

Now it’s clear that cache : int option ref , since we are actually storing something in it. Since there is only one cache , remember can only store one type.

In the following, you close 2 things, x and cache . Since we create a new cache ref with each call to remember , the type can be completely polymorphic again. The reason the type is not polymorphic is because we know that we are going to store x in it, and we have type x at the time the cache .

+8
source share

This is like a value constraint. A full cost cap (as in SML) will completely reject your code. Weakly polymorphic types are described in Jacques Garrig's article “Reducing the Cost Limit,” which I admittedly just stumbled upon reading your question:

http://caml.inria.fr/pub/papers/garrigue-value_restriction-fiwflp04.pdf

The fact that cache is shared between calls should be obvious if you have the right mental model of what ML code means. You define two values: remember and cache . An attachment simply makes the cache area private to the block.

+6
source share
 let remember x = let cache = ref None in match !cache with | Some y -> y | None -> cache := Some x; x let remember x = let cache = ref None in (fun z -> match !cache with | Some y -> z | None -> cache := Some x; x) 

In the two versions above, remember is a "direct" function, every time you call it remember 1 , it initializes the cache to ref None , right? In fact, it doesn’t remember anything, cache not used between any remember calls.


In another version:

 let remember = let cache = ref None in (fun x -> match !cache with | Some y -> y | None -> cache := Some x; x) 

it is different. remember is still a function, but the real part defining its contents is (fun x -> match ...) . It includes cache , and the cache is initialized once and will only be once. Thus, the cache is split between a future remember call.

0
source share

All Articles