* 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