Mailing list for all users of the OCaml language and system.
 help / color / mirror / Atom feed
From: Gabriel Scherer <gabriel.scherer@gmail.com>
To: Ashish Agarwal <agarwal1975@gmail.com>
Cc: Caml List <caml-list@inria.fr>
Subject: Re: [Caml-list] types not equal when I expect them to be
Date: Thu, 1 Mar 2012 23:18:03 +0100	[thread overview]
Message-ID: <CAPFanBHVQ9yqBcg3=k5yA6_gMnaeeW2EEdUPCNp+W9K47YbQ+A@mail.gmail.com> (raw)
In-Reply-To: <CAMu2m2LSymhSZ+u78Eiwb-LA2QhiMjHiwb45gOd2TfpS3Y9wHA@mail.gmail.com>

Note that Bar is actually unnecessary. The following code exhibit the
exact same behavior:

  module Foo = struct
    module St = struct type t = string let compare = compare end
    module Map (* : Map.S with type key = St.t *)
      = Map.Make(St)
    (* module Map = Map.Make(struct type t = string let compare =
compare end) *)
  end

  module Pack = struct
    module Foo = Foo
  end

  let x : int Foo.Map.t = Pack.Foo.Map.empty

The important point is the interface of Map.Make when looked through
the environment (for example when rebound by `module F = Map.Make`,
then compiled with `ocamlc -i`):

  functor (Ord : Map.OrderedType) ->
    sig
      type key = Ord.t
      type 'a t = 'a Map.Make(Ord).t
      ...
    end

Note the `'a t = 'a Map.Make(Ord).t` part: this is the result of
a so-called "strenghening" operation, where the abstract types `t` in
the signature of the module `M` are noted to be equal to `M.t`; this
allows `N` to remain type-compatible with `M` when writing `module N =
M`.

Now the result of this strenghtening operation in your case is that
your module Map defined in Foo has in its signature:

  type 'a t = 'a Map.Make(Foo.St).t

The sorrow and non-intuitive point is that, after rebinding in Pack,
Pack.Foo gets the following signature:

  type 'a t = 'a Map.Make(Pack.Foo.St).t

Those two type are not path-compatible because `Pack.Foo.St` is not
a path syntactically equal to `Foo.St` -- I'm not sure why the
type-checker couldn't check path equality upto known module aliasing,
however.

Now, why your fixes work:

1. by adding the `: Map.S with type key = ...` signature to your
   Foo.Map module, you seal the module under the Map.S signature which
   has its type `'a t` *abstract*. This cancels the effects of
   strenghtening in the definition of `Foo.Map`. Now, in the
   definition of Pack, aliasing `module Foo = Foo` re-apply
   strenghtening, and you get a `Pack.Foo` module with `type 'a t =
   Foo.Map.t`, so those two are compatible.

2. by applying `struct type t = String let compare = compare` directly
   instead of naming it `Foo.St`, you apply a module that is not
   itself a path, so the typing rules change and you get `'a
   Foo.Map.t` abstract as in the first case.

3. You could also define `St` out of `Foo`, so that types in both Foo
   and Pack.Foo have `type 'a t = Map.Make(St).t`. You are surely
   aware that could similarly use stdlib's `String` directly which
   provides a `compare` operation.

I believe this explains the observed behaviour of the type-checker --
the experts will surely correct if I made technical mistakes. I'm not
sure however whether this behavior is expected (or could be considered
slightly buggy), or if it is imposed by theoretical restrictions or
implementation considerations.

On Thu, Mar 1, 2012 at 5:38 PM, Ashish Agarwal <agarwal1975@gmail.com> wrote:
> The following code fails to compile:
>
> ----- a.ml ----
> module Foo = struct
>   module St = struct type t = string let compare = compare end
>
>   module Map (* : Map.S with type key = St.t *)
>     = Map.Make(St)
>
>   (* module Map = Map.Make(struct type t = string let compare = compare end)
> *)
> end
>
> module Bar = struct
>   type t = int Foo.Map.t
> end
>
> module Pack = struct
>   module Foo = Foo
>   module Bar = Bar
> end
>
> open Pack
> let x : Bar.t = Foo.Map.empty
> ----
>
> $ ocamlc -c foo.ml
> File "foo.ml", line 20, characters 16-29: (* refers to Foo.Map.empty on last
> line *)
> Error: This expression has type
>          'a Pack.Foo.Map.t = 'a Map.Make(Pack.Foo.St).t
>        but an expression was expected of type
>          Pack.Bar.t = int Map.Make(Foo.St).t
>
>
> So Pack.Foo and Foo are not compatible. By trial and error, I found two ways
> to resolve the problem:
>
> 1) Provide an explicit signature for Foo.Map (uncomment the provided
> signature), or
> 2) Inline the structure passed to Map.Make (comment out first definition of
> Foo.Map and uncomment the second one)
>
> But I don't understand why. Can anyone explain the main points. Thank you.
>


  reply	other threads:[~2012-03-01 22:18 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-03-01 16:38 Ashish Agarwal
2012-03-01 22:18 ` Gabriel Scherer [this message]
     [not found] <fa.O3xm0idbEURic/azoi8VH23mQEw@ifi.uio.no>
2012-03-07  8:02 ` Radu Grigore

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='CAPFanBHVQ9yqBcg3=k5yA6_gMnaeeW2EEdUPCNp+W9K47YbQ+A@mail.gmail.com' \
    --to=gabriel.scherer@gmail.com \
    --cc=agarwal1975@gmail.com \
    --cc=caml-list@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