* [Caml-list] GADTs and Menhir @ 2015-03-31 18:16 Andre Nathan 2015-03-31 19:45 ` Francois Pottier 0 siblings, 1 reply; 8+ messages in thread From: Andre Nathan @ 2015-03-31 18:16 UTC (permalink / raw) To: caml users [-- Attachment #1: Type: text/plain, Size: 1542 bytes --] Hello I'm trying to learn a bit about GADTs, but I can't figure out how to make them work with Menhir. I have defined the following GADT: (* foo.ml *) type 'a t = | Int : int -> int t | Bool : bool -> bool t With this definition I can write an "eval" function as let eval (type t) (foo : t Foo.t) : t = match foo with | Foo.Int i -> i | Foo.Bool b -> b Now considering the simple parser and lexer below, (* parser.mly *) %{ open Foo %} %token <int> INTEGER %token <bool> BOOL %token EOF %start <'a Foo.t> start %% start: | value; EOF { $1 } ; value: | i = INTEGER { Int i } | b = BOOL { Bool b } ; (* lexer.mll *) { open Parser } let digit = ['0'-'9'] let boolean = "true" | "false" rule token = parse | [' ' '\t'] { token lexbuf } | '\n' { Lexing.new_line lexbuf; token lexbuf } | digit+ as i { INTEGER (int_of_string i) } | boolean as b { BOOL (bool_of_string b) } | eof { EOF } when I try to compile this I get the error below: File "parser.mly", line 15, characters 43-52: Error: This expression has type int Foo.t but an expression was expected of type bool Foo.t Type int is not compatible with type bool This is the | b = BOOL { Bool b } line. I believe the error comes from not having the locally-abstract type annotations in the code generated by Menhir. Is there any way around this? Thanks in advance, Andre [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 490 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] GADTs and Menhir 2015-03-31 18:16 [Caml-list] GADTs and Menhir Andre Nathan @ 2015-03-31 19:45 ` Francois Pottier 2015-03-31 20:41 ` Andre Nathan 0 siblings, 1 reply; 8+ messages in thread From: Francois Pottier @ 2015-03-31 19:45 UTC (permalink / raw) To: Andre Nathan; +Cc: caml users On Tue, Mar 31, 2015 at 03:16:32PM -0300, Andre Nathan wrote: > value: > | i = INTEGER { Int i } > | b = BOOL { Bool b } The problem lies here. What is the OCaml type of the symbol "value"? The OCaml compiler complains that in one branch it seems to be int Foo.t, but in the other branch it seems to be bool Foo.t. (The problem does not have anything to do with Menhir, really.) Usually, it is not advisable to try to perform parsing and type-checking in one single phase. So, my advice would be to: 1- perform parsing in the usual way, constructing ASTs in a normal algebraic data type "ast" (not a GADT); 2- perform type-checking (or type inference). If you insist on using GADTs, you will probably need a GADT of terms (your type 'a Foo.t) and a GADT of type representations ('a ty) and the type-checking function will have type ast -> 'a ty -> 'a Foo.t option which means that, given an untyped term and a representation of an expected type, it either fails or succeeds and produces a typed term. There are examples of this kind of thing online, e.g. http://www.cs.ox.ac.uk/projects/gip/school/tc.hs by Stephanie Weirich. Hope this helps, -- François Pottier Francois.Pottier@inria.fr http://gallium.inria.fr/~fpottier/ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] GADTs and Menhir 2015-03-31 19:45 ` Francois Pottier @ 2015-03-31 20:41 ` Andre Nathan 2015-04-01 12:16 ` Andre Nathan 0 siblings, 1 reply; 8+ messages in thread From: Andre Nathan @ 2015-03-31 20:41 UTC (permalink / raw) To: Francois.Pottier; +Cc: caml users [-- Attachment #1: Type: text/plain, Size: 757 bytes --] On 03/31/2015 04:45 PM, Francois Pottier wrote: > The problem lies here. What is the OCaml type of the symbol "value"? I see now. > 2- perform type-checking (or type inference). > If you insist on using GADTs, you will probably need > a GADT of terms (your type 'a Foo.t) and > a GADT of type representations ('a ty) > and the type-checking function will have type > ast -> 'a ty -> 'a Foo.t option > which means that, given an untyped term and > a representation of an expected type, it > either fails or succeeds and produces a typed term. Can you give me an example of what this "GADT of type representations" would look like? I couldn't understand the Haskell example... Thanks, Andre [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 490 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] GADTs and Menhir 2015-03-31 20:41 ` Andre Nathan @ 2015-04-01 12:16 ` Andre Nathan 2015-04-01 12:27 ` Jeremy Yallop 2015-04-01 13:16 ` Gerd Stolpmann 0 siblings, 2 replies; 8+ messages in thread From: Andre Nathan @ 2015-04-01 12:16 UTC (permalink / raw) To: caml users [-- Attachment #1: Type: text/plain, Size: 1549 bytes --] On 03/31/2015 05:41 PM, Andre Nathan wrote: > Can you give me an example of what this "GADT of type representations" > would look like? I couldn't understand the Haskell example... I found a reference to an email from Jeremy Yallop to the list from 2013 [1], and managed to get it working with the following solution: (* foo.ml *) open Printf type 'a t = | Int : int -> int t | Bool : bool -> bool t type ast = [ `Int of int | `Bool of bool ] type any = | Any : 'a t -> any let typed = function | `Int i -> Any (Int i) | `Bool b -> Any (Bool b) let print : type a. a t -> unit = function | Int i -> printf "%d\n" i | Bool b -> printf "%b\n" b The parser now returns a `Foo.ast`: value: | i = INTEGER { `Int i } | b = BOOL { `Bool b } ; and with that I can print the parsed value with let ast = Parser.start Lexer.token lexbuf in let Foo.Any t = Foo.typed ast in Foo.print t I'm happy that it works, but the `any` type is a bit of a mistery to me. In Jeremy's email it's explained as "An existential to hide the type index of a well-typed AST, making it possible to write functions that return constructed ASTs whose type is not statically known." Does anyone have a reference to literature that explains this technique (I'm guessing that would be Pierce's book)? The OCaml manual briefly shows an example with a `dyn` type, but not much is said about it. Thanks, Andre [1] https://sympa.inria.fr/sympa/arc/caml-list/2013-01/msg00013.html [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 490 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] GADTs and Menhir 2015-04-01 12:16 ` Andre Nathan @ 2015-04-01 12:27 ` Jeremy Yallop 2015-04-01 12:43 ` Andre Nathan 2015-04-01 13:16 ` Gerd Stolpmann 1 sibling, 1 reply; 8+ messages in thread From: Jeremy Yallop @ 2015-04-01 12:27 UTC (permalink / raw) To: Andre Nathan; +Cc: caml users On 1 April 2015 at 13:16, Andre Nathan <andre@digirati.com.br> wrote: > I'm happy that it works, but the `any` type is a bit of a mistery to me. > In Jeremy's email it's explained as > > "An existential to hide the type index of a well-typed AST, making it > possible to write functions that return constructed ASTs whose type is > not statically known." > > Does anyone have a reference to literature that explains this technique > (I'm guessing that would be Pierce's book)? The OCaml manual briefly > shows an example with a `dyn` type, but not much is said about it. There's a more detailed explanation of the technique in the lecture notes on the following page: http://www.cl.cam.ac.uk/teaching/1415/L28/materials.html The section you want is 8.4.2 Pattern: building GADT values on p83 of the notes from 9 February, but it might also be helpful to look through some of the earlier notes for background. Jeremy. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] GADTs and Menhir 2015-04-01 12:27 ` Jeremy Yallop @ 2015-04-01 12:43 ` Andre Nathan 0 siblings, 0 replies; 8+ messages in thread From: Andre Nathan @ 2015-04-01 12:43 UTC (permalink / raw) To: Jeremy Yallop; +Cc: caml users [-- Attachment #1: Type: text/plain, Size: 446 bytes --] On 04/01/2015 09:27 AM, Jeremy Yallop wrote: > There's a more detailed explanation of the technique in the lecture > notes on the following page: > > http://www.cl.cam.ac.uk/teaching/1415/L28/materials.html > > The section you want is > > 8.4.2 Pattern: building GADT values > > on p83 of the notes from 9 February, but it might also be helpful to > look through some of the earlier notes for background. Thank you! [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 490 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] GADTs and Menhir 2015-04-01 12:16 ` Andre Nathan 2015-04-01 12:27 ` Jeremy Yallop @ 2015-04-01 13:16 ` Gerd Stolpmann 2015-04-01 18:12 ` Andre Nathan 1 sibling, 1 reply; 8+ messages in thread From: Gerd Stolpmann @ 2015-04-01 13:16 UTC (permalink / raw) To: Andre Nathan; +Cc: caml users [-- Attachment #1: Type: text/plain, Size: 2880 bytes --] Am Mittwoch, den 01.04.2015, 09:16 -0300 schrieb Andre Nathan: > type 'a t = > | Int : int -> int t > | Bool : bool -> bool t > > type ast = > [ `Int of int > | `Bool of bool > ] > > type any = > | Any : 'a t -> any ... > I'm happy that it works, but the `any` type is a bit of a mistery to me. > In Jeremy's email it's explained as > > "An existential to hide the type index of a well-typed AST, making it > possible to write functions that return constructed ASTs whose type is > not statically known." A couple of weeks ago I ran into the same problem. It is just a matter of not being accustomed to how GADTs work, and not having the right design patterns in your mind. OCaml users are used to expose any polymorphism and that it is not directly possible to hide it. So, normally if there is a variable 'a on the right side of the type definition, it must also appear on the left side (e.g. type 'a foo = Case1 of 'a | Case2 of ...). This is not required for GADTs, because the variable is bound by the case it applies to (i.e. for the Int case you have 'a=int and for the Bool case you have 'a=bool). So it is implicitly known. Because of this, many people prefer to write type _ t = (* look here, no 'a anymore *) | Int : int -> int t | Bool : bool -> bool t and type any = | Any : _ t -> any The type parameter still exists because t is still polymorphic, but you cannot do anything with it unless you match against the cases. Now, "any" goes a step further, and even discards this parameter. It's a matter of perspective: if 'a is case-dependent but not dependent to anything outside t, you can also consider t as monomorphic. In other words: Knowing all cases binds 'a. You need Any if you want to put several t values into a container, e.g. Does not work: [ Int 34; Bool true ] Does work: [ Any(Int 34); Any(Bool true) ] This nice thing with GADTs is that you can "undo" this change of perspective by matching against the cases: let print = ... (* as you defined it *) let print_any (Any x) = print x let () = List.iter print_any [ Any(Int 34); Any(Bool true) ] Gerd > Does anyone have a reference to literature that explains this technique > (I'm guessing that would be Pierce's book)? The OCaml manual briefly > shows an example with a `dyn` type, but not much is said about it. > > Thanks, > Andre > > [1] https://sympa.inria.fr/sympa/arc/caml-list/2013-01/msg00013.html > -- ------------------------------------------------------------ Gerd Stolpmann, Darmstadt, Germany gerd@gerd-stolpmann.de My OCaml site: http://www.camlcity.org Contact details: http://www.camlcity.org/contact.html Company homepage: http://www.gerd-stolpmann.de ------------------------------------------------------------ [-- Attachment #2: This is a digitally signed message part --] [-- Type: application/pgp-signature, Size: 473 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] GADTs and Menhir 2015-04-01 13:16 ` Gerd Stolpmann @ 2015-04-01 18:12 ` Andre Nathan 0 siblings, 0 replies; 8+ messages in thread From: Andre Nathan @ 2015-04-01 18:12 UTC (permalink / raw) To: Gerd Stolpmann; +Cc: caml users [-- Attachment #1: Type: text/plain, Size: 1415 bytes --] Hi Gerd On 04/01/2015 10:16 AM, Gerd Stolpmann wrote: > You need Any if you want to put several t values into a container, e.g. > > Does not work: [ Int 34; Bool true ] > Does work: [ Any(Int 34); Any(Bool true) ] > > This nice thing with GADTs is that you can "undo" this change of > perspective by matching against the cases: But is there any way to, say, write some sort of lookup function for values in such containers? Eg. given an eval function, this doesn't work: let rec find k l = match l with | [] -> raise Not_found | (k', v)::rest -> if k = k' then v else find k rest let foo () = let dict = ["int", Any (Int 1); "bool", Any (Bool true)] in let Any x = find "int" dict in eval x ^^^^^^ Error: This expression has type a#0 but an expression was expected of type a#0 The type constructor a#0 would escape its scope I don't understand why `eval` can be written but it's result cannot be returned from another function. I tried introducing a type variable but it still doesn't work let foo (type a) () : a = let dict = ["x", Any (Int 1); "y", Any (Bool true)] in let Any (x : a t) = find "x" dict in eval x ^^^^^^^^^ Error: This pattern matches values of type a t but a pattern was expected which matches values of type a#0 t Type a is not compatible with type a#0 Thanks, Andre [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 490 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2015-04-01 18:12 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-03-31 18:16 [Caml-list] GADTs and Menhir Andre Nathan 2015-03-31 19:45 ` Francois Pottier 2015-03-31 20:41 ` Andre Nathan 2015-04-01 12:16 ` Andre Nathan 2015-04-01 12:27 ` Jeremy Yallop 2015-04-01 12:43 ` Andre Nathan 2015-04-01 13:16 ` Gerd Stolpmann 2015-04-01 18:12 ` Andre Nathan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox