* [Caml-list] Wanted: GADT examples: string length, counting module x @ 2012-03-22 6:01 oleg 2012-03-22 9:04 ` Gabriel Scherer 2012-03-22 16:58 ` [Caml-list] " Goswin von Brederlow 0 siblings, 2 replies; 8+ messages in thread From: oleg @ 2012-03-22 6:01 UTC (permalink / raw) To: goswin-v-b; +Cc: caml-list Somehow typed tagless interpreters (embeddings of a typed language) and length-parameterized lists with the append function are the most popular examples of GADTs. Neither of them seem to be particularly good or compelling examples. One can write typed interpreters in the final-tagless style, with no GADTs. Writing append function whose type says the length of the resulting list is the sum of the lengths of the argument lists is cute. However, this example does not go too far, as one discovers when trying to write List.filter for length-parameterized lists. The ML2010 talk on GADT emulation specifically used a different illustrating example: a sort of generic programming, or implementing N-morphic functions: http://okmij.org/ftp/ML/first-class-modules/first-class-modules-talk-notes.pdf Polymorphic functions must operate uniformly on their arguments, which means they can't use a specific efficient operation if the argument happens to be of a convenient type (like int of float array). N-morphic functions can take such an advantage. The running example of the talk combined value and the shape information in the same data type: type 'a sif = Int of (int,'a) eq * int | Flo of (float,'a) eq * float val add_sif : 'a sif -> 'a sif -> 'a sif Shape may well be separated from the value: type 'a shape = Int of (int,'a) eq | Flo of (float,'a) eq val add_sif : 'a shape -> 'a -> 'a -> 'a and so we pass values to add_sif without `boxing' and wrapping. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] Wanted: GADT examples: string length, counting module x 2012-03-22 6:01 [Caml-list] Wanted: GADT examples: string length, counting module x oleg @ 2012-03-22 9:04 ` Gabriel Scherer 2012-03-22 16:58 ` [Caml-list] " Goswin von Brederlow 1 sibling, 0 replies; 8+ messages in thread From: Gabriel Scherer @ 2012-03-22 9:04 UTC (permalink / raw) To: oleg; +Cc: goswin-v-b, caml-list In this example, you use GADTs to safely provide runtime type information on untagged data. This can also be seen as a specific case of the "runtime type information" promoted by Alain Frisch [1] or equivalently as a dual (in the sum-of-data vs. product-of-functions sense) of type-classes, where you have a predicate over a subset of the types ("sif" being read as an "is_a_number" type constraint) whose instances are closed / fixed at class definition time, to which operations can be added modularly: `add` now, `mult` later. This can then be related to Pottier and Gauthier's "concretization" of type-classes mentioned in my previous message. [1] http://www.lexifi.com/blog/dynamic-types [2] http://gallium.inria.fr/~fpottier/publis/fpottier-gauthier-hosc.pdf > One can write typed interpreters in the > final-tagless style, with no GADTs. Isn't this true of all GADTs examples? You have already shown that GADTs can be relatively-practically expressible with equality types. I suspect the justification for GADTs is not increased expressivity, but a simpler/more familiar way to implement those type-information-passing techniques -- just as ordinary algebraic datatypes could be expressed with functional encodings, but are more practical and convenient to use in the usual case. Besides, there is the down-to-earth efficiency benefit of directly using first-order data instead of functional encodings. On Thu, Mar 22, 2012 at 7:01 AM, <oleg@okmij.org> wrote: > > Somehow typed tagless interpreters (embeddings of a typed language) > and length-parameterized lists with the append function are the most > popular examples of GADTs. Neither of them seem to be particularly > good or compelling examples. One can write typed interpreters in the > final-tagless style, with no GADTs. Writing append function whose type > says the length of the resulting list is the sum of the lengths of the > argument lists is cute. However, this example does not go too far, as > one discovers when trying to write List.filter for > length-parameterized lists. > > The ML2010 talk on GADT emulation specifically used a different > illustrating example: a sort of generic programming, or implementing > N-morphic functions: > http://okmij.org/ftp/ML/first-class-modules/first-class-modules-talk-notes.pdf > > Polymorphic functions must operate uniformly on their arguments, which > means they can't use a specific efficient operation if the argument > happens to be of a convenient type (like int of float > array). N-morphic functions can take such an advantage. > > The running example of the talk combined value and the shape > information in the same data type: > > type 'a sif = Int of (int,'a) eq * int > | Flo of (float,'a) eq * float > > val add_sif : 'a sif -> 'a sif -> 'a sif > > Shape may well be separated from the value: > > type 'a shape = Int of (int,'a) eq > | Flo of (float,'a) eq > > val add_sif : 'a shape -> 'a -> 'a -> 'a > > and so we pass values to add_sif without `boxing' and wrapping. > > > > > > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa-roc.inria.fr/wws/info/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > ^ permalink raw reply [flat|nested] 8+ messages in thread
* [Caml-list] Re: Wanted: GADT examples: string length, counting module x 2012-03-22 6:01 [Caml-list] Wanted: GADT examples: string length, counting module x oleg 2012-03-22 9:04 ` Gabriel Scherer @ 2012-03-22 16:58 ` Goswin von Brederlow 1 sibling, 0 replies; 8+ messages in thread From: Goswin von Brederlow @ 2012-03-22 16:58 UTC (permalink / raw) To: oleg; +Cc: goswin-v-b, caml-list oleg@okmij.org writes: > Somehow typed tagless interpreters (embeddings of a typed language) > and length-parameterized lists with the append function are the most > popular examples of GADTs. Neither of them seem to be particularly > good or compelling examples. One can write typed interpreters in the > final-tagless style, with no GADTs. Writing append function whose type > says the length of the resulting list is the sum of the lengths of the > argument lists is cute. However, this example does not go too far, as > one discovers when trying to write List.filter for > length-parameterized lists. > > The ML2010 talk on GADT emulation specifically used a different > illustrating example: a sort of generic programming, or implementing > N-morphic functions: > http://okmij.org/ftp/ML/first-class-modules/first-class-modules-talk-notes.pdf An interesting talk to read. But unless I'm mistaken that isn't using ocamls new GADT types but implements it own. MfG Goswin ^ permalink raw reply [flat|nested] 8+ messages in thread
* [Caml-list] Wanted: GADT examples: string length, counting module x @ 2012-03-12 11:30 Goswin von Brederlow 2012-03-19 14:43 ` Gabriel Scherer 0 siblings, 1 reply; 8+ messages in thread From: Goswin von Brederlow @ 2012-03-12 11:30 UTC (permalink / raw) To: caml-list Hi, yesterday I compiled ocaml 3.13 and played around a bit with the GDAT syntax but wasn't overly successfull. Or at least I had higher hopes for it. So it is time to invoke the internet to come up with a better example. :) 1) How do I write a GADT that encodes the length of a string or array? How do I use that to create a string or array? How do I specify a function that takes a string or array of a fixed length? Bonus: How do I specify a function that takes a string or array of a certain length or longer? 2) How do I write a GADT that counts an int module x? Say for an offset into a byte stream to safeguard when access is aligned and when unaligned. Again with an example that creates a value and a function that uses it. Bonus: Have one function that only allows aligned access and one that picks the right aligned/unaligned function to use depending on the type. Below I've included an example for checking aligned access (1/2/4/8 byte aligned). First using GADT and second using old style phantom types. The second looks much longer because it includes the signature needed to make the type (...) off private. The t1/t2/t4/t8 types are just aliases to make the type of the other functions shorter. One thing I couldn't manage is to write a "bind" function with GADTs or bind takeX to a string unless I specify the full type. "takeX s" always switches to '_a types and then gets bound to a specific type on the first use and fail on the second use. On the plus side of GADTs is that you do not need a private type (and therefore the module signature) to make them work. MfG Goswin PS: Other simple examples that show the power of GADTs are welcome too. ---------------------------------------------------------------------- (* Declare GADT type *) type z type u type _ t = | Zero : ((z * u) * (z * u * u * u) * (z * u * u * u * u * u * u * u)) t | Succ : (('a * 'b) * ('c * 'd * 'e * 'f) * ('g * 'h * 'i * 'j * 'k * 'l * 'm * 'n)) t -> (('b * 'a) * ('d * 'e * 'f * 'c) * ('h * 'i * 'j * 'k * 'l * 'm * 'n * 'g)) t (* start of stream *) let zero = (Zero, 0) (* advance by 1, 2, 4 or 8 *) let succ1 x = Succ x let succ2 x = succ1 (succ1 x) let succ4 x = succ2 (succ2 x) let succ8 x = x (* take 1, 2, 4 or 8 bytes with alignment restriction *) let take1 : type a b c d e f g h i j k l m n. string -> (((a * b) * (c * d * e * f) * (g * h * i * j * k * l * m * n)) t * int) -> ((((b * a) * (d * e * f * c) * (h * i * j * k * l * m * n * g)) t * int) * string) = fun s (t, x) -> ((succ1 t, x+1), String.sub s x 1) let take2 : type c d e f g h i j k l m n. string -> (((z * u) * (c * d * e * f) * (g * h * i * j * k * l * m * n)) t * int) -> ((((z * u) * (e * f * c * d) * (i * j * k * l * m * n * g * h)) t * int) * string) = fun s (t, x) -> ((succ2 t, x +2), String.sub s x 2) let take4 : type g h i j k l m n. string -> (((z * u) * (z * u * u * u) * (g * h * i * j * k * l * m * n)) t * int) -> ((((z * u) * (z * u * u * u) * (k * l * m * n * g * h * i * j)) t * int) * string) = fun s (t, x) -> ((succ4 t, x+4), Str ing.sub s x 4) let take8 : string -> (((z * u) * (z * u * u * u) * (z * u * u * u * u * u * u * u)) t * int) -> ((((z * u) * (z * u * u * u) * (z * u * u * u * u * u * u * u)) t * int) * string) = fun s (t, x) -> ((succ8 t, x+8), String.sub s x 8) (* Test string *) let s = "aabbccccdddddddd" (* extract things from string *) let foo () = let (off, a) = take1 s zero in let (off, b) = take1 s off in let (off, c) = take2 s off in let (off, d) = take4 s off in let (off, e) = take8 s off in Printf.printf "%s %s %s %s %s\n" a b c d e (* using take2/4/8 with an offset that isn't aligned gives a compile time type error: let bad () = let (off, a) = take1 s zero in take8 s off *) ---------------------------------------------------------------------- module M : sig (* Types for aligned unaligned tracking *) type z type u (* The type of an offset into a stream *) type ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off = private int (* Start of the stream *) val zero : (z, u, z, u, u, u, z, u, u, u, u, u, u, u) off (* Coercion to integer, same as (x :> int) *) val get : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> int (* Advance the position by 1, 2, 4 or 8 *) val succ : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ('b , 'a, 'd, 'e, 'f, 'c, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'g) off val succ2 : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ('a , 'b, 'e, 'f, 'c, 'd, 'i, 'j, 'k, 'l, 'm, 'n, 'g, 'h) off val succ4 : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ('a , 'b, 'c, 'd, 'e, 'f, 'k, 'l, 'm, 'n, 'g, 'h, 'i, 'j) off val succ8 : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ('a , 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off (* Aliases for shorter type names *) type ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t1 = ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> (('b, 'a, 'd, 'e, 'f, 'c, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'g) off * string) type ('c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t2 = (z, u, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ((z, u, 'e, 'f, 'c, 'd, 'i, 'j, 'k, 'l, 'm, 'n, 'g, 'h) off * string) type ('g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t4 = (z, u, z, u, u, u, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ((z, u, z, u, u, u, 'k, 'l, 'm, 'n, 'g, 'h, 'i, 'j) off * string) type t8 = (z, u, z, u, u, u, z, u, u, u, u, u, u, u) off -> ((z, u, z, u, u, u, z, u, u, u, u, u, u, u) off * string) (* Take 1, 2, 4 or 8 byte with alignment restriction *) val take1 : string -> ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t1 val take2 : string -> ('c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t2 val take4 : string -> ('g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t4 val take8 : string -> t8 (* Bind all take functions to a string for easier use *) val bind : string -> (('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t1 * ('c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t2 * ('g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t4 * t8) end = struct (* Types for aligned unaligned tracking *) type z type u (* The type of an offset into a stream *) type ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off = int (* Start of the stream *) let zero = 0 (* Coercion to integer, same as (x :> int) *) let get x = x (* Advance the position by 1, 2, 4 or 8 *) let succ x = x + 1 let succ2 x = succ (succ x) let succ4 x = succ2 (succ2 x) let succ8 x = succ4 (succ4 x) (* Aliases for shorter type names *) type ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t1 = ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> (('b, 'a, 'd, 'e, 'f, 'c, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'g) off * string) type ('c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t2 = (z, u, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ((z, u, 'e, 'f, 'c, 'd, 'i, 'j, 'k, 'l, 'm, 'n, 'g, 'h) off * string) type ('g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t4 = (z, u, z, u, u, u, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ((z, u, z, u, u, u, 'k, 'l, 'm, 'n, 'g, 'h, 'i, 'j) off * string) type t8 = (z, u, z, u, u, u, z, u, u, u, u, u, u, u) off -> ((z, u, z, u, u, u, z, u, u, u, u, u, u, u) off * string) (* Take 1, 2, 4 or 8 byte with alignment restriction *) let take1 = fun s x -> (succ x, String.sub s (get x) 1);; let take2 = fun s x -> (succ2 x, String.sub s (get x) 2);; let take4 = fun s x -> (succ4 x, String.sub s (get x) 4);; let take8 = fun s x -> (succ8 x, String.sub s (get x) 8);; (* Bind all take functions to a string for easier use *) let bind s = (take1 s, take2 s, take4 s, take8 s) end open M (* Test string *) let s = "aabbccccdddddddd" let (t1, t2, t4, t8) = bind s (* extract things from string *) let foo () = let (off, a) = t1 zero in let (off, b) = t1 off in let (off, c) = t2 off in let (off, d) = t4 off in let (off, e) = t8 off in Printf.printf "%s %s %s %s %s\n" a b c d e (* using take2/4/8 with an offset that isn't aligned gives a compile time type error: let bad () = let (off, a) = take1 s zero in take8 s off *) ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] Wanted: GADT examples: string length, counting module x 2012-03-12 11:30 [Caml-list] " Goswin von Brederlow @ 2012-03-19 14:43 ` Gabriel Scherer 2012-03-19 16:13 ` Jesper Louis Andersen 0 siblings, 1 reply; 8+ messages in thread From: Gabriel Scherer @ 2012-03-19 14:43 UTC (permalink / raw) To: Goswin von Brederlow; +Cc: caml-list I suspect you're seeing too much into the GADT as they're being added in OCaml. Your examples are not basically about GADTs, but about dependent types: you want to encode values (and operations on them) at the type level to get more expressivity. This is a well-known and extremely powerful trend in programming languages design, but it also results in complex systems with sophisticated rules. GADTs are not as expressive as full dependent types and, I would argue, their "intended use" is elsewhere. GADTs are plain old algebraic datatypes that come with equality constraints between types. By manipulating GADTs you can reason, in a convenient way, about some types being equal to some other in specific case. The cornerstone example of GADT is the (a) type of well-typed expressions (a expr), where the shape of the expression gives you more information about the type (a) : type _ expr = | Int : int -> int expr | Prod : 'a expr -> 'b expr -> ('a * 'b) expr | IfThenElse : bool expr -> 'a expr -> 'a expr -> 'a expr ... With a GADT type, you can write an evaluation function of type ('a expr -> 'a), for instance, which would not be possible in a type-safe way with the usual algebraic datatype with a phantom type parameter -- it was already possible to use encodings of GADT into first-class modules [1], or finally tagless representations [2] to achieve the same effect, but with more boilerplate and less efficient runtime representations. [1] http://okmij.org/ftp/ML/first-class-modules/ [2] http://okmij.org/ftp/tagless-final/index.html (Remark: a lot of algorithms can be understood as the transformation of data into code, that is evaluating a specific language of commands. It is therefore easy to see "a type of well-typed expressions" in almost every real-life use case.) On the contrary, your intended applications are not directly about equality between types, but about value and computations at the type level. While this can be done with GADTs when extended with enough machinery (in particular higher-kinded type variables and possibly higher-kinded datatypes; see the work on Omega [3] for example), in OCaml that is expected to be heavy, painful to write and read, and hard on the tooling (type inference, error messages, etc.). At some point the benefit of finer static checking does not compensate the overall pain and complexity of the whole process, and I think we enter the realm of unnecessarily complicated programs. [3] http://web.cecs.pdx.edu/~sheard/ I'm no expert on the subject, but I doubt GADT will provide you much more expressivity at the level of which computation you can embed in types. I suspect you'll mostly use the same techniques as you used before with phantom types (the question of signature redundancy being more a technical detail than an indication of expressivity, or lack thereof, of one or another technique). Unary natural numbers will keep being easy to express at the type-level (see draft implementation at the end of this email), and modulo, difference or what not will probably stay tricky and horrible to define if you want the *inference* engine to do all computation. There is also this technique of using term witnesses to do the heavy type lifting and use a logic-programming style at the type level, but that's probably not what you're looking for. A small draft of unary natural numbers mirrored at the type level through GADT. https://gitorious.org/gasche-snippets/ocaml-typed-units/blobs/master/gadts_and_type_level_numbers.ml They quickly get painful and impractical. Implementing addition is already non-trivial -- see Mandelbaum and Stump's 2009 article "GADTs for the OCaml masses" [4] for a more featureful, but no simpler, presentation: [4] https://www.cs.uiowa.edu/~astump/papers/icfp09.pdf I consider using GADT to emulate dependent types to be an advanced, and often counter-productive, level of "type hackery". We should rather seek examples that fit the GADT features well, instead of trying to stretch it to such unreasonable places. For example, François Pottier and Nadji Gauthier's "Polymorphic typed defunctionalization and concretization" article [5] (2004, 2006) uses GADT to represent functions by (well-typed) data tags, and Alain Frisch's use of GADTs for dynamic type witnesses [6]. But with such advanced features, it is of course not easy to draw a line between "reasonable" and "less reasonable" uses. [5] http://gallium.inria.fr/~fpottier/publis/fpottier-gauthier-hosc.pdf [6] http://www.lexifi.com/blog/dynamic-types On Mon, Mar 12, 2012 at 12:30 PM, Goswin von Brederlow <goswin-v-b@web.de> wrote: > Hi, > > yesterday I compiled ocaml 3.13 and played around a bit with the GDAT > syntax but wasn't overly successfull. Or at least I had higher hopes for > it. So it is time to invoke the internet to come up with a better > example. :) > > 1) How do I write a GADT that encodes the length of a string or array? > How do I use that to create a string or array? > How do I specify a function that takes a string or array of a fixed length? > Bonus: How do I specify a function that takes a string or array of a > certain length or longer? > > 2) How do I write a GADT that counts an int module x? Say for an offset > into a byte stream to safeguard when access is aligned and when > unaligned. > Again with an example that creates a value and a function that uses > it. > Bonus: Have one function that only allows aligned access and one that > picks the right aligned/unaligned function to use depending on > the type. > > Below I've included an example for checking aligned access (1/2/4/8 byte > aligned). First using GADT and second using old style phantom types. The > second looks much longer because it includes the signature needed to > make the type (...) off private. The t1/t2/t4/t8 types are just aliases > to make the type of the other functions shorter. > > One thing I couldn't manage is to write a "bind" function with GADTs or > bind takeX to a string unless I specify the full type. "takeX s" always > switches to '_a types and then gets bound to a specific type on the > first use and fail on the second use. > > On the plus side of GADTs is that you do not need a private type (and > therefore the module signature) to make them work. > > MfG > Goswin > > PS: Other simple examples that show the power of GADTs are welcome too. > ---------------------------------------------------------------------- > (* Declare GADT type *) > type z > type u > type _ t = > | Zero : ((z * u) * (z * u * u * u) * (z * u * u * u * u * u * u * u)) t > | Succ : (('a * 'b) * ('c * 'd * 'e * 'f) * ('g * 'h * 'i * 'j * 'k * 'l * 'm > * 'n)) t -> (('b * 'a) * ('d * 'e * 'f * 'c) * ('h * 'i * 'j * 'k * 'l * 'm * 'n > * 'g)) t > > (* start of stream *) > let zero = (Zero, 0) > > (* advance by 1, 2, 4 or 8 *) > let succ1 x = Succ x > let succ2 x = succ1 (succ1 x) > let succ4 x = succ2 (succ2 x) > let succ8 x = x > > (* take 1, 2, 4 or 8 bytes with alignment restriction *) > let take1 : type a b c d e f g h i j k l m n. string -> (((a * b) * (c * d * e * > f) * (g * h * i * j * k * l * m * n)) t * int) -> ((((b * a) * (d * e * f * c) > * (h * i * j * k * l * m * n * g)) t * int) * string) = fun s (t, x) -> ((succ1 > t, x+1), String.sub s x 1) > let take2 : type c d e f g h i j k l m n. string -> (((z * u) * (c * d * e * f) > * (g * h * i * j * k * l * m * n)) t * int) -> ((((z * u) * (e * f * c * d) * (i > * j * k * l * m * n * g * h)) t * int) * string) = fun s (t, x) -> ((succ2 t, x > +2), String.sub s x 2) > let take4 : type g h i j k l m n. string -> (((z * u) * (z * u * u * u) * (g * h > * i * j * k * l * m * n)) t * int) -> ((((z * u) * (z * u * u * u) * (k * l * m > * n * g * h * i * j)) t * int) * string) = fun s (t, x) -> ((succ4 t, x+4), Str > ing.sub s x 4) > let take8 : string -> (((z * u) * (z * u * u * u) * (z * u * u * u * u * u * u * > u)) t * int) -> ((((z * u) * (z * u * u * u) * (z * u * u * u * u * u * u * u)) > t * int) * string) = fun s (t, x) -> ((succ8 t, x+8), String.sub s x 8) > > (* Test string *) > let s = "aabbccccdddddddd" > > (* extract things from string *) > let foo () = > let (off, a) = take1 s zero in > let (off, b) = take1 s off in > let (off, c) = take2 s off in > let (off, d) = take4 s off in > let (off, e) = take8 s off in > Printf.printf "%s %s %s %s %s\n" a b c d e > > (* using take2/4/8 with an offset that isn't aligned gives a compile > time type error: > let bad () = > let (off, a) = take1 s zero in > take8 s off > *) > > ---------------------------------------------------------------------- > module M : sig > (* Types for aligned unaligned tracking *) > type z > type u > (* The type of an offset into a stream *) > type ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off = private int > (* Start of the stream *) > val zero : (z, u, z, u, u, u, z, u, u, u, u, u, u, u) off > (* Coercion to integer, same as (x :> int) *) > val get : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> int > (* Advance the position by 1, 2, 4 or 8 *) > val succ : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ('b , 'a, 'd, 'e, 'f, 'c, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'g) off > val succ2 : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ('a , 'b, 'e, 'f, 'c, 'd, 'i, 'j, 'k, 'l, 'm, 'n, 'g, 'h) off > val succ4 : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ('a , 'b, 'c, 'd, 'e, 'f, 'k, 'l, 'm, 'n, 'g, 'h, 'i, 'j) off > val succ8 : ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ('a , 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off > (* Aliases for shorter type names *) > type ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t1 = ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> (('b, 'a, 'd, 'e, 'f, 'c, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'g) off * string) > type ('c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t2 = (z, u, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ((z, u, 'e, 'f, 'c, 'd, 'i, 'j, 'k, 'l, 'm, 'n, 'g, 'h) off * string) > type ('g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t4 = (z, u, z, u, u, u, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ((z, u, z, u, u, u, 'k, 'l, 'm, 'n, 'g, 'h, 'i, 'j) off * string) > type t8 = (z, u, z, u, u, u, z, u, u, u, u, u, u, u) off -> ((z, u, z, u, u, u, z, u, u, u, u, u, u, u) off * string) > (* Take 1, 2, 4 or 8 byte with alignment restriction *) > val take1 : string -> ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t1 > val take2 : string -> ('c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t2 > val take4 : string -> ('g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t4 > val take8 : string -> t8 > (* Bind all take functions to a string for easier use *) > val bind : string -> (('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t1 * ('c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t2 * ('g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t4 * t8) > end = struct > (* Types for aligned unaligned tracking *) > type z > type u > (* The type of an offset into a stream *) > type ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off = int > (* Start of the stream *) > let zero = 0 > (* Coercion to integer, same as (x :> int) *) > let get x = x > (* Advance the position by 1, 2, 4 or 8 *) > let succ x = x + 1 > let succ2 x = succ (succ x) > let succ4 x = succ2 (succ2 x) > let succ8 x = succ4 (succ4 x) > (* Aliases for shorter type names *) > type ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t1 = ('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> (('b, 'a, 'd, 'e, 'f, 'c, 'h, 'i, 'j, 'k, 'l, 'm, 'n, 'g) off * string) > type ('c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t2 = (z, u, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ((z, u, 'e, 'f, 'c, 'd, 'i, 'j, 'k, 'l, 'm, 'n, 'g, 'h) off * string) > type ('g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) t4 = (z, u, z, u, u, u, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n) off -> ((z, u, z, u, u, u, 'k, 'l, 'm, 'n, 'g, 'h, 'i, 'j) off * string) > type t8 = (z, u, z, u, u, u, z, u, u, u, u, u, u, u) off -> ((z, u, z, u, u, u, z, u, u, u, u, u, u, u) off * string) > (* Take 1, 2, 4 or 8 byte with alignment restriction *) > let take1 = fun s x -> (succ x, String.sub s (get x) 1);; > let take2 = fun s x -> (succ2 x, String.sub s (get x) 2);; > let take4 = fun s x -> (succ4 x, String.sub s (get x) 4);; > let take8 = fun s x -> (succ8 x, String.sub s (get x) 8);; > (* Bind all take functions to a string for easier use *) > let bind s = (take1 s, take2 s, take4 s, take8 s) > end > open M > > (* Test string *) > let s = "aabbccccdddddddd" > let (t1, t2, t4, t8) = bind s > > (* extract things from string *) > let foo () = > let (off, a) = t1 zero in > let (off, b) = t1 off in > let (off, c) = t2 off in > let (off, d) = t4 off in > let (off, e) = t8 off in > Printf.printf "%s %s %s %s %s\n" a b c d e > > (* using take2/4/8 with an offset that isn't aligned gives a compile > time type error: > let bad () = > let (off, a) = take1 s zero in > take8 s off > *) > > -- > Caml-list mailing list. Subscription management and archives: > https://sympa-roc.inria.fr/wws/info/caml-list > Beginner's list: http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] Wanted: GADT examples: string length, counting module x 2012-03-19 14:43 ` Gabriel Scherer @ 2012-03-19 16:13 ` Jesper Louis Andersen 2012-03-19 16:22 ` Raoul Duke 0 siblings, 1 reply; 8+ messages in thread From: Jesper Louis Andersen @ 2012-03-19 16:13 UTC (permalink / raw) To: Gabriel Scherer; +Cc: Goswin von Brederlow, caml-list On Mon, Mar 19, 2012 at 15:43, Gabriel Scherer <gabriel.scherer@gmail.com> wrote: > I suspect you're seeing too much into the GADT as they're being added > in OCaml. Your examples are not basically about GADTs, but about > dependent types: you want to encode values (and operations on them) at > the type level to get more expressivity. This is a well-known and > extremely powerful trend in programming languages design, but it also > results in complex systems with sophisticated rules. I like the view of Conor McBride: the types are Priests overseeing the terms which are people. In a language like OCaml the priests and people and not allowed to mingle with each other. What is in the church of types, stays in the church of types. This means - and I believe this is what the remainder of Gabriels post is about - that the priests need to encode the people or that you need to represent the terms as a mirroring in the types. Real dependent types allow the priests and people to intermingle in a specific way: terms can be part of types. Or more succinctly: Types can be indexed by terms. This construction allow you to take a term from the language, say n : nat, and then talk about the type 'vector n', i.e., the vector (list) of length n. If you want to play with dependent types, there are two ways which seem popular at the moment: Agda or Coq. Agda is the more experimental and programming-language-like path, whereas Coq is a full proof assistant with automation support. But others live their life in these tools much more than I, and they would be more knowledgable in what to use. -- J. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] Wanted: GADT examples: string length, counting module x 2012-03-19 16:13 ` Jesper Louis Andersen @ 2012-03-19 16:22 ` Raoul Duke 2012-03-21 9:48 ` Goswin von Brederlow 0 siblings, 1 reply; 8+ messages in thread From: Raoul Duke @ 2012-03-19 16:22 UTC (permalink / raw) To: caml-list On Mon, Mar 19, 2012 at 9:13 AM, Jesper Louis Andersen > If you want to play with dependent types, there are two ways which > seem popular at the moment: Agda or Coq. and some not popular ones... http://www.ats-lang.org/ http://sandycat.info/blog/deptypes-shen/ et. al. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] Wanted: GADT examples: string length, counting module x 2012-03-19 16:22 ` Raoul Duke @ 2012-03-21 9:48 ` Goswin von Brederlow 0 siblings, 0 replies; 8+ messages in thread From: Goswin von Brederlow @ 2012-03-21 9:48 UTC (permalink / raw) To: Raoul Duke; +Cc: caml-list Raoul Duke <raould@gmail.com> writes: > On Mon, Mar 19, 2012 at 9:13 AM, Jesper Louis Andersen >> If you want to play with dependent types, there are two ways which >> seem popular at the moment: Agda or Coq. > > and some not popular ones... > > http://www.ats-lang.org/ > > http://sandycat.info/blog/deptypes-shen/ > > et. al. Neigther of which are examples for GADT in ocaml. MfG Goswin ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2012-03-22 16:58 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2012-03-22 6:01 [Caml-list] Wanted: GADT examples: string length, counting module x oleg 2012-03-22 9:04 ` Gabriel Scherer 2012-03-22 16:58 ` [Caml-list] " Goswin von Brederlow -- strict thread matches above, loose matches on Subject: below -- 2012-03-12 11:30 [Caml-list] " Goswin von Brederlow 2012-03-19 14:43 ` Gabriel Scherer 2012-03-19 16:13 ` Jesper Louis Andersen 2012-03-19 16:22 ` Raoul Duke 2012-03-21 9:48 ` Goswin von Brederlow
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox