caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Phantom types: transparency vs rogue unification
@ 2008-06-25 14:26 Dario Teixeira
  2008-06-25 15:02 ` [Caml-list] " Jacques Carette
  0 siblings, 1 reply; 5+ messages in thread
From: Dario Teixeira @ 2008-06-25 14:26 UTC (permalink / raw)
  To: caml-list

Hi,

I've been experimenting with phantom types since I've read Richard Jones'
excellent intro to the subject [1].  I've now come across a tricky problem,
however.  Essentially, I need to reconcile two seemingly incompatible goals:
making a module's phantom type opaque to avoid the "rogue unification" issue,
and making it transparent so that external modules can produce values of
that type.

So, suppose I have a "Stringies" module, which uses a phantom string to
parametrise floats.  Because the phantom type is opaque, the rogue unification
is prevented in the last line:

_______________________________________________________________
module Stringies:
sig
        type 'a t
        val make: float -> string t
        val add: string t -> string t -> string t
end =
struct
        type 'a t = float
        let make x = x
        let add a b = a +. b
end

open Stringies
let foo = add (make 1.0) (make 2.0);;
let bar = add (make 1.0) 2.0;;  (* Error! *)
_______________________________________________________________


But suppose you wish to create an external module with a function to parse
strings into Stringies.t.  The phantom must become visible, but that allows
for rogue unification to take place:

_______________________________________________________________
module Stringies:
sig
        type 'a t = float
        val make: float -> string t
        val add: string t -> string t -> string t
end =
struct
        type 'a t = float
        let make x = x
        let add a b = a +. b
end


module Stringies_IO:
sig
        val parse: string -> string Stringies.t
end =
struct
        let parse = float_of_string
end


open Stringies
let foo = add (make 1.0) (Stringies_IO.parse "2.0");;
let bar = add (make 1.0) 2.0;;  (* Rogue unification! *)
_______________________________________________________________


How would you go about solving this problem?

Thanks in advance!
cheers,
Dario Teixeira

[1] http://camltastic.blogspot.com/2008/05/phantom-types.html



      __________________________________________________________
Sent from Yahoo! Mail.
A Smarter Email http://uk.docs.yahoo.com/nowyoucan.html


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

* Re: [Caml-list] Phantom types: transparency vs rogue unification
  2008-06-25 14:26 Phantom types: transparency vs rogue unification Dario Teixeira
@ 2008-06-25 15:02 ` Jacques Carette
  2008-06-25 16:29   ` Dario Teixeira
  0 siblings, 1 reply; 5+ messages in thread
From: Jacques Carette @ 2008-06-25 15:02 UTC (permalink / raw)
  To: Dario Teixeira; +Cc: caml-list

How about leaving your type opaque but changing your implementation of 
parse to
        let parse s = Stringies.make (float_of_string s)
?
In my tests, this works as you intended.

Note that I would personally implement Stringies_IO a Functor over 
Stringies rather than having explicit dependencies.

Jacques


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

* Re: [Caml-list] Phantom types: transparency vs rogue unification
  2008-06-25 15:02 ` [Caml-list] " Jacques Carette
@ 2008-06-25 16:29   ` Dario Teixeira
  2008-06-26 21:41     ` Dario Teixeira
  0 siblings, 1 reply; 5+ messages in thread
From: Dario Teixeira @ 2008-06-25 16:29 UTC (permalink / raw)
  To: Jacques Carette; +Cc: caml-list

> Note that I would personally implement Stringies_IO a
> Functor over 
> Stringies rather than having explicit dependencies.
> 

Hi,

Indeed!  In the meantime I've also thought of a functor-based solution,
which does the job even though it looks rather contrived:

_________________________________________________________
module type PARSER =
sig
	val parse: string -> float
end

module type STRINGIES =
sig
	type 'a t
	val make: float -> string t
	val add: string t -> string t -> string t
	val parse: string -> string t
end

module Stringies (Parser : PARSER): STRINGIES =
struct
	type 'a t = float
	let make x = x
	let add a b = a +. b
	let parse = Parser.parse
end

module Myparser : PARSER =
struct
	let parse = float_of_string
end

module Mystringies = Stringies (Myparser)

open Mystringies

let ola = add (make 1.0) (make 2.0);;
let ola = add (make 1.0) 2.0;; (* Error *)
_________________________________________________________


Thanks for your time!
Cheers,
Dario Teixeira




      __________________________________________________________
Sent from Yahoo! Mail.
A Smarter Email http://uk.docs.yahoo.com/nowyoucan.html


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

* Re: [Caml-list] Phantom types: transparency vs rogue unification
  2008-06-25 16:29   ` Dario Teixeira
@ 2008-06-26 21:41     ` Dario Teixeira
  2008-06-27 20:35       ` Dario Teixeira
  0 siblings, 1 reply; 5+ messages in thread
From: Dario Teixeira @ 2008-06-26 21:41 UTC (permalink / raw)
  To: caml-list

Hi,

I may have spoken too soon about the functor solving the problem.  In fact,
in a non-trivial example the problem is merely shifted to a different place.

Consider thus the basic document below; note that the phantom type is made
opaque in the signature, thus preventing the rogue unification we wish to
avoid at all cost:

_____________________________________________________________________
module type DOCUMENT =
sig
        type inline_t = node_t list
         and node_t =
                private
                | Text of string
                | Bold of inline_t
                | Italic of inline_t

        type 'a t

        val text: string -> [> `Basic] t
        val bold: 'a t list -> 'a t
        val italic: [< `Basic | `Complex] t list -> [> `Complex] t
end


module Document: DOCUMENT =
struct
        type inline_t = node_t list
         and node_t =
                | Text of string
                | Bold of inline_t
                | Italic of inline_t

        type 'a t = node_t

        let text str = Text str
        let bold inl = Bold inl
        let italic inl = Italic inl
end
_____________________________________________________________________


Let us now try to extend the basic type with a couple of parsing functions.
Simpy using "include" will fail, because the signature DOCUMENT has made
Document.t opaque.  I thought the solution could come from the "with type"
directive:

_____________________________________________________________________
module type DOCUMENT_PARSER =
sig
        include DOCUMENT

        exception Invalid_Subset

        val parse_complex: string -> [> `Basic | `Complex] t
        val parse_basic: string -> [> `Basic] t
end

module Document_parser: DOCUMENT_PARSER with type 'a t = Document.node_t =
struct
        include Document

        exception Invalid_Subset

        let parse_complex = function
                | "complex"     -> italic [text "foo"]
                | _             -> bold [text "bar"]

        let rec complex_to_basic = function
                | Text str      -> text str
                | Bold inl      -> bold (List.map complex_to_basic inl)
                | Italic inl    -> raise Invalid_Subset

        let parse_basic str = complex_to_basic (parse_complex str)
end
_____________________________________________________________________


But unfortunately something else must me missing, because the compiler
complains on the one-before-last line:

Error: This expression has type
         ([> `Basic | `Complex ] as 'a) t = 'a Document.t
       but is here used with type node_t = Document.node_t


I have also tried the functor-based approach, but the same error happens.
Any ideas on how to solve this?  Or could this be one of those cases where
only the object system can help?

Thanks in advance for your time!

Kind regards,
Dario Teixeira




      __________________________________________________________
Not happy with your email address?.
Get the one you really want - millions of new email addresses available now at Yahoo! http://uk.docs.yahoo.com/ymail/new.html


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

* Re: [Caml-list] Phantom types: transparency vs rogue unification
  2008-06-26 21:41     ` Dario Teixeira
@ 2008-06-27 20:35       ` Dario Teixeira
  0 siblings, 0 replies; 5+ messages in thread
From: Dario Teixeira @ 2008-06-27 20:35 UTC (permalink / raw)
  To: caml-list

> I may have spoken too soon about the functor solving the
> problem.  In fact,
> in a non-trivial example the problem is merely shifted to a
> different place.

Hi,

Just to let you know that I have found an embarrassingly simple workaround:
the Document module can just provide node_of_complex and node_of_basic
functions to overcome the opaqueness of Document.t.  I haven't found (yet)
any major disadvantages to this solution, so I reckon the problem can be
solved this way (code below).

Thanks again for your attention,
Dario Teixeira

___________________________________________________________________________

(************************************************************************)
(* Document.								*)
(************************************************************************)

module type DOCUMENT =
sig
	type inline_t = node_t list
	 and node_t =
		private
		| Text of string
		| Bold of inline_t
		| Italic of inline_t

	type 'a t

	val node_of_complex: [< `Basic | `Complex] t -> node_t
	val node_of_basic: [< `Basic] t -> node_t

	val text: string -> [> `Basic] t
	val bold: 'a t list -> 'a t
	val italic: [< `Basic | `Complex] t list -> [> `Complex] t
end


module Document : DOCUMENT =
struct
	type inline_t = node_t list
	 and node_t =
		| Text of string
		| Bold of inline_t
		| Italic of inline_t

	type 'a t = node_t

	let node_of_complex node = node
	let node_of_basic node = node

	let text str = Text str
	let bold inl = Bold inl
	let italic inl = Italic inl
end


(************************************************************************)
(* Document_parser.							*)
(************************************************************************)

module Document_parser:
sig
	exception Invalid_subset

	val parse_complex: string -> [> `Basic | `Complex] Document.t
	val parse_basic: string -> [> `Basic] Document.t
end =
struct
	open Document

	exception Invalid_subset

	let parse_complex = function
		| "complex"     -> italic [bold [text "ola"]]
		| _             -> bold [text "ola"]

	let complex_to_basic node =
		let rec complex_to_basic_aux = function
			| Text str      -> text str
			| Bold inl      -> bold (List.map complex_to_basic_aux inl)
			| Italic inl    -> raise Invalid_subset
		in complex_to_basic_aux (node_of_complex node)

	let parse_basic str = complex_to_basic (parse_complex str)
end


(************************************************************************)
(* top-level.								*)
(************************************************************************)

open Document
open Document_parser

let foo = bold [text "foo"];;

let bar = italic [text "bar"];;

let a = parse_complex "complex";;

let b = parse_complex "basic";;

let c = parse_basic "basic";;

let d = parse_basic "complex";;  (* Raises exception *)
___________________________________________________________________________




      __________________________________________________________
Not happy with your email address?.
Get the one you really want - millions of new email addresses available now at Yahoo! http://uk.docs.yahoo.com/ymail/new.html


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

end of thread, other threads:[~2008-06-27 20:35 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-06-25 14:26 Phantom types: transparency vs rogue unification Dario Teixeira
2008-06-25 15:02 ` [Caml-list] " Jacques Carette
2008-06-25 16:29   ` Dario Teixeira
2008-06-26 21:41     ` Dario Teixeira
2008-06-27 20:35       ` Dario Teixeira

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).