* '_a @ 2005-01-27 0:19 Mike Hamburg 2005-01-27 0:51 ` [Caml-list] '_a Jacques Garrigue 0 siblings, 1 reply; 19+ messages in thread From: Mike Hamburg @ 2005-01-27 0:19 UTC (permalink / raw) To: caml-list The appearance of '_a in places where it shouldn't appear causes some annoyance, and a good deal of confusion among beginners to O'Caml. In particular, List.map (fun x -> x) "ought to" have type 'a list -> 'a list, whereas it instead has type '_a list -> '_a list. Some types are treated specially for creation of '_a, such as refs and arrays. For instance, if you have the following two declarations: # let a = let f x () = [x] in f [];; val a : unit -> 'a list list = <fun> # let b = let f x () = [|x|] in f [];; val b : unit -> '_a list array = <fun> Although I haven't read the code for O'Caml, I deduce from this that there is deep magic in place to determine when to turn 'a into '_a, and in many cases, the heuristic is wrong (in the conservative direction: in this case, 'a should not be turned into '_a until b is applied). The cause of the problem is that programs may create a closure with a mutable variable of type 'a, which if we were to let 'a generalize could be replaced with a subtype 'b of 'a, and then used as another subtype 'c of 'a, which would be unsafe. As a fix, I propose the following: the type system should keep track of where a mutable or immutable reference to a polymorphic variable with type parameter 'a is created on the stack. Call these places [m'a] and [i'a]. For example, ref should have type 'a -> [m'a] 'a, and ref [] should have type [m'a] 'a (which is equivalent to the current '_a). I don't propose that the printing should show this complexity, just as it hides whatever heuristic O'Caml is using now, except in the case where there is a mutable reference at top level, in which case it should convert 'a to '_a. Of course, module interfaces shouldn't have to show when they keep references to things, so we probably can't do much about applying List.map to the identity without modifying List (for instance, what if List.map decided to memoize the function fed into it using a hash table?). Is this a reasonable idea? If so, can someone give me a pointer on how to go about implementing it (or proving that it is type-safe with appropriate unification rules?)? Mike ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] '_a 2005-01-27 0:19 '_a Mike Hamburg @ 2005-01-27 0:51 ` Jacques Garrigue 2005-01-27 9:34 ` skaller ` (2 more replies) 0 siblings, 3 replies; 19+ messages in thread From: Jacques Garrigue @ 2005-01-27 0:51 UTC (permalink / raw) To: hamburg; +Cc: caml-list From: Mike Hamburg <hamburg@fas.harvard.edu> Subject: [Caml-list] '_a Date: Wed, 26 Jan 2005 19:19:16 -0500 > The appearance of '_a in places where it shouldn't appear causes some > annoyance, and a good deal of confusion among beginners to O'Caml. In > particular, List.map (fun x -> x) "ought to" have type 'a list -> 'a > list, whereas it instead has type '_a list -> '_a list. > > Some types are treated specially for creation of '_a, such as refs and > arrays. For instance, if you have the following two declarations: > > # let a = let f x () = [x] in f [];; > val a : unit -> 'a list list = <fun> > # let b = let f x () = [|x|] in f [];; > val b : unit -> '_a list array = <fun> > > Although I haven't read the code for O'Caml, I deduce from this that > there is deep magic in place to determine when to turn 'a into '_a, and > in many cases, the heuristic is wrong (in the conservative direction: > in this case, 'a should not be turned into '_a until b is applied). There is no deep magic, no heuristics. There is just a type system which guarantees type soundness (i.e. "well typed programs cannot produce runtime type errors"). If you want the type system and all the historical details, read my paper "Relaxing the value restriction" at http://wwwfun.kurims.kyoto-u.ac.jp/~garrigue/papers/ In a nutshell, ocaml uses an improvement of the value restriction. With the value restriction, only definitions which are syntactical values (i.e. that do not call functions, etc when defined) are allowed to contain polymorphic type variables. This is improved by allowing covariant type variables to be generalized in all cases. Your first example is ok, because list is a covariant type, but your second fails, because array is not (you may assign to an array, and you would have to look at the code to see that each call to b creates a different array) Of course, one could think of many clever tricks by looking at what the code actually does. The above paper describes some of the "crazy" things Xavier Leroy and others tried in the past, which actually subsume your ideas, before they all decided this was too complicated. The main reason is that, if something is going to break at module boundaries, then it is not really useful. Good reading, Jacques Garrigue ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] '_a 2005-01-27 0:51 ` [Caml-list] '_a Jacques Garrigue @ 2005-01-27 9:34 ` skaller 2005-01-27 10:02 ` Alex Baretta 2005-01-27 14:13 ` '_a Vincenzo Ciancia 2005-01-29 0:33 ` [Caml-list] '_a Dave Berry 2005-02-03 7:41 ` Florian Hars 2 siblings, 2 replies; 19+ messages in thread From: skaller @ 2005-01-27 9:34 UTC (permalink / raw) To: Jacques Garrigue; +Cc: hamburg, caml-list On Thu, 2005-01-27 at 11:51, Jacques Garrigue wrote: > There is no deep magic, no heuristics. There is just a type system > which guarantees type soundness (i.e. "well typed programs cannot > produce runtime type errors"). Unfortunately, 'soundness' as described is somewhat weaker than one would like, since it depends on the expressivity of the type system how useful soundness actually is. Ocaml can still produce run time errors for situations that 'should' have been considered type errors, except the typing is not strong enough to allow it. In the extreme, a fully dynamic type system in which everything has type 'object' has a fully sound static type system behind it, and there cannot be any run time 'type' errors in the sense of the static type. For less extreme circumstances, C++ programmers would express surprise the typing is so weak that array length is not part of an array type, so that a bounds violation is technically not a type error in Ocaml whereas in a language where the length was part of type information the very same error would indicate unsound typing. There are in fact quite a few incorrectly typed functions as well, for example List.hd and List.tl can fail at run time, but this isn't considered a type error simply because the function typing is actually wrong. Thus, 'soundness' being ensured must be taken with a grain of salt. The fact that 'well typed programs can't produce runtime *type* errors' doesn't really tell you as much as you'd like -- other errors can still occur which aren't technically type errors, but in a less 'technical' interpretation certainly are. Exceptions are part of the evil here, since they permit blatant uncontrolled lying about type. The 'real' type of List.hd is hd: 'a list -> 'a option considering it as a total function. The type hd: 'a stream -> 'a is correct but only applies to streams, which lists are not. An exception free implementation of List.hd would always produce the correct typing: let hd x = function | [] -> None | h :: _ -> Some h In effect, Ocaml first pretends unsound typing is actually sound, and then enforces this at run time by throwing an exception where one would otherwise accuse the type system of unsoundness, but claims the error is not a type error. It isn't clear whether this trick is enough to say the type system is genuinely sound. One could argue dereferencing a null pointer in C is, or is not, a type error -- either way it is a nasty error which can't be easily tracked down except by 'binary chop' on your code with diagnostics, or a debugger -- and Ocaml is no different in character when you find your code terminated with an uncaught Not_found exception. [A really nasty one is that polymorphic compare is not polymorphic .. it can't handle abstract types eg BigInt or functions .. but you'll only find out at run time ..] In practice, Ocaml really does help detect errors early that weaker languages like C++ would not, so this isn't a criticism so much as a warning: it is still possible to have a significant number of 'technical' errors in an Ocaml program (quite apart from just getting your encoding of some algorithm entirely wrong) -- and it is easy to be lulled into a false sense of security by the very fact the basic typing is both expressive and sound. -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] '_a 2005-01-27 9:34 ` skaller @ 2005-01-27 10:02 ` Alex Baretta 2005-01-27 14:13 ` '_a Vincenzo Ciancia 1 sibling, 0 replies; 19+ messages in thread From: Alex Baretta @ 2005-01-27 10:02 UTC (permalink / raw) To: skaller, Ocaml skaller wrote: > On Thu, 2005-01-27 at 11:51, Jacques Garrigue wrote: > > >>There is no deep magic, no heuristics. There is just a type system >>which guarantees type soundness (i.e. "well typed programs cannot >>produce runtime type errors"). > > > Unfortunately, 'soundness' as described is somewhat weaker > than one would like, since it depends on the expressivity > of the type system how useful soundness actually is. > ... Sadly, this is very true. Camls are no genies capable of generating correct code when the programmer exerts friction against the computer's case. This is also the reason behind such DSLs as CDuce. Of course, it is very reasonable to state that PXP is sound, but in now way does it guarantee the absence of runtime errors of the kind "required attributed not found". Here at DE&IT we are doing a good deal of work on extending the type system to handle XML well formedness constraints and possibly, to some extent, validity constraints. Alex -- ********************************************************************* http://www.barettadeit.com/ Baretta DE&IT A division of Baretta SRL tel. +39 02 370 111 55 fax. +39 02 370 111 54 Our technology: The Application System/Xcaml (AS/Xcaml) <http://www.asxcaml.org/> The FreerP Project <http://www.freerp.org/> ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: '_a 2005-01-27 9:34 ` skaller 2005-01-27 10:02 ` Alex Baretta @ 2005-01-27 14:13 ` Vincenzo Ciancia 2005-01-27 19:39 ` [Caml-list] '_a Jacques Carette ` (2 more replies) 1 sibling, 3 replies; 19+ messages in thread From: Vincenzo Ciancia @ 2005-01-27 14:13 UTC (permalink / raw) To: caml-list skaller wrote: > An exception free implementation of List.hd would > always produce the correct typing: > > let hd x = function > | [] -> None > | h :: _ -> Some h > > In effect, Ocaml first pretends unsound typing is actually sound, > and then enforces this at run time by throwing an exception > where one would otherwise accuse the type system of unsoundness, > but claims the error is not a type error. What about the possibility to include possible exceptions into a function signature (a la java)? Does this have problems with type inference? Also, there is the ocamlexc tool: http://caml.inria.fr/ocamlexc/ocamlexc.htm what about it? V. -- Please note that I do not read the e-mail address used in the from field but I read vincenzo_ml at yahoo dot it Attenzione: non leggo l'indirizzo di posta usato nel campo from, ma leggo vincenzo_ml at yahoo dot it ^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [Caml-list] Re: '_a 2005-01-27 14:13 ` '_a Vincenzo Ciancia @ 2005-01-27 19:39 ` Jacques Carette 2005-01-28 0:57 ` skaller 2005-01-28 12:54 ` Richard Jones 2 siblings, 0 replies; 19+ messages in thread From: Jacques Carette @ 2005-01-27 19:39 UTC (permalink / raw) To: 'Vincenzo Ciancia', caml-list > What about the possibility to include possible exceptions into a > function signature (a la java)? Does this have problems with type > inference? I would love an (optional?) way to get the type signature of my functions to reflect their non-totalness (exceptions + anything else), as well as reflecting their 'imperative' content [ie which state variables are used]. In fact, any such 'monadic' information that can be automatically inferred would be really useful to have (optionally). I guess these are known as 'types and effects' systems. Jacques ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] Re: '_a 2005-01-27 14:13 ` '_a Vincenzo Ciancia 2005-01-27 19:39 ` [Caml-list] '_a Jacques Carette @ 2005-01-28 0:57 ` skaller 2005-01-28 13:25 ` '_a Stefan Monnier 2005-01-28 13:42 ` Christophe TROESTLER 2005-01-28 12:54 ` Richard Jones 2 siblings, 2 replies; 19+ messages in thread From: skaller @ 2005-01-28 0:57 UTC (permalink / raw) To: Vincenzo Ciancia; +Cc: caml-list On Fri, 2005-01-28 at 01:13, Vincenzo Ciancia wrote: > skaller wrote: > > > An exception free implementation of List.hd would > > always produce the correct typing: > > > > let hd x = function > > | [] -> None > > | h :: _ -> Some h > > > > In effect, Ocaml first pretends unsound typing is actually sound, > > and then enforces this at run time by throwing an exception > > where one would otherwise accuse the type system of unsoundness, > > but claims the error is not a type error. > > What about the possibility to include possible exceptions into a function > signature (a la java)? Does this have problems with type inference? Well I doubt the Java technique is any more meaningful than the C++ one. There are three meanings of exceptions: (a) the function domain was mis-specified, there is actually an unstated constraint on it: the correct signature for division is: divide: integer - { 0 } -> integer (b) the codomain is mis-specified, we actually have List.hd: 'a list -> Some 'a but enforce codomain 'a instead by throwing in the None case (c) The function depends on some complex details not considered part of the program, which can fail, for example status of a file. Documenting the exception instead of the constraint on the signature doesn't seem very nice. > Also, > there is the ocamlexc tool: > > http://caml.inria.fr/ocamlexc/ocamlexc.htm > > what about it? > If I recall, this is a superb tool backed by some very smart theory -- but in practice the output is extremely hard to interpret. Exceptions are often used where the constraint is expected to be satisfied -- I myself say: .. Hashtbl.find table key ... without any try/with wrapper when I 'know' the key is in the table, and I may write .. List.hd l ... instead of match l with | [] -> assert false | h :: _ -> h However these uses of cast 'magic' are distinct from alternate returns, where one sometimes has to cheat the type system in the opposite direction, adding a dummy value to code that will never be used just to get the type right: let x = let rec f l -> | [] -> raise Not_found | h :: t -> if h == v then (raise Found; 0) else f t in try f l with Found -> 1 | Not_found -> 2 in print_endline (string_of_int x) ;; where the compiler doesn't know f l cannot return, so it needs a useless '0' after the Found case to get the typing correct. (Or you could add it after the 'try f l', either way it's equivalent to a safe use of Obj.magic, safe since the cast will never be applied). I guess some of these cases would be better handled with a monadic technique, static exceptions, or just doing the nasty testing: in many ways, exceptions are *worse* than gotos, since at least the latter in ISO C require the target to be visible. BTW: Felix actually uses goto to replace exceptions, and makes you pass handler into the code if necessary: proc error() { goto endoff; } fun g(e:unit->void) { ... error(); ... } ... endoff:> // error found .. Thus if you can't see the handler target in the code, you can pass a closure which can. This guarrantees you cannot fail to catch an exception. It is still flawed though. -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: '_a 2005-01-28 0:57 ` skaller @ 2005-01-28 13:25 ` Stefan Monnier 2005-01-28 14:46 ` [Caml-list] '_a skaller 2005-01-28 14:46 ` Keith Wansbrough 2005-01-28 13:42 ` Christophe TROESTLER 1 sibling, 2 replies; 19+ messages in thread From: Stefan Monnier @ 2005-01-28 13:25 UTC (permalink / raw) To: caml-list > (b) the codomain is mis-specified, we actually have > List.hd: 'a list -> Some 'a Funny, I always assumed that the domain of List.hd was "'a list - []". Stefan ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] Re: '_a 2005-01-28 13:25 ` '_a Stefan Monnier @ 2005-01-28 14:46 ` skaller 2005-01-28 14:46 ` Keith Wansbrough 1 sibling, 0 replies; 19+ messages in thread From: skaller @ 2005-01-28 14:46 UTC (permalink / raw) To: Stefan Monnier; +Cc: caml-list On Sat, 2005-01-29 at 00:25, Stefan Monnier wrote: > > (b) the codomain is mis-specified, we actually have > > List.hd: 'a list -> Some 'a > > Funny, I always assumed that the domain of List.hd was "'a list - []". That would work too! -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] Re: '_a 2005-01-28 13:25 ` '_a Stefan Monnier 2005-01-28 14:46 ` [Caml-list] '_a skaller @ 2005-01-28 14:46 ` Keith Wansbrough 2005-01-28 15:48 ` skaller 1 sibling, 1 reply; 19+ messages in thread From: Keith Wansbrough @ 2005-01-28 14:46 UTC (permalink / raw) To: Stefan Monnier; +Cc: caml-list Stefan Monnier <monnier@iro.umontreal.ca> writes: > > (b) the codomain is mis-specified, we actually have > > List.hd: 'a list -> Some 'a > > Funny, I always assumed that the domain of List.hd was "'a list - []". Yes, indeed. Were List.hd of type 'a list -> 'a option, we'd be stuck if we ever wanted actually to _use_ the value: any function that attempted to extract it (say f : 'a option -> 'a) would have to have a similar type (f : 'a option -> 'a option). The same problem would ensue if we had explicit exception typing: List.hd : 'a list -> 'a with_possible_exception extract : 'a with_possible_exception -> 'a option really_extract : 'a option -> 'a with_possible_exception and so on...! The only way out of this mess is to add a monad. OCaml already has one: return is implicit, bind is called ";", and the monad operations include "raise" and "try ... with ...". HTH. --KW 8-) ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] Re: '_a 2005-01-28 14:46 ` Keith Wansbrough @ 2005-01-28 15:48 ` skaller 2005-01-29 1:37 ` Michael Walter 0 siblings, 1 reply; 19+ messages in thread From: skaller @ 2005-01-28 15:48 UTC (permalink / raw) To: Keith Wansbrough; +Cc: Stefan Monnier, caml-list On Sat, 2005-01-29 at 01:46, Keith Wansbrough wrote: > Stefan Monnier <monnier@iro.umontreal.ca> writes: > > > > (b) the codomain is mis-specified, we actually have > > > List.hd: 'a list -> Some 'a > > > > Funny, I always assumed that the domain of List.hd was "'a list - []". > > Yes, indeed. Were List.hd of type 'a list -> 'a option, we'd be stuck > if we ever wanted actually to _use_ the value: any function that > attempted to extract it (say f : 'a option -> 'a) would have to have a > similar type (f : 'a option -> 'a option). Of course! That's what destructors are for. Ultimately, there's no choice, no matter how you factor it you cannot have a List.hd function returning a non sum: destructors are the *only* way to analyse sums. The data summation *must* be dualised by a destructor to convert that data into control branches. Note that List.hd actually does that .. but one of the control paths is implicit (the one carried by the exception). > The only way out of this mess is to add a monad. That isn't the only way. I have a fairly large project that only throw an exception in *one* essential place (to get out of a really deep complex recursion). With one exception -- i do use 'monadic' failwith () like functions to terminate when I detect an error (and I do use that a lot), these are all caught at the top level (and they're not allowed to be caught anywhere else). Otherwise .. I find the 'pain' of using destructors everywhere preferable to throwing exceptions -- the additional complexity of the control paths is a small price to pay for their localisation. Meaning -- I sometimes have trouble holding enough of a picture in my head to understand my code, but with exceptions I'm completely lost because half the code isn't even visible :) > OCaml already has > one: return is implicit, bind is called ";", and the monad operations > include "raise" and "try ... with ...". Indeed, but that isn't necessarily a good monad for all purposes (otherwise Haskell would be Ocaml and wouldn't have any typeclasses .. LOL :) In particular, raise is very nasty -- I can't say this very well, but 'the monad is too global'. It's way too powerful -- and thus too hard to reason about. I think this is because it crosses abstraction boundaries. You can't predict what a function will throw from its interface, so you basically have lost control of your program. As I understand it, Haskell style monads provide better localisation (is that right?) The problem with using destructors left, right, and centre, is that you need heaps of extremely localised code to handle all the cases. However going from that to total globality is no solution. (In the first case the error handling is too tighly coupled to the error detection, and in the second too loosely coupled). Exception specs try to relieve this but they don't work. To me the solution appears to require some kind of 'static exceptions'. Felix uses a non-local goto. This is definitely better for some purpose than dynamic EH, since it ensures every 'throw' has a matching 'catch'. The goto can be wrapped in a closure and passed explicitly anywhere inside the closures abstraction, allowing both static and dynamic control path to be constructed: either way the path is either manifest (by locality) or explicit (by argument passing). However this solution is not modular. EG: you can provide a handler for division by zero, by there's no guarrantee the client you pass it to actually calls it for the correct error .. :) Monads provide better modularity? -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] Re: '_a 2005-01-28 15:48 ` skaller @ 2005-01-29 1:37 ` Michael Walter 0 siblings, 0 replies; 19+ messages in thread From: Michael Walter @ 2005-01-29 1:37 UTC (permalink / raw) To: skaller; +Cc: Keith Wansbrough, Stefan Monnier, caml-list On 29 Jan 2005 02:48:20 +1100, skaller <skaller@users.sourceforge.net> wrote: > [...] > > OCaml already has > > one: return is implicit, bind is called ";", and the monad operations > > include "raise" and "try ... with ...". > > Indeed, but that isn't necessarily a good monad > for all purposes (otherwise Haskell would be Ocaml > and wouldn't have any typeclasses .. LOL :) This is indeed pretty much the IO monad (AFAIK O'caml). return: (implicit) >>: ; fail: raise catch: try ... with ... (not part of the Monad type class) > In particular, raise is very nasty -- I can't say this > very well, but 'the monad is too global'. It's way too > powerful -- and thus too hard to reason about. Hum. > You can't predict what a function will throw from its > interface, so you basically have lost control of your program. > > As I understand it, Haskell style monads provide > better localisation (is that right?) As you define binding by yourself, you have all possibilities to propagate errors, such as implementation exceptions, etc. Simple examples are Maybe and Either. And IO, obviously ;-) Michael ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] Re: '_a 2005-01-28 0:57 ` skaller 2005-01-28 13:25 ` '_a Stefan Monnier @ 2005-01-28 13:42 ` Christophe TROESTLER 2005-01-28 14:50 ` skaller 1 sibling, 1 reply; 19+ messages in thread From: Christophe TROESTLER @ 2005-01-28 13:42 UTC (permalink / raw) To: O'Caml Mailing List On 28 Jan 2005, skaller <skaller@users.sourceforge.net> wrote: > > let x = > let rec f l -> | [] -> raise Not_found > | h :: t -> if h == v then (raise Found; 0) else f t > in try f l with Found -> 1 | Not_found -> 2 > in print_endline (string_of_int x) > > where the compiler doesn't know f l cannot return, so it needs a > useless '0' after the Found case to get the typing correct. Not quite, let find v l = let x = let rec f = function | [] -> raise Not_found | h :: t -> if h = v then raise Found else f t in try f l with Found -> 1 | Not_found -> 2 in print_endline (string_of_int x) has type "val find : 'a -> 'a list -> unit = <fun>". (BTW, note that the equality is "=" in Caml.) Maybe you mean something like let cl file = let fh = open_in file in let nl = ref 0 in try while true do let _ = input_line fh in incr nl done with End_of_file -> !nl where the [while] has type [unit] while [!nl] has type [int] and the two cannot be unified. But this is because there is no way for the compiler to know that a loop indeed never ends, so one has to tell: let cl file = let fh = open_in file in let nl = ref 0 in try while true do let _ = input_line fh in incr nl done; assert false with End_of_file -> !nl With that everything is fine. Regards, ChriS ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] Re: '_a 2005-01-28 13:42 ` Christophe TROESTLER @ 2005-01-28 14:50 ` skaller 0 siblings, 0 replies; 19+ messages in thread From: skaller @ 2005-01-28 14:50 UTC (permalink / raw) To: Christophe TROESTLER; +Cc: O'Caml Mailing List On Sat, 2005-01-29 at 00:42, Christophe TROESTLER wrote: > On 28 Jan 2005, skaller <skaller@users.sourceforge.net> wrote: > > > > let x = > > let rec f l -> | [] -> raise Not_found > > | h :: t -> if h == v then (raise Found; 0) else f t > > in try f l with Found -> 1 | Not_found -> 2 > > in print_endline (string_of_int x) > > > > where the compiler doesn't know f l cannot return, so it needs a > > useless '0' after the Found case to get the typing correct. > > Not quite, [] > Maybe you mean something like > > let cl file = > let fh = open_in file in > let nl = ref 0 in > try > while true do > let _ = input_line fh in > incr nl > done > with End_of_file -> !nl Thanks for a better example (well a right one has to better :) > where the [while] has type [unit] while [!nl] has type [int] and the > two cannot be unified. But this is because there is no way for the > compiler to know that a loop indeed never ends, so one has to tell: > > let cl file = > let fh = open_in file in > let nl = ref 0 in > try > while true do > let _ = input_line fh in > incr nl > done; > assert false > with End_of_file -> !nl > > With that everything is fine. Yes, that's a better solution too. -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] Re: '_a 2005-01-27 14:13 ` '_a Vincenzo Ciancia 2005-01-27 19:39 ` [Caml-list] '_a Jacques Carette 2005-01-28 0:57 ` skaller @ 2005-01-28 12:54 ` Richard Jones 2005-01-28 14:39 ` Alex Baretta 2 siblings, 1 reply; 19+ messages in thread From: Richard Jones @ 2005-01-28 12:54 UTC (permalink / raw) Cc: caml-list On Thu, Jan 27, 2005 at 03:13:40PM +0100, Vincenzo Ciancia wrote: > What about the possibility to include possible exceptions into a function > signature (a la java)? That's one of the most annoying features of Java. Let's not copy it. Rich. -- Richard Jones, CTO Merjis Ltd. Merjis - web marketing and technology - http://merjis.com Team Notepad - intranets and extranets for business - http://team-notepad.com ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] Re: '_a 2005-01-28 12:54 ` Richard Jones @ 2005-01-28 14:39 ` Alex Baretta 0 siblings, 0 replies; 19+ messages in thread From: Alex Baretta @ 2005-01-28 14:39 UTC (permalink / raw) To: caml-list Richard Jones wrote: > On Thu, Jan 27, 2005 at 03:13:40PM +0100, Vincenzo Ciancia wrote: > >>What about the possibility to include possible exceptions into a function >>signature (a la java)? > > > That's one of the most annoying features of Java. Let's not copy it. > > Rich. > The misfeature is the complexity explosion which tracing exceptions in function signatures causes. I don't want to have to deal with that. Alex -- ********************************************************************* http://www.barettadeit.com/ Baretta DE&IT A division of Baretta SRL tel. +39 02 370 111 55 fax. +39 02 370 111 54 Our technology: The Application System/Xcaml (AS/Xcaml) <http://www.asxcaml.org/> The FreerP Project <http://www.freerp.org/> ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] '_a 2005-01-27 0:51 ` [Caml-list] '_a Jacques Garrigue 2005-01-27 9:34 ` skaller @ 2005-01-29 0:33 ` Dave Berry 2005-02-02 9:17 ` Jacques Garrigue 2005-02-03 7:41 ` Florian Hars 2 siblings, 1 reply; 19+ messages in thread From: Dave Berry @ 2005-01-29 0:33 UTC (permalink / raw) To: Jacques Garrigue; +Cc: caml-list Jacques, That's a very interesting paper. I note that you ask "Our examples with constructor functions and abstract datatypes were expressible in systems predating the value restriction, and are refused by the strict value restriction. This makes one wonder why this didn't cause more problems during the transition." At the time that SML'97 was being defined, I was working on Harlequin's SML compiler and programming environment (which has long since vanished into legal limbo). The Harlequin team initially opposed the adoption of the value polymorphism rule for precisely the reasons you give. Eventually we gave in because the other advantages outweighed the disadvantages. (All these discussions took place in private e-mail). Basically, the touted advantages of adopting the value polymorphism rule were: 1. The formal semantics became simpler. 2. Eliminating the different types of type variable made the language easier to explain, without affecting expressibility in practice. 3. It enabled typeful compilation (as you note in your paper). I have never believed either half of point 2. The value polymorphism rule means that you have to explain about references and their effect on type inference even for some simple programs that don't use references at all, such as the example that Mike gave. This "raises the bar" for explaining type inference to beginners. Furthermore, expressiveness is affected for the examples that you give in your paper. We had to change several parts of our code when we adopted the value polymorphism rule. However, we felt (and I still think) that points 1 and 3, particularly point 3, outweigh these disadvantages. From a practical point of view, I like your approach. However, it does negate point 1 above, because it makes the formal semantics more complex again - although this is less of a problem in O'Caml, because its semantics are already so complicated compared to SML97. (I do not intend that remark as an insult). It will be interesting to see how your approach affects point 2 - novices may still encounter the value restriction in a simple program, and the new system will be more complicated to explain. I sometimes wonder whether it would help to have a "pure" annotation for function types that would require the implementation to not use references nor to raise exceptions. I don't mean a detailed closure analysis, just a simple flag. Most practical functions would not be pure, but many simple ones could be, and these could be used to introduce people to the basics of functional programming without raising the complication of the value polymorphism rule. (You wouldn't get a theory paper out of it though). Unfortunately I'm no longer working on programming languages and so I don't have the capability to explore this. It may be a half-baked idea that doesn't work in practice. Best wishes, Dave. At 00:51 27/01/2005, Jacques Garrigue wrote: >From: Mike Hamburg <hamburg@fas.harvard.edu> >Subject: [Caml-list] '_a >Date: Wed, 26 Jan 2005 19:19:16 -0500 > > > The appearance of '_a in places where it shouldn't appear causes some > > annoyance, and a good deal of confusion among beginners to O'Caml. In > > particular, List.map (fun x -> x) "ought to" have type 'a list -> 'a > > list, whereas it instead has type '_a list -> '_a list. > > > > Some types are treated specially for creation of '_a, such as refs and > > arrays. For instance, if you have the following two declarations: > > > > # let a = let f x () = [x] in f [];; > > val a : unit -> 'a list list = <fun> > > # let b = let f x () = [|x|] in f [];; > > val b : unit -> '_a list array = <fun> > > > > Although I haven't read the code for O'Caml, I deduce from this that > > there is deep magic in place to determine when to turn 'a into '_a, and > > in many cases, the heuristic is wrong (in the conservative direction: > > in this case, 'a should not be turned into '_a until b is applied). > >There is no deep magic, no heuristics. There is just a type system >which guarantees type soundness (i.e. "well typed programs cannot >produce runtime type errors"). > >If you want the type system and all the historical details, read my >paper "Relaxing the value restriction" at > http://wwwfun.kurims.kyoto-u.ac.jp/~garrigue/papers/ > >In a nutshell, ocaml uses an improvement of the value restriction. >With the value restriction, only definitions which are syntactical >values (i.e. that do not call functions, etc when defined) are allowed >to contain polymorphic type variables. >This is improved by allowing covariant type variables to be >generalized in all cases. Your first example is ok, because list is a >covariant type, but your second fails, because array is not (you may >assign to an array, and you would have to look at the code to see that >each call to b creates a different array) > >Of course, one could think of many clever tricks by looking >at what the code actually does. The above paper describes some of the >"crazy" things Xavier Leroy and others tried in the past, which >actually subsume your ideas, before they all decided this was too >complicated. The main reason is that, if something is going to break >at module boundaries, then it is not really useful. > >Good reading, > >Jacques Garrigue > >_______________________________________________ >Caml-list mailing list. Subscription management: >http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list >Archives: http://caml.inria.fr >Beginner's list: http://groups.yahoo.com/group/ocaml_beginners >Bug reports: http://caml.inria.fr/bin/caml-bugs ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] '_a 2005-01-29 0:33 ` [Caml-list] '_a Dave Berry @ 2005-02-02 9:17 ` Jacques Garrigue 0 siblings, 0 replies; 19+ messages in thread From: Jacques Garrigue @ 2005-02-02 9:17 UTC (permalink / raw) To: daveberry; +Cc: caml-list Dave, Thanks for your comments. > Basically, the touted advantages of adopting the value polymorphism > rule were: > > 1. The formal semantics became simpler. > 2. Eliminating the different types of type variable made the language > easier to explain, without affecting expressibility in practice. > 3. It enabled typeful compilation (as you note in your paper). I personally believe that the main advantage is 0. Keep the typing abstract from implementation details. This limits one very strongly when seeking alternatives. > I have never believed either half of point 2. The value polymorphism rule > means that you have to explain about references and their effect on type > inference even for some simple programs that don't use references at all, > such as the example that Mike gave. This "raises the bar" for explaining > type inference to beginners. Furthermore, expressiveness is affected for > the examples that you give in your paper. We had to change several parts > of our code when we adopted the value polymorphism rule. However, we felt > (and I still think) that points 1 and 3, particularly point 3, outweigh > these disadvantages. > > From a practical point of view, I like your approach. However, it does > negate point 1 above, because it makes the formal semantics more complex > again - although this is less of a problem in O'Caml, because its semantics > are already so complicated compared to SML97. (I do not intend that remark > as an insult). It will be interesting to see how your approach affects > point 2 - novices may still encounter the value restriction in a simple > program, and the new system will be more complicated to explain. I agree with your first remark on point 2: while it avoids introducing a new kind of variables, the value restriction is really confusing for beginners. The theoretical rule may be simple, but it is not intuitive, so one only understands it through a series of approximations. So my argument goes as: avoid introducing a new kind of variables (for the sake of abstraction), but get as much polymorphism as possible, as the non-generalizable case should be the exception. Even if the rule for what to generalize is more complex, in some ways it is closer to intuition. In "most" cases, it is just okay to generalize the result of function applications. Basically, non-generalizable cases with the relaxed restriction end up belonging to two categories: * partial applications * mutable data structures I believe this is easy to understand why they have to be restricted. For the semantics, this should not be too hard. You just need to introduce subtyping. And ocaml already has subtyping for evident reasons. Typed compilation shall also be ok. Note that we only allow generalization of covariant variables (meaning "bottom"), not contravariant ones (meaning "top"). By definition the covariant variables correspond to no value, so you shouldn't need them to determine the data representation. If we were also to generalize purely contravariant variables, then we need a top object, meaning that the representation would have to be uniform. > I sometimes wonder whether it would help to have a "pure" annotation for > function types that would require the implementation to not use references > nor to raise exceptions. I don't mean a detailed closure analysis, just a > simple flag. Most practical functions would not be pure, but many simple > ones could be, and these could be used to introduce people to the basics of > functional programming without raising the complication of the value > polymorphism rule. (You wouldn't get a theory paper out of it > though). Unfortunately I'm no longer working on programming languages and > so I don't have the capability to explore this. It may be a half-baked > idea that doesn't work in practice. This is also an idea that crosses my mind once in a while, but I never tried to formalize it further. Looks like the main problem would be to decide where to use this annotation. You can quickly get back into the above abstraction problem, if purity is not considered as an essential part of the semantics. So this looks like an idea hard to apply in practice. Jacques Garrigue ^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [Caml-list] '_a 2005-01-27 0:51 ` [Caml-list] '_a Jacques Garrigue 2005-01-27 9:34 ` skaller 2005-01-29 0:33 ` [Caml-list] '_a Dave Berry @ 2005-02-03 7:41 ` Florian Hars 2 siblings, 0 replies; 19+ messages in thread From: Florian Hars @ 2005-02-03 7:41 UTC (permalink / raw) To: Jacques Garrigue; +Cc: hamburg, caml-list Jacques Garrigue wrote: > From: Mike Hamburg <hamburg@fas.harvard.edu> >># let b = let f x () = [|x|] in f [];; >>val b : unit -> '_a list array = <fun> > > but your second fails, because array is not [covariant] (you may > assign to an array, and you would have to look at the code to see that > each call to b creates a different array) Of course, in this case the usual trick of some greek letter-expansion (was it eta?) works: # let b () = let f x () = [|x|] in f [] ();; val b : unit -> 'a list array = <fun> Maybe this should be mentioned in the "A type variable starts with _?" part of the FAQ, like: | In this case the type checker errs on the conservative side, as the function | map_id could be fully polymorphic. This is caused by the fact that the | definition is given in the so called eta-reduced form, and you can recover | the full polymorphism by giving it in eta-expanded form, which the type | checker handles more gracefully: | | # let map_id l = List.map (function x -> x) l;; | val map_id : 'a list -> 'a list = <fun> Yours, Florian. -- #!/bin/sh - set - `type -p $0` 'tr [a-m][n-z]RUXJAKBOZ [n-z][a-m]EH$W/@OBM' fu XUBZRA.fvt\ angher echo;while [ "$5" != "" ];do shift;done;$4 "gbhpu $3;znvy sKunef.qr<$3\ &&frq -a -rc "`$4 "$0"|$1`">$3;rpub 'Jr ner Svtangher bs Obet.'"|$1|`$4 $2|$1` ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2005-02-03 7:42 UTC | newest] Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2005-01-27 0:19 '_a Mike Hamburg 2005-01-27 0:51 ` [Caml-list] '_a Jacques Garrigue 2005-01-27 9:34 ` skaller 2005-01-27 10:02 ` Alex Baretta 2005-01-27 14:13 ` '_a Vincenzo Ciancia 2005-01-27 19:39 ` [Caml-list] '_a Jacques Carette 2005-01-28 0:57 ` skaller 2005-01-28 13:25 ` '_a Stefan Monnier 2005-01-28 14:46 ` [Caml-list] '_a skaller 2005-01-28 14:46 ` Keith Wansbrough 2005-01-28 15:48 ` skaller 2005-01-29 1:37 ` Michael Walter 2005-01-28 13:42 ` Christophe TROESTLER 2005-01-28 14:50 ` skaller 2005-01-28 12:54 ` Richard Jones 2005-01-28 14:39 ` Alex Baretta 2005-01-29 0:33 ` [Caml-list] '_a Dave Berry 2005-02-02 9:17 ` Jacques Garrigue 2005-02-03 7:41 ` Florian Hars
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox