* [Caml-list] typer strangeness (3.12.0) @ 2011-08-06 12:50 rixed 2011-08-06 12:58 ` Fabrice Le Fessant 2011-08-14 9:46 ` Jacques Garrigue 0 siblings, 2 replies; 7+ messages in thread From: rixed @ 2011-08-06 12:50 UTC (permalink / raw) To: caml-list Given these types: (* A parser is given a list of items and returns either a Failure indication, a Wait for more inputs indication, or a result (composed of a new value and the list of tokens that were unused) *) type ('a, 'b) parzer_result = Wait | Res of ('a * 'b list) | Fail type ('a, 'b) parzer = 'b list -> ('a, 'b) parzer_result And this function used to pipe two parsers together: (* Use the results of the first parser as the input elements of the second. Stop as soon as p2 returns a result or fails. Notice that if p1 is a ('a, 'b) parzer and p2 a ('c, 'a) parzer then pipe p1 p2 is a ('c, 'b) parzer, which comes handy but p2 is then forced to consume everything ! *) let (pipe : ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer) p1 p2 = let p1_rem = ref [] in fun bs -> match p1 (!p1_rem @ bs) with | Fail -> Fail | Wait -> Wait | Res (res, rem) -> p1_rem := rem ; (match p2 [res] with | Res (res', rem') -> if rem' <> [] then Printf.printf "WRN: second end of a pipe did not consume eveything !\n" ; Res (res', !p1_rem) | Fail -> Fail | Wait -> Wait) This pipe function has the expected type : # pipe;; - : ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer = <fun> Now, if I change it's last line for : "| x -> x)", ie not repeating Wait and Fail, the result is very different : # pipe;; - : ('a, 'a) parzer -> ('b, 'a) parzer -> ('b, 'a) parzer = <fun> How come? And why didn't the compiler complain since I explicitely typed the function definition? ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Caml-list] typer strangeness (3.12.0) 2011-08-06 12:50 [Caml-list] typer strangeness (3.12.0) rixed @ 2011-08-06 12:58 ` Fabrice Le Fessant 2011-08-06 17:03 ` Guillaume Yziquel 2011-08-14 6:19 ` rixed 2011-08-14 9:46 ` Jacques Garrigue 1 sibling, 2 replies; 7+ messages in thread From: Fabrice Le Fessant @ 2011-08-06 12:58 UTC (permalink / raw) To: caml-list; +Cc: rixed [-- Attachment #1: Type: text/plain, Size: 2714 bytes --] The type constraint that you specified does not constraint the polymorphism of the type. To declare a polymorphic constraint, you must use (with OCaml >= 3.12.0) : let pipe : 'a 'b 'c. ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer = fun p1 p2 -> let p1_rem = ref [] in fun bs -> match p1 (!p1_rem @ bs) with | Fail -> Fail | Wait -> Wait | Res (res, rem) -> p1_rem := rem ; (match p2 [res] with | Res (res', rem') -> if rem' <> [] then Printf.printf "WRN: second end of a pipe did not consume eveything !\n" ; Res (res', !p1_rem) | x -> x) In which case you get the following error : Error: This definition has type 'a 'b. ('b, 'b) parzer -> ('a, 'b) parzer -> ('a, 'b) parzer which is less general than 'c 'd 'e. ('e, 'd) parzer -> ('c, 'e) parzer -> ('c, 'd) parzer Fabrice On 08/06/2011 02:50 PM, rixed@happyleptic.org wrote: > Given these types: > > (* A parser is given a list of items and returns either a Failure indication, a > Wait for more inputs indication, or a result (composed of a new value and the list > of tokens that were unused) *) > type ('a, 'b) parzer_result = Wait | Res of ('a * 'b list) | Fail > type ('a, 'b) parzer = 'b list -> ('a, 'b) parzer_result > > And this function used to pipe two parsers together: > > (* Use the results of the first parser as the input elements of the second. > Stop as soon as p2 returns a result or fails. > Notice that if p1 is a ('a, 'b) parzer and p2 a ('c, 'a) parzer > then pipe p1 p2 is a ('c, 'b) parzer, which comes handy but p2 is then > forced to consume everything ! *) > let (pipe : ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer) p1 p2 = > let p1_rem = ref [] in > fun bs -> match p1 (!p1_rem @ bs) with > | Fail -> Fail > | Wait -> Wait > | Res (res, rem) -> > p1_rem := rem ; > (match p2 [res] with > | Res (res', rem') -> > if rem' <> [] then Printf.printf "WRN: second end of a pipe did not consume eveything !\n" ; > Res (res', !p1_rem) > | Fail -> Fail | Wait -> Wait) > > This pipe function has the expected type : > > # pipe;; > - : ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer = <fun> > > Now, if I change it's last line for : "| x -> x)", ie not repeating Wait and Fail, the result is > very different : > > # pipe;; > - : ('a, 'a) parzer -> ('b, 'a) parzer -> ('b, 'a) parzer = <fun> > > How come? > And why didn't the compiler complain since I explicitely typed the function definition? > > [-- Attachment #2: fabrice_le_fessant.vcf --] [-- Type: text/x-vcard, Size: 380 bytes --] begin:vcard fn:Fabrice LE FESSANT n:LE FESSANT;Fabrice org:INRIA Saclay -- Ile-de-France;P2P & OCaml adr;quoted-printable:;;Parc Orsay Universit=C3=A9 ;Orsay CEDEX;;91893;France email;internet:fabrice.le_fessant@inria.fr title;quoted-printable:Charg=C3=A9 de Recherche tel;work:+33 1 74 85 42 14 tel;fax:+33 1 74 85 42 49 url:http://fabrice.lefessant.net/ version:2.1 end:vcard ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Caml-list] typer strangeness (3.12.0) 2011-08-06 12:58 ` Fabrice Le Fessant @ 2011-08-06 17:03 ` Guillaume Yziquel 2011-08-14 6:19 ` rixed 1 sibling, 0 replies; 7+ messages in thread From: Guillaume Yziquel @ 2011-08-06 17:03 UTC (permalink / raw) To: Fabrice Le Fessant; +Cc: caml-list, rixed Le Saturday 06 Aug 2011 à 14:58:49 (+0200), Fabrice Le Fessant a écrit : > The type constraint that you specified does not constraint the > polymorphism of the type. To declare a polymorphic constraint, you must > use (with OCaml >= 3.12.0) : > > let pipe : 'a 'b 'c. ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer = > fun p1 p2 -> > let p1_rem = ref [] in > fun bs -> match p1 (!p1_rem @ bs) with > | Fail -> Fail > | Wait -> Wait > | Res (res, rem) -> > p1_rem := rem ; > (match p2 [res] with > | Res (res', rem') -> > if rem' <> [] then Printf.printf "WRN: second end of > a pipe did not consume eveything !\n" ; > Res (res', !p1_rem) > | x -> x) > > In which case you get the following error : > Error: This definition has type > 'a 'b. ('b, 'b) parzer -> ('a, 'b) parzer -> ('a, 'b) parzer > which is less general than > 'c 'd 'e. ('e, 'd) parzer -> ('c, 'e) parzer -> ('c, 'd) parzer > > Fabrice You can achieve a similar goal with stuff like: # let f (type a) (type b) (x : a) = (x : b);; Error: This expression has type a but an expression was expected of type b The benefit of which is that you do not have f explicitely quantified as opposed to the "let f : 'a 'b. ..." alternative. Feels more ocaml-ish to me. -- Guillaume Yziquel ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Caml-list] typer strangeness (3.12.0) 2011-08-06 12:58 ` Fabrice Le Fessant 2011-08-06 17:03 ` Guillaume Yziquel @ 2011-08-14 6:19 ` rixed 2011-08-14 7:08 ` Guillaume Yziquel 1 sibling, 1 reply; 7+ messages in thread From: rixed @ 2011-08-14 6:19 UTC (permalink / raw) To: caml-list Thank you for your answers, but you aimed too high above my understanding :-) -[ Sat, Aug 06, 2011 at 02:58:49PM +0200, Fabrice Le Fessant ]---- > The type constraint that you specified does not constraint the > polymorphism of the type. To declare a polymorphic constraint, you must > use (with OCaml >= 3.12.0) : > > let pipe : 'a 'b 'c. ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer = Never saw this notation before. I checked in the manual but beside in the language grammar (in the definition of a poly-typexpr) I could find more explanation. Is it explained somewhere why this notation was required and why this: let pipe : ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer = is not enough? What's adding the "'a 'b 'c. " exactly ? I understand that it forbids ocaml to consider that the various 'a (for instance) may be differents 'a, but I don't understand why ocaml migh do that in the first place (especially to produce a _less general_ type than the one given) ? If it's not a bug, then what's the purpose ? As for my second question, I received no answer (was: why changing the pattern match for something that should be equivalent changes the infered type). If you suspect this can be a bug then I can look for a simple reproductible case. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Caml-list] typer strangeness (3.12.0) 2011-08-14 6:19 ` rixed @ 2011-08-14 7:08 ` Guillaume Yziquel 2011-08-14 7:59 ` rixed 0 siblings, 1 reply; 7+ messages in thread From: Guillaume Yziquel @ 2011-08-14 7:08 UTC (permalink / raw) To: rixed; +Cc: caml-list Le Sunday 14 Aug 2011 à 08:19:59 (+0200), rixed@happyleptic.org a écrit : > Thank you for your answers, but you aimed too high above my > understanding :-) > > -[ Sat, Aug 06, 2011 at 02:58:49PM +0200, Fabrice Le Fessant ]---- > > The type constraint that you specified does not constraint the > > polymorphism of the type. To declare a polymorphic constraint, you must > > use (with OCaml >= 3.12.0) : > > > > let pipe : 'a 'b 'c. ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer = > > Never saw this notation before. I checked in the manual but beside in > the language grammar (in the definition of a poly-typexpr) I could find > more explanation. > Is it explained somewhere why this notation was required and why this: > > let pipe : ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer = > > is not enough? What's adding the "'a 'b 'c. " exactly ? It does explicit universal quantification on the type. So you create a monomorphic type out of that. \forall 'a. 'a -> 'a is a type for functions that can be used as int -> int, float -> float, string -> string, etc... \forall 'a. 'a -> 'a is not a polymorphic type as 'a is bound. By contrast, 'a -> 'a is polymorphic in the sense that is does depend on the 'a to form a monomorphic type. > I understand > that it forbids ocaml to consider that the various 'a (for instance) may > be differents 'a, but I don't understand why ocaml migh do that in the > first place (especially to produce a _less general_ type than the one > given) ? If it's not a bug, then what's the purpose ? Allowing universal quantification of types allows you to have a richer type system. Such as system F or ML^F. Typical issues it helps to solve are existential types or polymorphic recursion. It does more than simply force the various 'a to be distinct. It also binds them to form a monomorphic type. If you just want the types to be different, but not bound, you have the (type s) syntax to introduce types explicitely. > As for my second question, I received no answer (was: why changing the > pattern match for something that should be equivalent changes the infered > type). If you suspect this can be a bug then I can look for a simple > reproductible case. It's likely not a bug. What you did is replace | Fail -> Fail | Wait -> Wait by | x -> x. If you take into consideration the fact that Fail and Wait are constructors for a polymorphic type, what you have in the clauses and what is returned may have different types because you have not unified the type parameters of this datatype. However, when you write | x -> x, you unify x and x, and hence unify the type parameters. That likely explains what you observe. -- Guillaume Yziquel ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Caml-list] typer strangeness (3.12.0) 2011-08-14 7:08 ` Guillaume Yziquel @ 2011-08-14 7:59 ` rixed 0 siblings, 0 replies; 7+ messages in thread From: rixed @ 2011-08-14 7:59 UTC (permalink / raw) To: caml-list Thank you very much for these additional explanations. Not that I understood much, but next time I encounter such a strange behavior I will know that my simple programs are made more complex because it's helping some superior lifeform somewhere in the galaxy to make much more complex programs. :) (that plus the browser cache-thing, I feel exta stupid this morning :)) ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Caml-list] typer strangeness (3.12.0) 2011-08-06 12:50 [Caml-list] typer strangeness (3.12.0) rixed 2011-08-06 12:58 ` Fabrice Le Fessant @ 2011-08-14 9:46 ` Jacques Garrigue 1 sibling, 0 replies; 7+ messages in thread From: Jacques Garrigue @ 2011-08-14 9:46 UTC (permalink / raw) To: rixed; +Cc: caml-list On 2011/08/06, at 21:50, rixed@happyleptic.org wrote: > Given these types: > > (* A parser is given a list of items and returns either a Failure indication, a > Wait for more inputs indication, or a result (composed of a new value and the list > of tokens that were unused) *) > type ('a, 'b) parzer_result = Wait | Res of ('a * 'b list) | Fail > type ('a, 'b) parzer = 'b list -> ('a, 'b) parzer_result > > And this function used to pipe two parsers together: > > (* Use the results of the first parser as the input elements of the second. > Stop as soon as p2 returns a result or fails. > Notice that if p1 is a ('a, 'b) parzer and p2 a ('c, 'a) parzer > then pipe p1 p2 is a ('c, 'b) parzer, which comes handy but p2 is then > forced to consume everything ! *) > let (pipe : ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer) p1 p2 = > let p1_rem = ref [] in > fun bs -> match p1 (!p1_rem @ bs) with > | Fail -> Fail > | Wait -> Wait > | Res (res, rem) -> > p1_rem := rem ; > (match p2 [res] with > | Res (res', rem') -> > if rem' <> [] then Printf.printf "WRN: second end of a pipe did not consume eveything !\n" ; > Res (res', !p1_rem) > | Fail -> Fail | Wait -> Wait) > > This pipe function has the expected type : > > # pipe;; > - : ('a, 'b) parzer -> ('c, 'a) parzer -> ('c, 'b) parzer = <fun> > > Now, if I change it's last line for : "| x -> x)", ie not repeating Wait and Fail, the result is > very different : > > # pipe;; > - : ('a, 'a) parzer -> ('b, 'a) parzer -> ('b, 'a) parzer = <fun> > > How come? If you write "x -> x", the output of p1 is forced to be the same type as the output of the function, which forces the unification of 'a and 'b. Note that this second type is equivalent to > - : ('a, 'a) parzer -> ('c, 'a) parzer -> ('c, 'a) parzer = <fun> If you explicitly write "Fail -> Fail | Wait -> Wait", no such unification happens, and you get the intended type. You can get the same behaviour by writing "Fail | Wait as x -> x", i.e. the type checker is clever enough to not unify the pattern type and the variable type. > And why didn't the compiler complain since I explicitely typed the function definition? You already got your answer. A simple type annotation does not enforce polymorphism. Hope this helps, Jacques ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2011-08-14 9:47 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2011-08-06 12:50 [Caml-list] typer strangeness (3.12.0) rixed 2011-08-06 12:58 ` Fabrice Le Fessant 2011-08-06 17:03 ` Guillaume Yziquel 2011-08-14 6:19 ` rixed 2011-08-14 7:08 ` Guillaume Yziquel 2011-08-14 7:59 ` rixed 2011-08-14 9:46 ` Jacques Garrigue
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox