Can someone give a concise description of when the relaxed value restriction kicks in? I\'ve had trouble finding a concise and clear description of the rules. There\'s Garr
Jeffrey provided the intuitive explanation of why the relaxation is correct. As to when it is useful, I think we can first reproduce the answer octref helpfully linked to:
You may safely ignore those subtleties until, someday, you hit a problem with an abstract type of yours that is not as polymorphic as you would like, and then you should remember than a covariance annotation in the signature may help.
We discussed this on reddit/ocaml a few months ago:
Consider the following code example:
module type S = sig type 'a collection val empty : unit -> 'a collection end module C : S = struct type 'a collection = | Nil | Cons of 'a * 'a collection let empty () = Nil end let test = C.empty ()The type you get for
testis'_a C.collection, instead of the'a C.collectionthat you would expect. It is not a polymorphic type ('_ais a monomorphic inference variable that is not yet fully determined), and you won't be happy with it in most cases.This is because
C.empty ()is not a value, so its type is not generalized (~ made polymorphic). To benefit from the relaxed value restriction, you have to mark the abstract type'a collectioncovariant:module type S = sig type +'a collection val empty : unit -> 'a collection endOf course this only happens because the module
Cis sealed with the signatureS:module C : S = .... If the moduleCwas not given an explicit signature, the type-system would infer the most general variance (here covariance) and one wouldn't notice that.Programming against an abstract interface is often useful (when defining a functor, or enforcing a phantom type discipline, or writing modular programs) so this sort of situation definitely happens and it is then useful to know about the relaxed value restriction.
That's an example of when you need to be aware of it to get more polymorphism, because you set up an abstraction boundary (a module signature with an abstract type) and it doesn't work automatically, you have explicitly to say that the abstract type is covariant.
In most cases it happens without your notice, when you manipulate polymorphic data structures. [] @ [] only has the polymorphic type 'a list thanks to the relaxation.
A concrete but more advanced example is Oleg's Ber-MetaOCaml, which uses a type ('cl, 'ty) code to represent quoted expressions which are built piecewise. 'ty represents the type of the result of the quoted code, and 'cl is a kind of phantom region variable that guarantees that, when it remains polymorphic, the scoping of variable in quoted code is correct. As this relies on polymorphism in situations where quoted expressions are built by composing other quoted expressions (so are generally not values), it basically would not work at all without the relaxed value restriction (it's a side remark in his excellent yet technical document on type inference).