From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail3-relais-sop.national.inria.fr (mail3-relais-sop.national.inria.fr [192.134.164.104]) by sympa.inria.fr (Postfix) with ESMTPS id 0403D7FC6B for ; Mon, 19 Oct 2015 20:14:56 +0200 (CEST) IronPort-PHdr: 9a23:QK7KFBfIFQ8otldreGN7LJAhlGMj4u6mDksu8pMizoh2WeGdxc69bB7h7PlgxGXEQZ/co6odzbGG7+axBydZu8rJmUtBWaIPfidNsd8RkQ0kDZzNImzAB9muURYHGt9fXkRu5XCxPBsdMs//Y1rPvi/6tmZKSV3BPAZ4bt74BpTVx5zukbvip9uCPU4R3mb1SIgxBSv1hD2ZjtMRj4pmJ/R54TryiVwMRd5rw3h1L0mYhRf265T41pdi9yNNp6BprJYYAu3SNp41Rr1ADTkgL3t9pIiy7UGCHkOz4S4nSmQMlQEALgzE6wn9Q5P2qWOured52S6COtDeQrU9WDDk5KBuHkzGkiACYhw06mzSm4RBhaZfpwrp8w12x4rTeKmaMfN/euXWetZMFjkJZdpYSyEUWtD0VIAIFedUeL8A94Q= Authentication-Results: mail3-smtp-sop.national.inria.fr; spf=None smtp.pra=yallop@gmail.com; spf=Pass smtp.mailfrom=yallop@gmail.com; spf=None smtp.helo=postmaster@mail-lb0-f179.google.com Received-SPF: None (mail3-smtp-sop.national.inria.fr: no sender authenticity information available from domain of yallop@gmail.com) identity=pra; client-ip=209.85.217.179; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="yallop@gmail.com"; x-sender="yallop@gmail.com"; x-conformance=sidf_compatible Received-SPF: Pass (mail3-smtp-sop.national.inria.fr: domain of yallop@gmail.com designates 209.85.217.179 as permitted sender) identity=mailfrom; client-ip=209.85.217.179; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="yallop@gmail.com"; x-sender="yallop@gmail.com"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail3-smtp-sop.national.inria.fr: no sender authenticity information available from domain of postmaster@mail-lb0-f179.google.com) identity=helo; client-ip=209.85.217.179; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="yallop@gmail.com"; x-sender="postmaster@mail-lb0-f179.google.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: A0AnAQCJMiVWlLPZVdFehGoPBqsDlG6GHgKBNQc8EAEBAQEBAQEBEAEBAQEHCwsJHzCCJoIIAQEEEhEEGQEbHQEDDAYFCwMKAgImAgIhAQERAQUBHAYTIod4AQMSo3SBMT4xi0mBbIJ5iDEKGScNVoQmAQEBAQEFAQEBAQEYAQUOgRSKU4JQgj0HgmmBRQWWI4gJgx+BdYFYknaDW4IjEiOBFziCLyOBXT00hWcBAQE X-IPAS-Result: A0AnAQCJMiVWlLPZVdFehGoPBqsDlG6GHgKBNQc8EAEBAQEBAQEBEAEBAQEHCwsJHzCCJoIIAQEEEhEEGQEbHQEDDAYFCwMKAgImAgIhAQERAQUBHAYTIod4AQMSo3SBMT4xi0mBbIJ5iDEKGScNVoQmAQEBAQEFAQEBAQEYAQUOgRSKU4JQgj0HgmmBRQWWI4gJgx+BdYFYknaDW4IjEiOBFziCLyOBXT00hWcBAQE X-IronPort-AV: E=Sophos;i="5.17,703,1437429600"; d="scan'208";a="151066650" Received: from mail-lb0-f179.google.com ([209.85.217.179]) by mail3-smtp-sop.national.inria.fr with ESMTP/TLS/AES128-GCM-SHA256; 19 Oct 2015 20:14:55 +0200 Received: by lbbwb3 with SMTP id wb3so89611225lbb.1 for ; Mon, 19 Oct 2015 11:14:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=m1Zyn70gQ8R56MmdCKVPI9SQMRvRR3LbC3eIUhVV1iY=; b=H9wcgKXlKokvgRL/w9OYmJVI6E2gxkZuC3ihcKnb9YKin2I0CDFbsJZeIV9cE71TD+ 1vu0Ark31ta9AeUXxAG1H2ijJ8+47tjGZEJI7ImD64IAO5RYws9hDq1eltF2YwDmc+cH OVAbya++jC3uEXFc0IDjDptnt5PYZyb1t64WyHrjbuHrxxsMOkPMXzpIiKzns3HD/i3y gJrhpTjk8mAiw7KSTTzEkSCuz2v4hkVq7iW1KSZB4WJq9FovtUtiWiNKGPdrnyObXPGB Y4EZFkXNkl8CL6GHlOj3DwYHX9NhthAWmhjyKXANcO8RJcNpTZs5de8d8Dsl+C09Pf9+ oLYg== MIME-Version: 1.0 X-Received: by 10.112.200.202 with SMTP id ju10mr15113586lbc.97.1445278494371; Mon, 19 Oct 2015 11:14:54 -0700 (PDT) Received: by 10.25.24.90 with HTTP; Mon, 19 Oct 2015 11:14:54 -0700 (PDT) In-Reply-To: References: Date: Mon, 19 Oct 2015 19:14:54 +0100 Message-ID: From: Jeremy Yallop To: Spiros Eliopoulos Cc: OCaml Content-Type: text/plain; charset=UTF-8 Subject: Re: [Caml-list] "map"-ing parameterized class types 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 =