caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] opaque polymorphism
@ 2001-09-07 18:35 Charles Martin
  2001-09-10  7:02 ` Francois Pottier
  0 siblings, 1 reply; 9+ messages in thread
From: Charles Martin @ 2001-09-07 18:35 UTC (permalink / raw)
  To: caml-list

A feature that would be nice would be to hide the polymorphism of a type in a
module signature:

foo.ml:
type ('a, 'b, 'c) t = { ... }

foo.mli:
type ('a, 'c) t

Thus, clients of Foo would be unaware of the polymorphism in 'b.  This would
require that type variables in signatures and structures be matched on their
names, obviously.

Can any of the type experts out there tell me if this is possible?


__________________________________________________
Do You Yahoo!?
Get email alerts & NEW webcam video instant messaging with Yahoo! Messenger
http://im.yahoo.com
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] opaque polymorphism
  2001-09-07 18:35 [Caml-list] opaque polymorphism Charles Martin
@ 2001-09-10  7:02 ` Francois Pottier
  2001-09-10 23:19   ` Phantom types (very long) (Was Re: [Caml-list] opaque polymorphism) Brian Rogoff
  0 siblings, 1 reply; 9+ messages in thread
From: Francois Pottier @ 2001-09-10  7:02 UTC (permalink / raw)
  To: Charles Martin; +Cc: caml-list


Hello Charles,

You can write:

  foo.ml:
  type ('a, 'b, 'c) internal_t = { ... }

  type ('a, 'c) t = ('a, <some type>, 'c) internal_t

 
  foo.mli:
  type ('a, 'c) t

So ``hiding the polymorphism in 'b'' can be done, but you have to tell
the compiler which internal type should be used instead of 'b, i.e. you
have to choose <some type>. I guess the unit type will do in most
situations.

Hope this helps,

-- 
François Pottier
Francois.Pottier@inria.fr
http://pauillac.inria.fr/~fpottier/
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Phantom types (very long) (Was Re: [Caml-list] opaque polymorphism)
  2001-09-10  7:02 ` Francois Pottier
@ 2001-09-10 23:19   ` Brian Rogoff
  2001-09-11  9:44     ` Andreas Rossberg
  2001-09-11 18:38     ` [Caml-list] Re: Phantom types (very long) j h woodyatt
  0 siblings, 2 replies; 9+ messages in thread
From: Brian Rogoff @ 2001-09-10 23:19 UTC (permalink / raw)
  To: caml-list; +Cc: Francois Pottier, Charles Martin

On Mon, 10 Sep 2001, Francois Pottier wrote:
> So ``hiding the polymorphism in 'b'' can be done, but you have to tell
> the compiler which internal type should be used instead of 'b, i.e. you
> have to choose <some type>. I guess the unit type will do in most
> situations.

There's a sort of "dual" trick of parameterizing a type over a type
variable which is not used in the definition, the so-called phantom types.
This trick seems to be well known by ML experts, but its not mentioned in
any ML programming texts, sort of like the parameterization trick for
recursive types and modules. I'll show three examples of phantom types
below; if you already know this trick you can skip my very long, rambling
message.

The easiest example to start with is the core of an untyped lambda calculus
interpreter.

type term =
    Int of int
  | Bool of bool
  | Add of term * term
  | App of term * term
  | Lam of (term -> term);;

As we know, since this is untyped we can have self applications.

# let bug = Lam(fun x -> App(x,x));;
val bug : term = Lam <fun>

We'd like to forbid this, and one way is to implement a type checker.
Here's how we can piggyback off of the OCaml one with phantom types.

type 'a expr = Expr of term;; (* The phantom type *)
let int : int -> int expr = fun i -> Expr (Int i);;
let bool : bool -> bool expr = fun b -> Expr (Bool b);;

let add : int expr -> int expr -> int expr =
  fun (Expr e1) (Expr e2) -> Expr (Add(e1,e2));;

let app : ('a -> 'b) expr -> ('a expr -> 'b expr) =
  fun (Expr e1) (Expr e2) -> Expr (App(e1,e2));;

let lam : ('a expr -> 'b expr) -> ('a -> 'b) expr =
  fun f -> Expr (Lam(fun x -> let (Expr b) = f (Expr x) in b));;

Now we use these functions instead of the raw constructors. So our buggy
term becomes

# let bug = lam (fun x -> app x x);;
This expression has type 'a expr but is here used with type ('a -> 'b)
expr

Cool! Note that we have to use explicit types or use the module system
here (.mli and .ml files) otherwise we have (assuming I omit the type
declarations in int, bool, add, app, lam)

# let bug = lam (fun x -> app x x);;
val bug : '_a expr = Expr (Lam <fun>)

The next example is based on a mail to this very Caml list

	http://caml.inria.fr/archives/199912/msg00090.html

One of the points of that example was to put the permissions (readable or
writable) of an entity into it's type. Rather than just copy Vouillon's
excellent approach with row types, I'll be infinitesimally creative and
show a version with polymorphic variants. here's the .mli

(* Vouillon's phantom type, which originally used row types but works
   just fine with polymorphic variants. The idea is to make the permission
   check only once, when the mapped file is created, and thereafter use
   the type system to enforce read/write safety.
*)

type 'a perms
type 'a t

val read_only  : [ `Readable ] perms
val write_only : [ `Writable ] perms
val read_write : [> `Readable | `Writable ] perms

val map_file : string -> 'a perms -> int -> 'a t
val get : [> `Readable ] t -> int -> char
val set : [> `Writable ] t -> int -> char -> unit

(* Vouillon's original interface with row types
val read_only : <read:unit> perms
val write_only : <write:unit> perms
val read_write : <read:unit;write:unit> perms

val map_file : string -> 'a perms -> int -> 'a t
val get : <read:unit;..> t -> int -> char
val set : <write:unit;..> t -> int -> char -> unit
*)

and here's a sample .ml, which type checks but might now work ;-).

open Unix
open Bigarray

type bytes = (int, int8_unsigned_elt, c_layout) Bigarray.Array1.t
type 'a perms = int
type 'a t = bytes

let read_only = 1
let write_only = 2
let read_write = 3

let openflags_of_perms n =
  if      n = 1 then O_RDONLY, 0o400
  else if n = 2 then O_WRONLY, 0o200
  else if n = 3 then O_RDWR, 0o600
  else invalid_arg "openflags_of_perms"

let access_of_openflags = function
    O_RDONLY -> [R_OK; F_OK]
  | O_WRONLY -> [W_OK; F_OK]
  | O_RDWR   -> [R_OK; W_OK; F_OK]
  | _ -> invalid_arg "access_of_openflags"

(* Check that "fd" permissions and "perms" matches *)

let map_file filename perms sz =
  let (oflags, fperm) = openflags_of_perms perms in
  try
    access filename (access_of_openflags oflags); (* Check... *)
    let fd = Unix.openfile filename [oflags; O_CREAT] fperm in
    Array1.map_file fd int8_unsigned c_layout false sz
  with _ ->
    invalid_arg "map_file: not even a valid permission!"

let get a i = Char.chr (Array1.get a i)
let set a i c = Array1.set a i (Char.code c)

The final example is familiar to anyone who reads comp.lang.ml, where I
mistakenly asserted that you couldn't have statically typed array
dimensions in ML like you can in C++ or Ada. Matthias Blume then posted
a solution which works (though it reminds me a bit of that proverb of the
dancing bear). I've included a translation of the code into OCaml, with
most of Blume's original comments.

(* It is, surprisingly to some perhaps, actually possible to statically
type
   arrays with (statically) fixed bounds in ML (just like in C).

   Here is the trick:

   We build an infinite family of types together with value constructors
   such that there is a 1-1 correspondence between non-negative integers
   and types that have constructable values in this family.  We can use
   the types in the family as "witness types" (aka "phantom types") that
   statically track array dimensions:

   In Ocaml:
*)
module DimArray : sig
  type dec
  type 'a d0 and 'a d1 and 'a d2 and 'a d3 and 'a d4
  type 'a d5 and 'a d6 and 'a d7 and 'a d8 and 'a d9
  type zero and nonzero
  type ('a, 'z) dim0
  type 'a dim = ('a, nonzero) dim0

  val dec :            ((dec, zero) dim0 -> 'b) -> 'b

  val d0 : 'a dim        -> ('a d0 dim -> 'b) -> 'b
  val d1 : ('a, 'z) dim0 -> ('a d1 dim -> 'b) -> 'b
  val d2 : ('a, 'z) dim0 -> ('a d2 dim -> 'b) -> 'b
  val d3 : ('a, 'z) dim0 -> ('a d3 dim -> 'b) -> 'b
  val d4 : ('a, 'z) dim0 -> ('a d4 dim -> 'b) -> 'b
  val d5 : ('a, 'z) dim0 -> ('a d5 dim -> 'b) -> 'b
  val d6 : ('a, 'z) dim0 -> ('a d6 dim -> 'b) -> 'b
  val d7 : ('a, 'z) dim0 -> ('a d7 dim -> 'b) -> 'b
  val d8 : ('a, 'z) dim0 -> ('a d8 dim -> 'b) -> 'b
  val d9 : ('a, 'z) dim0 -> ('a d9 dim -> 'b) -> 'b

  val dim : ('a, 'z) dim0 -> ('a, 'z) dim0

  val to_int : ('a, 'z) dim0 -> int

    (* arrays with static dimensions *)
  type ('t, 'd) dim_array

  val make : 'd dim -> 't -> ('t, 'd) dim_array
  val init : 'd dim -> (int -> 'a) -> ('a, 'd) dim_array
  val copy : ('a, 'd) dim_array -> ('a, 'd) dim_array
  (* other array operations go here ... *)
  val get : ('a, 'd) dim_array -> int -> 'a
  val set : ('a, 'd) dim_array -> int -> 'a -> unit
  val combine :
      ('a, 'd) dim_array -> ('b, 'd) dim_array -> ('a -> 'b -> 'c) ->
        ('c, 'd) dim_array
  val length : ('a, 'd) dim_array -> int
  val update : ('a, 'd) dim_array -> int -> 'a -> ('a, 'd) dim_array
  val iter : f:('a -> unit) -> ('a, 'd) dim_array -> unit
  val map : f:('a -> 'b) -> ('a, 'd) dim_array -> ('b, 'd) dim_array
  val iteri : f:(int -> 'a -> unit) -> ('a, 'd) dim_array -> unit
  val mapi : f:(int -> 'a -> 'b) -> ('a, 'd) dim_array ->
     ('b, 'd) dim_array
  val fold_left : f:('a -> 'b -> 'a) -> init:'a -> ('b,'d) dim_array -> 'a
  val fold_right : f:('b -> 'a -> 'a) -> ('b, 'd) dim_array -> init:'a ->
'a

  val iter2 :
      f:('a -> 'b -> unit) -> ('a,'d) dim_array -> ('b, 'd) dim_array ->
unit
  val map2 :
      f:('a -> 'b -> 'c) -> ('a, 'd) dim_array -> ('b, 'd) dim_array ->
('c, 'd) dim_array
  val iteri2 :
      f:(int -> 'a -> 'b -> unit) -> ('a,'d) dim_array -> ('b, 'd)
dim_array ->
	unit
  val mapi2 :
      f:(int -> 'a -> 'b -> 'c) -> ('a, 'd) dim_array -> ('b, 'd)
dim_array ->
	('c, 'd) dim_array
  val fold_left2 :
      f:('a -> 'b -> 'c -> 'a) -> init:'a -> ('b, 'd) dim_array -> ('c,
'd) dim_array -> 'a
  val fold_right2 :
      f:('a -> 'b -> 'c -> 'c) -> ('a, 'd) dim_array -> ('b, 'd) dim_array
-> init:'c -> 'c
  val to_array : ('a, 'd) dim_array -> 'a array
end = struct
  type dec = unit
  type 'a d0 = unit
  type 'a d1 = unit
  type 'a d2 = unit
  type 'a d3 = unit
  type 'a d4 = unit
  type 'a d5 = unit
  type 'a d6 = unit
  type 'a d7 = unit
  type 'a d8 = unit
  type 'a d9 = unit
  type zero = unit
  type nonzero = unit

  type ('a, 'z) dim0 = int (* Phantom type *)
  type 'a dim = ('a, nonzero) dim0

  let dec k = k 0

  let d0 d k = k (10 * d + 0)
  let d1 d k = k (10 * d + 1)
  let d2 d k = k (10 * d + 2)
  let d3 d k = k (10 * d + 3)
  let d4 d k = k (10 * d + 4)
  let d5 d k = k (10 * d + 5)
  let d6 d k = k (10 * d + 6)
  let d7 d k = k (10 * d + 7)
  let d8 d k = k (10 * d + 8)
  let d9 d k = k (10 * d + 9)

  let dim d = d

  let to_int d = d

  type ('t, 'd) dim_array = 't array

  let make d x = Array.make (to_int d) x
  let init d f = Array.init (to_int d) f
  let copy x = Array.copy  x
  (* other array operations go here ... *)
  let get : ('a, 'd) dim_array -> int -> 'a = fun a d ->
    Array.get a d

  let set : ('a, 'd) dim_array -> int -> 'a -> unit = fun a d v ->
    Array.set a d v

  let unsafe_get : ('a, 'd) dim_array -> int -> 'a = fun a d ->
    Array.unsafe_get a d

  let unsafe_set : ('a, 'd) dim_array -> int -> 'a -> unit = fun a d v ->
    Array.unsafe_set a d v

  let combine :
      ('a, 'd) dim_array -> ('b, 'd) dim_array -> ('a -> 'b -> 'c) -> ('c,
'd) dim_array =
	fun a b f ->
	  Array.init (Array.length a) (fun i -> f a.(i) b.(i))

  let length : ('a, 'd) dim_array -> int = fun a -> Array.length a

  let update : ('a, 'd) dim_array -> int -> 'a -> ('a, 'd) dim_array =
    fun a d v -> let result = Array.copy a in (Array.set result d v;
result)

  let iter f a = Array.iter f a
  let map f a = Array.map f a
  let iteri f a = Array.iteri f a
  let mapi f a = Array.mapi f a
  let fold_left f x a = Array.fold_left f x a
  let fold_right f a x = Array.fold_right f a x

  let rec iter2 f a1 a2 =
    for i = 0 to length a1 - 1 do
      f (unsafe_get a1 i) (unsafe_get a2 i)
    done

  let rec map2 f a1 a2 =
    let l = length a1 in
    if l = 0 then [||] else
    (let r = Array.make l (f (unsafe_get a1 0) (unsafe_get a2 0)) in
     for i = 1 to l - 1 do
       unsafe_set r i (f (unsafe_get a1 i) (unsafe_get a2 i))
     done;
     r)

  let rec iteri2 f a1 a2 =
    for i = 0 to length a1 - 1 do
      f i (unsafe_get a1 i) (unsafe_get a2 i)
    done

  let mapi2 f a1 a2 =
    let l = length a1 in
    if l = 0 then [||] else
    (let r = Array.make l (f 0 (unsafe_get a1 0) (unsafe_get a2 0)) in
     for i = 1 to l - 1 do
       unsafe_set r i (f i (unsafe_get a1 i) (unsafe_get a2 i))
     done;
     r)

  let fold_left2 f accu a1 a2 =
    let r = ref accu in
    for i = 0 to length a1 - 1 do
      r := f !r (unsafe_get a1 i) (unsafe_get a2 i)
    done;
    !r

  let fold_right2 f a1 a2 accu =
    let r = ref accu in
    for i = length a1 - 1 downto 0 do
      r := f (unsafe_get a1 i) (unsafe_get a2 i) !r
    done;
    !r

  let to_array : ('a, 'd) dim_array -> 'a array = fun d -> d

end;;

(*
Once all this is in place, you can say things like this

 ...

 open DimArray;;
 let  d1230 = dec d1 d2 d3 d0 dim;;
 let  a = make d1230 0.0;;
 ...

 open DimArray;;
 let  d12 = dec d1 d2 dim;;
 let  a = make d12 "bullshi";;
 let  b = make d12 't';;
 let  f s c = s ^ (Char.escaped c);;
 let  abf = combine a b f;;
 ...

The type of d1230 is "dec d1 d2 d3 d0 dim" (work it out!).  By
careful construction, this even happens to be textually the same as
the expression that created d1230.  Moreover, the value behind d1230
is 1230.  In general, you get the dim value for any number by writing
the number in decimal, adding "d" to each digit, adding a few spaces,
and surrounding the thing with "dec ... dim".

With this, you can now declare a variable to be an array of some precisely
specified length:

   let a : (int, dec d1 d2 d3 d0) arr = ...

The type checker will statically reject any attempt of making a an array
of
different size.  This is just like in C.

Now, beyond C, you can write functions that require twe equal-sized arrays
(but which make no other demands):

  val dot_product : (real, 'd) arr * (real, 'd) arr -> real

etc. It's easy to extend this to multidimensional arrays, where we can
define a
more type safe product

 val ( * ) :
   ('a, 'rows, 'cols) dim_array2 -> ('b, 'cols, 'rows) dim_array2 -> ('a
-> 'b -> 'c) ->
     ('c, 'rows, 'cols) dim_array2

You might argue that the above construction is ugly and lengthy, but it
can
be packaged up and put into a library.  This is what I have done for my
C interface, for example.  Usage is exceedingly easy because thanks to
ML's
type inference you rarely have to actually write these types.

Notice also that the above construction is slightly more complicated than
it
needs to be:  I wrote the value constructors in CPS to get that cute
type/expression equivalence.  The set of constructors would have less
complicated types if we were content to write

   let d1230 = d0 (d3 (d2 (d1 dec)))

instead of the much cooler (:-)

   let d1230 = dec d1 d2 d3 dim
*)

-- Brian


-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Phantom types (very long) (Was Re: [Caml-list] opaque polymorphism)
  2001-09-10 23:19   ` Phantom types (very long) (Was Re: [Caml-list] opaque polymorphism) Brian Rogoff
@ 2001-09-11  9:44     ` Andreas Rossberg
  2001-09-11 18:38     ` [Caml-list] Re: Phantom types (very long) j h woodyatt
  1 sibling, 0 replies; 9+ messages in thread
From: Andreas Rossberg @ 2001-09-11  9:44 UTC (permalink / raw)
  To: caml-list

Brian Rogoff wrote:
> 
> The final example is familiar to anyone who reads comp.lang.ml, where I
> mistakenly asserted that you couldn't have statically typed array
> dimensions in ML like you can in C++ or Ada. Matthias Blume then posted
> a solution which works (though it reminds me a bit of that proverb of the
> dancing bear).

Actually, Matthias gave a very interesting talk on the Babel workshop in
Florence last Saturday where he showed how to encode the complete C type
system in ML (including functions, pointers, constness, bitfields, and
all dark corners - the only bit still missing is varargs), using even
more phantom type trickery. Unfortunately, the paper is not yet
available online, but as his work is part of the new FFI of SML/NJ you
can read about the encoding in its documentation (inside the
ml-nlffi-lib.tgz of the latest working version).

Cheers,

	- Andreas

-- 
Andreas Rossberg, rossberg@ps.uni-sb.de

"Computer games don't affect kids; I mean if Pac Man affected us
 as kids, we would all be running around in darkened rooms, munching
 magic pills, and listening to repetitive electronic music."
 - Kristian Wilson, Nintendo Inc.
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Caml-list] Re: Phantom types (very long)
  2001-09-10 23:19   ` Phantom types (very long) (Was Re: [Caml-list] opaque polymorphism) Brian Rogoff
  2001-09-11  9:44     ` Andreas Rossberg
@ 2001-09-11 18:38     ` j h woodyatt
  2001-09-11 19:16       ` Brian Rogoff
                         ` (2 more replies)
  1 sibling, 3 replies; 9+ messages in thread
From: j h woodyatt @ 2001-09-11 18:38 UTC (permalink / raw)
  To: The Caml Trade

So okay... I take it back.  Caml *does* have invariants.  (I'm 
learning.  Slowly.  But I'm learning.)

This "phantom types" design pattern is one I have never seen before.  It 
doesn't seem to be used in the standard library anywhere I can see.  It 
looks like it might be useful in presenting a safer network programming 
interface than the low-level wrappers around BSD sockets (which I've 
never liked).

Are there any other mind-blowingly elegant design patterns lurking in 
the corners of the Caml type inference engine that I should know about?


--
j h woodyatt <jhw@wetware.com>
"You're standing on sacred ground.  Many strange and wonderful
things have transpired right where you're standing."
                                               --unattributable

-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Re: Phantom types (very long)
  2001-09-11 18:38     ` [Caml-list] Re: Phantom types (very long) j h woodyatt
@ 2001-09-11 19:16       ` Brian Rogoff
  2001-09-12  9:33       ` Daan Leijen
  2001-09-14  8:49       ` Jacques Garrigue
  2 siblings, 0 replies; 9+ messages in thread
From: Brian Rogoff @ 2001-09-11 19:16 UTC (permalink / raw)
  To: jhw; +Cc: The Caml Trade

On Tue, 11 Sep 2001, j h woodyatt wrote:
> So okay... I take it back.  Caml *does* have invariants.  (I'm
> learning.  Slowly.  But I'm learning.)
>
> This "phantom types" design pattern is one I have never seen before.

I remember reading J. Vouillon's post a long time ago and thinking "Man,
that's weird, having that type parameter not show up on the right hand
side of the type definition!" But it wasn't until Blume's post on the ML
ng that I got a name for the concept "phantom" and "witness" types. Do a
Google search on "phantom type" and you'll turn up a few Haskell paper's,
most notably tsome ones on connecting Haskell to Microsoft COM, and some
on domain specific languages.

> It doesn't seem to be used in the standard library anywhere I can see.  It
> looks like it might be useful in presenting a safer network programming
> interface than the low-level wrappers around BSD sockets (which I've
> never liked).

It's always a good idea to have thin (low level bindings) available. You
can build your abstractions on top of them, but to do otherwise is an
abstraction inversion.

> Are there any other mind-blowingly elegant design patterns lurking in
> the corners of the Caml type inference engine that I should know about?

Yes! And there there will be even more when we get polymorphic recursion
and generics ;-)

-- Brian

PS: Yeah, I'll post some more soon assuming I don't get blown up. Many
tricks involve simulating dependent types so if you look up dependent
types that'll get you started. In particular try to understand Danvy's
"Functional Unparsing"" paper.



-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Re: Phantom types (very long)
  2001-09-11 18:38     ` [Caml-list] Re: Phantom types (very long) j h woodyatt
  2001-09-11 19:16       ` Brian Rogoff
@ 2001-09-12  9:33       ` Daan Leijen
  2001-09-14  8:49       ` Jacques Garrigue
  2 siblings, 0 replies; 9+ messages in thread
From: Daan Leijen @ 2001-09-12  9:33 UTC (permalink / raw)
  To: jhw, The Caml Trade

On Tue, 11 Sep 2001, j h woodyatt wrote:
> This "phantom types" design pattern is one I have never seen before.  It
> doesn't seem to be used in the standard library anywhere I can see.  It
> looks like it might be useful in presenting a safer network programming
> interface than the low-level wrappers around BSD sockets (which I've
> never liked).

We used phantom types extensively  for exactly this purpose -- a typed
wrapper around a low-level interface. It seems that the trick has been in
use
already in the 80's for ML libraries that accessed sockets (allthough it
didn't
had a sexy name in that time). We tried to describe the general use pattern
in detail in the paper "domain specific embedded compilers" which can be
found at:

http://www.cs.uu.nl/~daan/pubs.html

We also used phantom types to encode single type inheritance for the
H/direct frame work (described in "calling hell from heaven and heaven from
hell")

All the best,
    Daan Leijen.


-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Re: Phantom types (very long)
  2001-09-11 18:38     ` [Caml-list] Re: Phantom types (very long) j h woodyatt
  2001-09-11 19:16       ` Brian Rogoff
  2001-09-12  9:33       ` Daan Leijen
@ 2001-09-14  8:49       ` Jacques Garrigue
  2001-09-14 19:10         ` Brian Rogoff
  2 siblings, 1 reply; 9+ messages in thread
From: Jacques Garrigue @ 2001-09-14  8:49 UTC (permalink / raw)
  To: jhw; +Cc: caml-list

> This "phantom types" design pattern is one I have never seen before.  It 
> doesn't seem to be used in the standard library anywhere I can see.  It 
> looks like it might be useful in presenting a safer network programming 
> interface than the low-level wrappers around BSD sockets (which I've 
> never liked).

Thay are used in both Bigarray and Labltk (the 'a widget type). In
fact they were already used in Labltk before the word phantom started
to be used for them.

> Are there any other mind-blowingly elegant design patterns lurking in 
> the corners of the Caml type inference engine that I should know about?

I don't know if this is mind blowing, but thanks to variance
annotations on abstract types, you can also have subtyping on
"phantom" types. This is used in lablgtk for instance:

type (-'a) gtkobj
type widget = [`widget]
type container = [`widget|`container]
type box = [`widget|`container|`box]

now you can cast safely between classes:
    (mybox : box gtkobj :> container gtkobj)

Note that the parameter must be contravariant if you use polymorphic
variant types as indexes, or covariant if you rather use object types.

This also works ok with polymorphism, encoding inheritance (even
multiple) in a direct way.
val add : [> `container] gtkobj -> [> `widget] gtkobj -> unit

Cheers,

        Jacques Garrigue
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Re: Phantom types (very long)
  2001-09-14  8:49       ` Jacques Garrigue
@ 2001-09-14 19:10         ` Brian Rogoff
  0 siblings, 0 replies; 9+ messages in thread
From: Brian Rogoff @ 2001-09-14 19:10 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: caml-list

On Fri, 14 Sep 2001, Jacques Garrigue wrote:
> > This "phantom types" design pattern is one I have never seen before.  It
> > doesn't seem to be used in the standard library anywhere I can see.  It
> > looks like it might be useful in presenting a safer network programming
> > interface than the low-level wrappers around BSD sockets (which I've
> > never liked).
>
> Thay are used in both Bigarray and Labltk (the 'a widget type). In
> fact they were already used in Labltk before the word phantom started
> to be used for them.

Right, and I think that each new use causes some of the newer ML (and
Haskell, Mercury too?) programmers to do a few double takes. I know the
first time I saw this usage on this mailing list I was a bit boggled.
In retrospect each new usage seems relatively obvious, but it wouldn't
hurt to have this (and the parameterization trick, and the trick that
Tyng-Ruey Chuang uses to write polynomial data types, and a whole bunch of
others ...) explained simply for the practitioner.

> > Are there any other mind-blowingly elegant design patterns lurking in
> > the corners of the Caml type inference engine that I should know about?
>
> I don't know if this is mind blowing, but thanks to variance
> annotations on abstract types, you can also have subtyping on
> "phantom" types. This is used in lablgtk for instance:
>
> type (-'a) gtkobj
> type widget = [`widget]
> type container = [`widget|`container]
> type box = [`widget|`container|`box]
>
> now you can cast safely between classes:
>     (mybox : box gtkobj :> container gtkobj)
>
> Note that the parameter must be contravariant if you use polymorphic
> variant types as indexes, or covariant if you rather use object types.
>
> This also works ok with polymorphism, encoding inheritance (even
> multiple) in a direct way.
> val add : [> `container] gtkobj -> [> `widget] gtkobj -> unit

What a great mailing list! Thanks for that one. I hope to see it in the
lablgtk docs soon.

-- Brian


-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2001-09-14 19:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-09-07 18:35 [Caml-list] opaque polymorphism Charles Martin
2001-09-10  7:02 ` Francois Pottier
2001-09-10 23:19   ` Phantom types (very long) (Was Re: [Caml-list] opaque polymorphism) Brian Rogoff
2001-09-11  9:44     ` Andreas Rossberg
2001-09-11 18:38     ` [Caml-list] Re: Phantom types (very long) j h woodyatt
2001-09-11 19:16       ` Brian Rogoff
2001-09-12  9:33       ` Daan Leijen
2001-09-14  8:49       ` Jacques Garrigue
2001-09-14 19:10         ` Brian Rogoff

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).