caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* killing a cat
@ 2008-09-08 18:37 Warren Harris
  2008-09-09 14:11 ` Zheng Li
  0 siblings, 1 reply; 2+ messages in thread
From: Warren Harris @ 2008-09-08 18:37 UTC (permalink / raw)
  To: caml-list caml-list

[-- Attachment #1: Type: text/plain, Size: 3094 bytes --]

I've encountered a complicated issue that I'm wondering if anyone can  
shed some light on here. It relates to polymorphic types what I like  
to call Schrödinger types (sorry, I don't know the real name for  
these, but they're the dreaded polymorphic variables with underscores  
that, like Schrödinger's cat, are undefined until you use them for the  
first time). I'll try to distill it down to its simplest form here,  
but the issue arises from desiring to implement something based on  
"type-indexed values" such as described here: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.53.1977

In its simplest form, I have a writer type and a dispatching handler  
like this:

type ('a, 'b) writer = ((unit -> 'a) -> 'b)
type ('a, 'b) handler = { handle : 'a -> (string -> 'a -> 'b, 'b)  
writer }

Then I wish to define a family of handlers that ultimately call the  
writer's continuation. In the very simplest form, a handler might be  
expressed as:

let f = { handle = fun a k -> write_int_field "f" a k }

which would have type "(int, 'a) handler" as desired. (In the real  
implementation, there will be more than one handler, hence the need  
for the record.) However, I wish to define a family of handlers that  
all share common implementation, so I define a helper function (again,  
vastly simplified):

let helper name wf = { handle = fun a k -> wf name a k }
let f = helper "f" write_int_field

However, here the type of "f" is now "('int, '_a) handler" and fails  
to unify with the desired type expressed in the interface.

Now, I could get around this by making "f" be a function that when  
evaluated returns the result of the helper. However, I'm trying to  
avoid that because in the real implementation, the work of the helper  
is somewhat expensive and should only be performed once. An alternate  
solution can be achieved by pushing the universal qualifier for the  
result type down into the handler:

type 'a handler = { handle : 'b . 'a -> (string -> 'a -> 'b, 'b)  
writer }

However, this doesn't allow me to define the helper function as before:

   let helper name wf = { handle = fun a k -> wf name a k };;
                                   ^^^^^^^^^^^^^^^^^^^^^^
This field value has type 'a -> (string -> 'a -> 'b, 'b) writer
which is less general than 'c. 'd -> (string -> 'd -> 'c, 'c) writer

I am forced into reworking the helper parameter function, wf, to also  
become a record in order for it to be universally qualified:

type 'a field_handler = { write_field : 'b . string -> 'a -> (unit ->  
string -> 'a -> 'b) -> 'b }

This works:

let helper name wf = { handle = fun a k -> wf.write_field name a k }
let f = helper "f" write_int_field

Here "f" has type "int handler" as desired, although this solution  
seems somewhat round-about, and was difficult to arrive at. Can anyone  
suggest another approach that prevents the dreaded underscores? (Note  
that using objects instead of records exhibits the exact same behavior.)

Warren Harris
Metaweb Technologies




[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3739 bytes --]

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

* Re: killing a cat
  2008-09-08 18:37 killing a cat Warren Harris
@ 2008-09-09 14:11 ` Zheng Li
  0 siblings, 0 replies; 2+ messages in thread
From: Zheng Li @ 2008-09-09 14:11 UTC (permalink / raw)
  To: caml-list; +Cc: warren

Hi,

Warren Harris wrote:
> Now, I could get around this by making "f" be a function that when 
> evaluated returns the result of the helper. However, I'm trying to 
> avoid that because in the real implementation, the work of the helper
>  is somewhat expensive and should only be performed once. An
> alternate solution can be achieved by pushing the universal qualifier
> for the result type down into the handler:
> 
> type 'a handler = { handle : 'b . 'a -> (string -> 'a -> 'b, 'b) 
> writer }
I think it's reasonable to use universal qualifier here.

> I am forced into reworking the helper parameter function, wf, to also
>  become a record in order for it to be universally qualified:
> 
> type 'a field_handler = { write_field : 'b . string -> 'a -> (unit ->
>  string -> 'a -> 'b) -> 'b }
> 
> This works:
> 
> let helper name wf = { handle = fun a k -> wf.write_field name a k }
>  let f = helper "f" write_int_field

It's hard to tell where a better option is without knowing your overall
design on types and data structures. On the other hand, I would usually
restrict universal qualifiers into a range as small as possible. In your
example, I would probably define as the follows:

<code>
type 'a handler = { handle: 'a -> (string * 'a) writer }
and  'a writer = { write: 'b. (unit -> 'a -> 'b) -> 'b }

let helper name wf = { handle = wf name }
let write_int_field s i = { write = fun k -> k () (s, i+1) }
let f = helper "f" write_int_field
</code>

which seems simpler to me. However this really depends on your overall
design.

HTH.

--
Zheng


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

end of thread, other threads:[~2008-09-09 14:11 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-09-08 18:37 killing a cat Warren Harris
2008-09-09 14:11 ` Zheng Li

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