I can understand that allowing mutable is the reason for value restriction and weakly polymorphism. Basically a mutable ref inside a function may change the type involved and affect the future use of the function. So real polymorphism may not be introduced in case of type mismatch.
For example,
# let remember =
let cache = ref None in
(fun x ->
match !cache with
| Some y -> y
| None -> cache := Some x; x)
;;
val remember : '_a -> '_a = <fun>
In remember, cache originally was 'a option, but once it gets called first time let () = remember 1, cache turns to be int option, thus the type becomes limited. Value restriction solves this potential problem.
What I still don't understand is the value restriction on partial application.
For example,
let identity x = x
val identity: 'a -> 'a = <fun>
let map_rep = List.map identity
val map_rep: '_a list -> '_a list = <fun>
in the functions above, I don't see any ref or mutable place, why still value restriction is applied?
Here is a good paper that describes OCaml's current handling of the value restriction:
Garrigue, Relaxing the Value Restriction
It has a good capsule summary of the problem and its history.
Here are some observations, for what they're worth. I'm not an expert, just an amateur observer:
The meaning of "value" in the term "value restriction" is highly technical, and isn't directly related to the values manipulated by a particular language. It's a syntactic term; i.e., you can recognize values by just looking at the symbols of the program, without knowing anything about types.
It's not hard at all to produce examples where the value restriction is too restrictive. I.e., where it would be safe to generalize a type when the value restriction forbids it. But attempts to do a better job (to allow more generalization) resulted in rules that were too difficult to remember and follow for mere mortals (such as myself).
The impediment to generalizing exactly when it would be safe to do so is not separate compilation (IMHO) but the halting problem. I.e., it's not possible in theory even if you see all the program text.
The value restriction is pretty simple: only let-bound expressions that are syntactically values are generalized. Applications, including partial applications, are not values and thus are not generalized.
Note that in general it is impossible to tell whether an application is partial, and thus whether the application could have an effect on the value of a reference cell. Of course in this particular case it is obvious that no such thing occurs, but the inference rules are designed to be sound in the event that it does.
A 'let' expression is not a (syntactic) value. While there is a precise definition of 'value', roughly the only values are identifiers, functions, constants, and constructors applied to values.
This paper and those it references explains the problem in detail.
Partial application doesn't preclude mutation. For example, here is a refactored version of your code that would also be incorrect without value restriction:
let aux cache x =
match !cache with
| Some y -> y
| None -> cache := Some x; x
let remember = aux (ref None)
Related
I was desperately looking for the last hour for a method in the OCaml Library which converts an 'a to a string:
'a -> string
Is there something in the library which I just haven't found? Or do I have to do it different (writing everything by my own)?
It is not possible to write a printing function show of type 'a -> string in OCaml.
Indeed, types are erased after compilation in OCaml. (They are in fact erased after the typechecking which is one of the early phase of the compilation pipeline).
Consequently, a function of type 'a -> _ can either:
ignore its argument:
let f _ = "<something>"
peek at the memory representation of a value
let f x = if Obj.is_block x then "<block>" else "<immediate>"
Even peeking at the memory representation of a value has limited utility since many different types will share the same memory representation.
If you want to print a type, you need to create a printer for this type. You can either do this by hand using the Fmt library (or the Format module in the standard library)
type tree = Leaf of int | Node of { left:tree; right: tree }
let pp ppf tree = match tree with
| Leaf d -> Fmt.fp ppf "Leaf %d" d
| Node n -> Fmt.fp ppf "Node { left:%a; right:%a}" pp n.left pp n.right
or by using a ppx (a small preprocessing extension for OCaml) like https://github.com/ocaml-ppx/ppx_deriving.
type tree = Leaf of int | Node of { left:tree; right: tree } [##deriving show]
If you just want a quick hacky solution, you can use dump from theBatteries library. It doesn't work for all cases, but it does work for primitives, lists, etc. It accesses the underlying raw memory representation, hence is able to overcome (to some extent) the difficulties mentioned in the other answers.
You can use it like this (after installing it via opam install batteries):
# #require "batteries";;
# Batteries.dump 1;;
- : string = "1"
# Batteries.dump 1.2;;
- : string = "1.2"
# Batteries.dump [1;2;3];;
- : string = "[1; 2; 3]"
If you want a more "proper" solution, use ppx_deriving as recommended by #octachron. It is much more reliable/maintainable/customizable.
What you are looking for is a meaningful function of type 'a. 'a -> string, with parametric polymorphism (i.e. a single function that can operate the same for all possible types 'a, even those that didn’t exist when the function was created). This is not possible in OCaml. Here are explications depending on your programming background.
Coming from Haskell
If you were expecting such a function because you are familiar with the Haskell function show, then notice that its type is actually show :: Show a => a -> String. It uses an instance of the typeclass Show a, which is implicitly inserted by the compiler at call sites. This is not parametric polymorphism, this is ad-hoc polymorphism (show is overloaded, if you want). There is no such feature in OCaml (yet? there are projects for the future of the language, look for “modular implicits” or “modular explicits”).
Coming from OOP
If you were expecting such a function because you are familiar with OO languages in which every value is an object with a method toString, then this is not the case of OCaml. OCaml does not use the object model pervasively, and run-time representation of OCaml values retains no (or very few) notion of type. I refer you to #octachron’s answer.
Again, toString in OOP is not parametric polymorphism but overloading: there is not a single method toString which is defined for all possible types. Instead there are multiple — possibly very different — implementations of a method of the same name. In some OO languages, programmers try to follow the discipline of implementing a method by that name for every class they define, but it is only a coding practice. One could very well create objects that do not have such a method.
[ Actually, the notions involved in both worlds are pretty similar: Haskell requires an instance of a typeclass Show a providing a function show; OOP requires an object of a class Stringifiable (for instance) providing a method toString. Or, of course, an instance/object of a descendent typeclass/class. ]
Another possibility is to use https://github.com/ocaml-ppx/ppx_deriving with will create the function of Path.To.My.Super.Type.t -> string you can then use with your value. However you still need to track the path of the type by hand but it is better than nothing.
Another project provide feature similar to Batterie https://github.com/reasonml/reason-native/blob/master/src/console/README.md (I haven't tested Batterie so can't give opinion) They have the same limitation: they introspect the runtime encoding so can't get something really useable. I think it was done with windows/browser in mind so if cross plat is required I will test this one before (unless batterie is already pulled). and even if the code source is in reason you can use with same API in OCaml.
This chapter in Real World OCaml describes the runtime memory layout of different data types. However, there is no discussion on lazy values.
How is lazy_t implemented, i.e., what does its runtime representation and what the key operations that are compiler built-ins? Links to the source code would be appreciated. I looked at the CamlinternalLazy module, but the actual representation seems hard to decipher just based on calls to functions in the Obj module there.
Could someone provide a summary of the changes made to the representation/operations to make it thread safe for the OCaml multicore project? This commit seems to be the one with the implementation, but it seems a bit opaque to me as an outsider.
Note: This question is about the OCaml compiler/runtime. I understand that there is no standard specification for how lazy values should be implemented by an OCaml compiler/runtime.
In simple terms, a lazy value that needs computation is represented as a thunk which is rewritten by a reference to the computed value (if such exists†) once the value is forced. A lazy value that doesn't need a computation (and is not a float) is represented as it is.
First, let's focus on values that do not require computation. Those are constants, functions (not their applications, nor partial applications), or identifiers. They are represented without any extra boxing and have the same representation as their eager counterparts, e.g.,
# Obj.repr (lazy 42) == Obj.repr 42;;
- : bool = true
# Obj.tag (Obj.repr sin) = (Obj.tag (Obj.repr (lazy sin)));;
- : bool = true
# Obj.closure_tag = (Obj.tag (Obj.repr (lazy sin)));;
- : bool = true
The same is true for types which usually have a boxed representation, e.g., strings,
let s = "hello" in
Obj.repr s == Obj.repr (lazy s);;
- : bool = true
The only exception is the float type (because of another optimization which enables unboxed storage of arrays or records of floats, which would be broken otherwise). Floats are stored in the forwarded notation, as a boxed value with a header indicating the Forward_tag and the only field being the stored value.
Values that are classified as computations are stored as thunks. If we will speak OCaml (note, that is not the actual implementation, but the concept is the same)
type 'a value = Deferred of (unit -> 'a) | Ready of 'a
type 'a lazy_t = {
mutable lazy : 'a value;
}
and the lazy operator captures the enclosed expression, i.e., on the syntactic level of the language, it translates something like this:
lazy x => {lazy = Deferred (fun () -> x)
Here are some interactions with OCaml which showcase the representation:
let x = lazy (2+2) in
Obj.lazy_tag = Obj.tag (Obj.repr x);;
- : bool = true
let x = lazy (2+2) in
let _ = Lazy.force x in
Obj.forward_tag = Obj.tag (Obj.repr x);;
- : bool = true
As we can see a computation is stored as a thunk (and uses 4 words)
let x = lazy (2+2) in
Obj.reachable_words (Obj.repr x);;
- : int = 4
while after we force the computation it will be stored as a forwarded (boxed) int,
let x = lazy (2+2) in
let _ = Lazy.force x in
Obj.reachable_words (Obj.repr x);;
- : int = 2
†) There is also a special case for exceptions, which are computations that diverge and therefore do not have values, thus the couldn't be translated to the forwarded form. As a result, exceptions remain lazy values even after being forced, e.g.,
let x = lazy (raise Not_found) in
Obj.lazy_tag = Obj.tag (Obj.repr x);;
- : bool = true
let x = lazy (raise Not_found) in
try Lazy.force x with Not_found ->
Obj.lazy_tag = Obj.tag (Obj.repr x)
Implementation wise, a computation which raises an exception is substituted by a function that raises this exception. So there is still some memoization happening, in other words, if you had lazy (x (); y (); z ()) and y () is raising an exception E, then the lazy value payload will be substituted by a function fun () -> raise E, i.e., it will never repeat x (), nor it will ever reach z ().
Lazy values in Multicore
Laziness is a restricted form of mutability and, like any other mutability, it complicates things when parallel computations come into play.
In OCaml implementation, lazy values are not only changing their value throughout time but also type and representation. The representation of a value in OCaml is dictated by the header. For performance reasons, the OCaml Multicore Team decided to forbid any changed to the header, thus values can no longer change their representations (otherwise, if they would allow changing the header, each access to the header field would require an expensive synchronization).
A solution to this problem introduces a new level of indirection, where the state of the lazy value is stored in its payload (which actually makes the new lazy representation even closer to our conceptual view).
Before we delve into the implementation there is also one more thing to be explained about the lazy values in OCaml. When a lazy value is forced, it is not immediately updated to the result of the computation, since the computation itself could be recursive and reference the lazy value. That's why on the first step before the computation attached to the lazy value is called, the payload of a lazy function is substituted with a function that raises the Lazy.Undefined exception, so that not well-formed recursive expressions still terminate nicely.
This trick was hijacked and reused by the Multicore Team to make lazy values safe in the presence of multiple threads trying to force it at the same time. When a lazy value is being forced they substitute its payload with a function called bomb which checks whether the lazy value is referenced again (either because the computation recurses, or because it is shared with another thread) during the evaluation, and if the reference is from the same domain then it triggers the Undefined exception, indicating that this is not a well-formed lazy value, or if the domain is different, then it raises theRacyLazy exception, that indicates that there is an unserialized access to the same lazy value from different domains.
The crucial moment here is to understand that since lazy is a mutable value, it is still the responsibility of a user to properly serialize accesses to it. How to do this properly and efficiently is still in the Future Work section.
References to implementation
This is
how lazy values are compiled
how lazy values are classified
What is the best intuition for why the first definition would be refused, while the second one would be accepted ?
let rec a = b (* This kind of expression is not allowed as right-hand side of `let rec' *)
and b x = a x
let rec a x = b x (* oki doki *)
and b x = a x
Is it linked to the 2 reduction approaches : one rule for every function substitution (and a Rec delimiter) VS one rule per function definition (and lambda lifting) ?
Verifying that recursive definitions are valid is a very hard thing to do.
Basically, you want to avoid patterns in this kind of form:
let rec x = x
In the case where every left-hand side of the definitions are function declarations, you know it is going to be fine. At worst, you are creating an infinite loop, but at least you are creating a value. But the x = x case does not produce anything and has no semantic altogether.
Now, in your specific case, you are indeed creating functions (that loop indefinitely), but checking that you are is actually harder. To avoid writing a code that would attempt exhaustive checking, the OCaml developers decided to go with a much easier algorithm.
You can have an outlook of the rules here. Here is an excerpt (emphasis mine):
It will be accepted if each one of expr1 … exprn is statically constructive with respect to name1 … namen, is not immediately linked to any of name1 … namen, and is not an array constructor whose arguments have abstract type.
As you can see, direct recursive variable binding is not permitted.
This is not a final rule though, as there are improvements to that part of the compiler pending release. I haven't tested if your example passes with it, but some day your code might be accepted.
Recently I was given the code
List.fold_left (fun acc x -> raise x ; acc) 3
I'm completely fine with this partial application having a functional
value of type exn list -> int, and the fact it yields a warning
isn't surprising. I am, however, not certain what half of the warning
means:
Warning 21: this statement never returns (or has an unsound type.)
I can't actually find any reference to this warning where it isn't the
result of a non-returning statement. Even the man page for ocamlc only
mentions non-returning statements for this warning, and warnings.ml
refers to it merely as Nonreturning_statement.
I am familiar with the concept of soundness as it relates to type
systems, but the idea of a type itself being inherently unsound seems
odd to me.
So my questions are:
What exactly is an unsound type?
What's a situation in which an unsound type would arise when OCaml
would only issue a warning rather than failing hard outright?
Someone has posted this question, and while I was writing an answer, it was deleted. I believe the question is very interesting and worth for reposting. Please consider you may have someone who is willing to help you :-(
How Warning 21 is reported
First, let's think of functions which returns unrelated 'a: I do not mean function like let id x = x here since it has type 'a -> 'a and the return type 'a relates with the input. I mean functions like raise : exn -> 'a and exit : int -> 'a.
These functions return unrelated 'a are considered never returning. Since the type 'a (more precisely forall 'a. 'a) has no citizen. Only thing the functions can do are terminating the program (exit or raising an exception) or falling into an infinite loop: let rec loop () = loop ().
Warning 21 is mentioned when the type of a statement is 'a. (Actually there is another condition but I just skip for simplicity.) For example,
# loop (); print_string "end of the infinite loop";;
Warning 21: this statement never returns (or has an unsound type.)
This is the main purpose of warning 21. Then what is the latter half?
"Unsound type"
Warning 21 can be reported even if the statement returns something actually. In this case, as the warning message suggests the statement has a unsound type.
Why unsound? Since the expression does return a value of type forall 'a. 'a, which has no citizen. It breaks the basis of the type theory OCaml depends on.
In OCaml, there are several ways to write an expression with such an unsound type:
Use of Obj.magic. It screws type system therefore you can write an expression of type 'a which returns:
(Obj.magic 1); print_string "2"
Use of external. Same as Obj.magic you can give arbitrary type to any external values and functions:
external crazy : unit -> 'a = "%identity"
let f () = crazy () (* val f : unit -> 'a *)
let _ = f (); print_string "3"
For OCaml type system, it is impossible to distinguish non-returning expressions and expressions with unsound types. This is why it cannot rule out unsound things as errors. Tracking the definitions to tell a statement has an unsound type or not is generally impossible either and costs a lot even when possible.
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 Garrigue's paper:
http://caml.inria.fr/pub/papers/garrigue-value_restriction-fiwflp04.pdf
but it's a little dense. Anyone know of a pithier source?
An Addendum
Some good explanations were added below, but I was unable to find an explanation there for the following behavior:
# let _x = 3 in (fun () -> ref None);;
- : unit -> 'a option ref = <fun>
# let _x = ref 3 in (fun () -> ref None);;
- : unit -> '_a option ref = <fun>
Can anyone clarify the above? Why does the stray definition of a ref within the RHS of the enclosing let affect the heuristic.
I am not a type theorist, but here is my interpretation of Garrigue's explanation. You have a value V. Start with the type that would be assigned to V (in OCaml) under the usual value restriction. There will be some number (maybe 0) monomorphic type variables in the type. For each such variable that appears only in covariant position in the type (on the right sides of function arrows), you can replace it with a fully polymorphic type variable.
The argument goes as follows. Since your monomorphic variable is a variable, you can imagine replacing it with any single type. So you choose an uninhabited type U. Now since it is in covariant position only, U can in turn be replaced by any supertype. But every type is a supertype of an uninhabited type, hence it's safe to replace with a fully polymorphic variable.
So, the relaxed value restriction kicks in when you have (what would be) monomorphic variables that appear only in covariant positions.
(I hope I have this right. Certainly #gasche would do better, as octref suggests.)
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 test is '_a C.collection, instead of the 'a C.collection that you would expect. It is not a polymorphic type ('_a is 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 collection covariant:
module type S = sig
type +'a collection
val empty : unit -> 'a collection
end
Of course this only happens because the module C is sealed with the signature S : module C : S = .... If the module C was 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).
The question why the two examples given in the addendum are typed differently has puzzled me for a couple of days. Here is what I found by digging into the OCaml compiler's code (disclaimer: I'm neither an expert on OCaml nor on the ML type system).
Recap
# let _x = 3 in (fun () -> ref None);; (* (1) *)
- : unit -> 'a option ref = <fun>
is given a polymorphic type (think ∀ α. unit → α option ref) while
# let _x = ref 3 in (fun () -> ref None);; (* (2) *)
- : unit -> '_a option ref = <fun>
is given a monomorphic type (think unit → α option ref, that is, the type variable α is not universally quantified).
Intuition
For the purposes of type checking, the OCaml compiler sees no difference between example (2) and
# let r = ref None in (fun () -> r);; (* (3) *)
- : unit -> '_a option ref = <fun>
since it doesn't look into the body of the let to see if the bound variable is actually used (as one might expect). But (3) clearly must be given a monomorphic type, otherwise a polymorphically typed reference cell could escape, potentially leading to unsound behaviour like memory corruption.
Expansiveness
To understand why (1) and (2) are typed the way they are, let's have a look at how the OCaml compiler actually checks whether a let expression is a value (i.e. "nonexpansive") or not (see is_nonexpansive):
let rec is_nonexpansive exp =
match exp.exp_desc with
(* ... *)
| Texp_let(rec_flag, pat_exp_list, body) ->
List.for_all (fun vb -> is_nonexpansive vb.vb_expr) pat_exp_list &&
is_nonexpansive body
| (* ... *)
So a let-expression is a value if both its body and all the bound variables are values.
In both examples given in the addendum, the body is fun () -> ref None, which is a function and hence a value. The difference between the two pieces of code is that 3 is a value while ref 3 is not. Therefore OCaml considers the first let a value but not the second.
Typing
Again looking at the code of the OCaml compiler, we can see that whether an expression is considered expansive determines how the type of the let-expressions is generalised (see type_expression):
(* Typing of toplevel expressions *)
let type_expression env sexp =
(* ... *)
let exp = type_exp env sexp in
(* ... *)
if is_nonexpansive exp then generalize exp.exp_type
else generalize_expansive env exp.exp_type;
(* ... *)
Since let _x = 3 in (fun () -> ref None) is nonexpansive, it is typed using generalize which gives it a polymorphic type. let _x = ref 3 in (fun () -> ref None), on the other hand, is typed via generalize_expansive, giving it a monomorphic type.
That's as far as I got. If you want to dig even deeper, reading Oleg Kiselyov's Efficient and Insightful Generalization alongside generalize and generalize_expansive may be a good start.
Many thanks to Leo White from OCaml Labs Cambridge for encouraging me to start digging!
Although I'm not very familiar with this theory, I have asked a question about it.
gasche provided me with a concise explanation. The example is just a part of OCaml's map module. Check it out!
Maybe he will be able to provide you with a better answer. #gasche