From: Dario Teixeira <darioteixeira@yahoo.com>
To: caml-list@yquem.inria.fr
Subject: Private types in 3.11, again
Date: Thu, 15 Jan 2009 09:31:43 -0800 (PST) [thread overview]
Message-ID: <279131.80665.qm@web111514.mail.gq1.yahoo.com> (raw)
Hi,
(Note: this problem is similar to one I've posted before; there are however
differences. Also, my apologies for the problem requiring a long introduction
before actually being presented).
Consider a sequence of nodes such as those present in an inline context in
XHTML. Some of these nodes are leaf nodes (Text and Href), while others are
built from a list of existing nodes (Bold and Mref). Moreover, some nodes
create hyperlinks (Href and Mref). A straightforward Ocaml representation
could be as follows:
type node_t =
| Text of string
| Bold of node_t list (* recursive! *)
| Href of string
| Mref of string * node_t list (* recursive! *)
There is furthermore an important restriction: a link node may not be the
ancestor of another link node, no matter how deep the ancestry level (in
similarity with the W3C rules for XHTML). While this restriction could be
enforced by runtime checking, I very much prefer to represent nodes in such
a way that the type system itself would ensure the impossibility of creating
illegal values.
Below is the best solution I could come up with so far (note that this code
requires 3.11). It uses a phantom type to "taint" link nodes, making them
unsuitable to be descendants of other link nodes. Also, note the use of
'private' to allow pattern-matching by external code without breaking the
phantom type restrictions.
module Node:
sig
type node_t =
private
| Text of string
| Bold of node_t list
| Href of string
| Mref of string * node_t list
type +'a t = private node_t
val text: string -> [> `Nonlink ] t
val bold: 'a t list -> 'a t
val href: string -> [> `Link ] t
val mref: string -> [< `Nonlink ] t list -> [> `Link ] t
end =
struct
type node_t =
| Text of string
| Bold of node_t list
| Href of string
| Mref of string * node_t list
type +'a t = node_t
let text txt = Text txt
let bold seq = Bold seq
let href lnk = Href lnk
let mref lnk seq = Mref (lnk, seq)
end
So far so good. Now consider a function that takes a node and returns
another node, in all identical except that all occurrences of Text (either
in the parent or in all descendants if the node is defined recursively)
are replaced by the capitalised version. Moreover, I want to place this
function in a different module, called Foobar:
module Foobar:
sig
open M
val capitalise_node: 'a t -> 'a t
end =
struct
open M
let capitalise_node node =
let rec capitalise_node_aux forbid_link node = match (forbid_link, node) with
| (_, Text txt) -> text (String.capitalize txt)
| (x, Bold seq) -> bold (List.map (capitalise_node_aux x) seq)
| (false, Href lnk) -> href lnk
| (false, Mref (lnk, seq)) -> mref lnk (List.map (capitalise_node_aux true) seq)
| _ -> failwith "oops"
in capitalise_node_aux false node
end
Unfortunately, the code above fails to compile, producing this error:
Error: This expression has type [> `Link | `Nonlink ] M.t list
but is here used with type [< `Nonlink ] M.t list
The second variant type does not allow tag(s) `Link
For the Foobar module to work, I have to remove the 'private' declaration in
module Node, which of course breaks the phantom type. Note that the function
above will also compile fine if it is defined inside Node instead of Foobar
(but which is useless to me).
So my question is how can I make the Foobar code behave as if it were defined
inside Node. Based on a previous thread [1], I'm guessing there is a solution,
but I've been unable to hit on its exact formulation.
Thanks in advance for any light you may shed on this issue. It is much
appreciated!
Kind regards,
Dario Teixeira
[1] http://groups.google.com/group/fa.caml/browse_thread/thread/d9c5e30b973db187#
next reply other threads:[~2009-01-15 17:31 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-01-15 17:31 Dario Teixeira [this message]
2009-01-19 15:09 ` [Caml-list] " Dario Teixeira
2009-01-20 16:33 ` Dario Teixeira
2009-01-20 17:29 ` Jacques Carette
2009-01-20 17:48 ` Dario Teixeira
2009-01-21 10:48 ` Jacques Garrigue
2009-01-21 10:38 ` Jacques Garrigue
2009-01-21 13:22 ` Jacques Garrigue
2009-01-21 19:11 ` Dario Teixeira
2009-01-21 20:17 ` Gabriel Kerneis
2009-01-21 21:52 ` GADTs in Ocaml (was: Private types in 3.11, again) Dario Teixeira
2009-01-22 1:36 ` [Caml-list] Private types in 3.11, again Jacques Garrigue
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=279131.80665.qm@web111514.mail.gq1.yahoo.com \
--to=darioteixeira@yahoo.com \
--cc=caml-list@yquem.inria.fr \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox