From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail2-relais-roc.national.inria.fr (mail2-relais-roc.national.inria.fr [192.134.164.83]) by sympa.inria.fr (Postfix) with ESMTPS id 95EFB7FC6B for ; Mon, 19 Oct 2015 22:16:15 +0200 (CEST) IronPort-PHdr: 9a23:qAq9KBXqQI619F5xRUSlFS7mqfbV8LGtZVwlr6E/grcLSJyIuqrYZhOPt8tkgFKBZ4jH8fUM07OQ6PC8Hz1eqs/d4DgrS99laVwssY0uhQsuAcqIWwXQDcXBSGgEJvlET0Jv5HqhMEJYS47UblzWpWCuv3ZJQk2sfTR8Kum9IIPOlcP/j7n0oM2PJVQWz2PhMftbF1afk0b4joEum4xsK6I8mFPig0BjXKBo/15uPk+ZhB3m5829r9ZJ+iVUvO89pYYbCf2pN4xxd7FTDSwnPmYp/4Wr8ECbFUrcrkcbB0cbiBdNEkD05RX+WY+55jrzsu56wAGVOMT3SfY/XjH0vIlxTxq9sz8GLTkiuEzTi8toi79fpwnp8wdyx4/TeIaIHPV7d6LZO9gdQDwSDY5qSyVdD9bkPMM0BO0bMLMd9tGlqg== Authentication-Results: mail2-smtp-roc.national.inria.fr; spf=None smtp.pra=seliopou@gmail.com; spf=Pass smtp.mailfrom=seliopou@gmail.com; spf=None smtp.helo=postmaster@mail-lb0-f177.google.com Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of seliopou@gmail.com) identity=pra; client-ip=209.85.217.177; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="seliopou@gmail.com"; x-sender="seliopou@gmail.com"; x-conformance=sidf_compatible Received-SPF: Pass (mail2-smtp-roc.national.inria.fr: domain of seliopou@gmail.com designates 209.85.217.177 as permitted sender) identity=mailfrom; client-ip=209.85.217.177; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="seliopou@gmail.com"; x-sender="seliopou@gmail.com"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of postmaster@mail-lb0-f177.google.com) identity=helo; client-ip=209.85.217.177; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="seliopou@gmail.com"; x-sender="postmaster@mail-lb0-f177.google.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: A0CyAACsTiVWlLHZVdFehHkGv3GGHgKBNQc8EAEBAQEBAQEBEAEBAQEHCwsJHzCCJoIHAQEBAwESEQQZARsdAQMBCwYDAgsNKgICIQEBEQEFARwGEwgah3gBAwoIlDuPToExPjGLSYFsgnmINQoZJw1WhCYBAQEBBgEBAQEBARcBBQ6GaYR+glCBWWQHgmmBRQEEliOICYMfgXWBWJJ2g1uCIxIjgRc4gi8jgXgiNIVnAQEB X-IPAS-Result: A0CyAACsTiVWlLHZVdFehHkGv3GGHgKBNQc8EAEBAQEBAQEBEAEBAQEHCwsJHzCCJoIHAQEBAwESEQQZARsdAQMBCwYDAgsNKgICIQEBEQEFARwGEwgah3gBAwoIlDuPToExPjGLSYFsgnmINQoZJw1WhCYBAQEBBgEBAQEBARcBBQ6GaYR+glCBWWQHgmmBRQEEliOICYMfgXWBWJJ2g1uCIxIjgRc4gi8jgXgiNIVnAQEB X-IronPort-AV: E=Sophos;i="5.17,703,1437429600"; d="scan'208";a="183522245" Received: from mail-lb0-f177.google.com ([209.85.217.177]) by mail2-smtp-roc.national.inria.fr with ESMTP/TLS/AES128-GCM-SHA256; 19 Oct 2015 22:16:14 +0200 Received: by lbbes7 with SMTP id es7so65615412lbb.2 for ; Mon, 19 Oct 2015 13:16:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc:content-type; bh=zpp4nHnJds8avswniikTA5ugV3TGNZQipWASUYH/X9E=; b=MS0EJ8vJT7WqBPwIXlBa7z0C2i7g3oVDjtcxUf+VkmjPifCC3zXHL4EFSP8DcQKiyA vmVeaMltHysvwNzF2jGtaDjwgljiM8YZYbGcLsDMpbe9PAnf3KeLBfYlpzrczZfnTHzo J6qREBERD1iEF6bqKyvqtGsH395ZRk0kRyv7FxgnJ0pHTPHb47FqG7wI0mdDEFpuRbxo L1V1/OKtQagXocPQj7V8BxqbQuu2dT7lAnslg0B+Xq+7yTUtBlP9a/VBbv6Xor6aoIfz CZEkezaCVRIXqpWO2USsIShb72nzttvyVVxcTmiWX7GMUJ1D6Ro6PTW68Mpg1KenCFle zBCA== X-Received: by 10.112.147.10 with SMTP id tg10mr16157280lbb.58.1445285773618; Mon, 19 Oct 2015 13:16:13 -0700 (PDT) MIME-Version: 1.0 Received: by 10.25.213.2 with HTTP; Mon, 19 Oct 2015 13:15:54 -0700 (PDT) In-Reply-To: References: From: Spiros Eliopoulos Date: Mon, 19 Oct 2015 16:15:54 -0400 Message-ID: To: Jeremy Yallop Cc: OCaml Content-Type: multipart/alternative; boundary=047d7b3a8ec022f45705227ad10c Subject: Re: [Caml-list] "map"-ing parameterized class types --047d7b3a8ec022f45705227ad10c Content-Type: text/plain; charset=UTF-8 Thank, Jeremy, for the great explanation and possible work around. Unfortunately for me, the workaround won't be possible to use for the js_of_ocaml use case I have in mind. At least not without sprinkling Obj.magic everywhere, which is what I'm trying to avoid. Based on your workaround, I thought I might be able to create my own workaround with code that looked something like this: module rec R : sig class type ['a] container1 = object method map : ('a -> 'b) -> 'b R.container2 end class type ['a] container2 = object method op : 'a R.container1 end end = struct ... end But of course the type system complained that: Error: In the definition of R.container2, type 'b R.container1 should be 'a R.container1 I thought this might be for a different than the one you mentioned, but upon further reflection and a single unrolling of the types, it seems to be the regular type constraint that's causing this error as well. Well, back to drawing board. Thanks again. On Mon, Oct 19, 2015 at 2:14 PM, Jeremy Yallop wrote: > On 19 October 2015 at 17:58, Spiros Eliopoulos wrote: > > I'm trying to create a "container" class[0] that can store a value of > type > > 'a, and transform that value to another value of type 'b. I'm trying to > do > > this by including a "map" method in the container that applies a > function to > > the value and returns a new instance of container with the transformed > > value. Despite the annotations, the types aren't working out as I > expected: > > > > class ['a] container (v:'a) = object > > method map (f:'a -> 'b) : 'b container = new container (f v) > > end;; > > (* class ['a] container : 'a -> object method map : ('a -> 'a) -> 'a > > container end *) > > > > I gather I'm either doing something wrong, or it's not possible. I > suppose > > my question, which one is it? > > It's not exactly possible, but there are workarounds. > > The reason the types don't work out as you expect is that structural > types (objects, classes, polymorphic variants) in OCaml are required > to be "regular". A parameterised type t is regular if every > occurrence of t within its own definition is instantiated with the > parameters. For example, the following type (t1) is regular: > > # type ('a, 'b) t1 = [`A of ('a, 'b) t1];; > type ('a, 'b) t1 = [ `A of ('a, 'b) t1 ] > type ('a, 'b) t1 = [`A of ('a, 'b) t1] > > but this one (t2) isn't, because the order of parameters is reversed > > # type ('a, 'b) t2 = [`A of ('b, 'a) t2];; > Characters 5-38: > type ('a, 'b) t2 = [`A of ('b, 'a) t2];; > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > Error: In the definition of t2, type ('b, 'a) t2 should be ('a, 'b) t2 > type ('a, 'b) t2 = [`A of ('b, 'a) t2] > > and this one (t3) isn't, either, because the parameters are > instantiated with concrete types > > # type ('a, 'b) t3 = [`A of (int, string) t3];; > Characters 5-43: > type ('a, 'b) t3 = [`A of (int, string) t3];; > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > Error: In the definition of t3, type (int, string) t3 should be ('a, > 'b) t3 > > As the output shows, OCaml rejects the non-regular definitions for t2 > and t3. Your example code also attempts to define a non-regular type, > but since the type variable 'b is available for unification, OCaml > doesn't need to reject the definition altogether. Instead, 'b is > unified with the class parameter 'a to produce a regular type which is > acceptable to OCaml (but which doesn't do what you want). > > How might we side-step the regularity constraint? One approach is to > arrange things so that the recursion passes through a non-structural > type, such as a variant or record. In an imaginary extension to OCaml > with support for groups of mutually-recursive types and classes we > could write something like this: > > class ['a] container (v:'a) = object > method map : 'b. ('a -> 'b) -> 'b container_aux = > fun f -> { container = new container (f v) } > end > and 'a container_aux = { container: 'a container } > > In today's OCaml we can achieve a similar effect by routing all the > recursive references through a recursive module, albeit at a rather > heavy syntactic cost: > > module rec R: > sig > class ['a] container : 'a -> > object > method map : 'b. ('a -> 'b) -> 'b R.container_aux > end > type 'a container_aux = { container: 'a container } > end = > struct > class ['a] container (v:'a) = object > method map : 'b. ('a -> 'b) -> 'b R.container_aux = > fun f -> { R.container = new R.container (f v) } > end > type 'a container_aux = { container: 'a container } > end > > which at least achieves the desired effect: > > # let c = new R.container 3;; > val c : int R.container = > # (c#map string_of_int).R.container;; > - : string R.container = > --047d7b3a8ec022f45705227ad10c Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
Thank, Jeremy, for the great explanation and possible= work around. Unfortunately for me, the workaround won't be possible to= use for the js_of_ocaml use case I have in mind. At least not without spri= nkling Obj.magic everywhere, which is what I'm trying to avoid.

Based on your workaround, I thought I might be able to cr= eate my own workaround with code that looked something like this:

=C2=A0 module rec R : sig
=C2=A0 =C2=A0 class type ['a] = container1 =3D object
=C2=A0 =C2=A0 =C2=A0 method map : ('a -= > 'b) -> 'b R.container2
=C2=A0 =C2=A0 end
=C2=A0 =C2=A0 class type ['a] container2 =3D object
=C2=A0 = =C2=A0 =C2=A0 method op : 'a R.container1
=C2=A0 =C2=A0 end
=C2=A0 end =3D struct=C2=A0
=C2=A0 =C2=A0 ...
= =C2=A0 end

But of course the type system complaine= d that:

=C2=A0 Error: In the definition of R.conta= iner2, type
=C2=A0 =C2=A0 =C2=A0 =C2=A0'b R.containe= r1
=C2=A0 =C2=A0 =C2=A0 =C2=A0should be
=C2=A0 =C2=A0 = =C2=A0 =C2=A0'a R.container1

I thought t= his might be for a different than the one you mentioned, but upon further r= eflection and a single unrolling of the types, it seems to be the regular t= ype constraint that's causing this error as well.

<= div>Well, back to drawing board. Thanks again.

On Mon, Oct 19, 2015 at 2:14 PM, J= eremy Yallop <yallop@gmail.com> wrote:
On 19 October 2015 at 17:58, Spiros Eliopoul= os <seliopou@gmail.com> wro= te:
> I'm trying to create a "container" class[0] that can sto= re a value of type
> 'a, and transform that value to another value of type 'b. I= 9;m trying to do
> this by including a "map" method in the container that appli= es a function to
> the value and returns a new instance of container with the transformed=
> value. Despite the annotations, the types aren't working out as I = expected:
>
>=C2=A0 =C2=A0class ['a] container (v:'a) =3D object
>=C2=A0 =C2=A0 =C2=A0method map (f:'a -> 'b) : 'b contain= er =3D new container (f v)
>=C2=A0 =C2=A0end;;
>=C2=A0 =C2=A0(* class ['a] container : 'a -> object method m= ap : ('a -> 'a) -> 'a
> container end=C2=A0 *)
>
> I gather I'm either doing something wrong, or it's not possibl= e. I suppose
> my question, which one is it?

It's not exactly possible, but there are workarounds.

The reason the types don't work out as you expect is that structural
types (objects, classes, polymorphic variants) in OCaml are required
to be "regular".=C2=A0 A parameterised type t is regular if every=
occurrence of t within its own definition is instantiated with the
parameters.=C2=A0 For example, the following type (t1) is regular:

=C2=A0 =C2=A0# type ('a, 'b) t1 =3D [`A of ('a, 'b) t1];; =C2=A0 =C2=A0type ('a, 'b) t1 =3D [ `A of ('a, 'b) t1 ]
=C2=A0 =C2=A0 =C2=A0type ('a, 'b) t1 =3D [`A of ('a, 'b) t1= ]

but this one (t2) isn't, because the order of parameters is reversed

=C2=A0 =C2=A0# type ('a, 'b) t2 =3D [`A of ('b, 'a) t2];; =C2=A0 =C2=A0Characters 5-38:
=C2=A0 =C2=A0 =C2=A0type ('a, 'b) t2 =3D [`A of ('b, 'a) t2= ];;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
=C2=A0 =C2=A0Error: In the definition of t2, type ('b, 'a) t2 shoul= d be ('a, 'b) t2
=C2=A0 =C2=A0 =C2=A0type ('a, 'b) t2 =3D [`A of ('b, 'a) t2= ]

and this one (t3) isn't, either, because the parameters are
instantiated with concrete types

=C2=A0 =C2=A0# type ('a, 'b) t3 =3D [`A of (int, string) t3];;
=C2=A0 =C2=A0Characters 5-43:
=C2=A0 =C2=A0 =C2=A0type ('a, 'b) t3 =3D [`A of (int, string) t3];;=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ =C2=A0 =C2=A0Error: In the definition of t3, type (int, string) t3 should b= e ('a, 'b) t3

As the output shows, OCaml rejects the non-regular definitions for t2
and t3.=C2=A0 Your example code also attempts to define a non-regular type,=
but since the type variable 'b is available for unification, OCaml
doesn't need to reject the definition altogether.=C2=A0 Instead, 'b= is
unified with the class parameter 'a to produce a regular type which is<= br> acceptable to OCaml (but which doesn't do what you want).

How might we side-step the regularity constraint?=C2=A0 One approach is to<= br> arrange things so that the recursion passes through a non-structural
type, such as a variant or record.=C2=A0 In an imaginary extension to OCaml=
with support for groups of mutually-recursive types and classes we
could write something like this:

=C2=A0 =C2=A0class ['a] container (v:'a) =3D object
=C2=A0 =C2=A0 =C2=A0method map : 'b. ('a -> 'b) ->= 'b container_aux =3D
=C2=A0 =C2=A0 =C2=A0 =C2=A0fun f -> { container =3D new container (f v) = }
=C2=A0 =C2=A0end
=C2=A0 =C2=A0and 'a container_aux =3D { container: 'a container }
In today's OCaml we can achieve a similar effect by routing all the
recursive references through a recursive module, albeit at a rather
heavy syntactic cost:

=C2=A0 =C2=A0module rec R:
=C2=A0 =C2=A0sig
=C2=A0 =C2=A0 =C2=A0class ['a] container : 'a ->= ;
=C2=A0 =C2=A0 =C2=A0 =C2=A0object
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0method map : 'b. ('a ->= 'b) -> 'b R.container_aux
=C2=A0 =C2=A0 =C2=A0 =C2=A0end
=C2=A0 =C2=A0 =C2=A0type 'a container_aux =3D { container: 'a conta= iner }
=C2=A0 =C2=A0end =3D
=C2=A0 =C2=A0struct
=C2=A0 =C2=A0 =C2=A0class ['a] container (v:'a) = =3D object
=C2=A0 =C2=A0 =C2=A0 =C2=A0method map : 'b. ('a -> 'b= ) -> 'b R.container_aux =3D
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fun f -> { R.container =3D new R.conta= iner (f v) }
=C2=A0 =C2=A0 =C2=A0end
=C2=A0 =C2=A0 =C2=A0type 'a container_aux =3D { container: 'a conta= iner }
=C2=A0 =C2=A0end

which at least achieves the desired effect:

=C2=A0 =C2=A0# let c =3D new R.container 3;;
=C2=A0 =C2=A0val c : int R.container =3D <obj>
=C2=A0 =C2=A0# (c#map string_of_int).R.container;;
=C2=A0 =C2=A0- : string R.container =3D <obj>

--047d7b3a8ec022f45705227ad10c--