caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
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#







             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;
as well as URLs for NNTP newsgroup(s).