caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Leo White <lpw25@cam.ac.uk>
To: David Allsopp <dra-news@metastack.com>
Cc: OCaml List <caml-list@inria.fr>
Subject: Re: [Caml-list] Match error with abstract types in modules
Date: Tue, 24 Feb 2015 19:11:40 +0000	[thread overview]
Message-ID: <87sidv2kgj.fsf@study.localdomain> (raw)
In-Reply-To: <E51C5B015DBD1348A1D85763337FB6D9E994424B@Remus.metastack.local> (David Allsopp's message of "Tue, 24 Feb 2015 18:26:41 +0000")

>> so that `int foo` is equal to `float foo`, then your match isn't really
>> exhaustive.
>
> How so?

Both constructors would then have the same type. Since it is possible
they both have the same type, you need to match on both of them.

> What's (very) confusing to me is that it seems to permit
> nonsensical matches without warning. In both the unconstrained and
> constrained versions of Foo, the resulting type of the function [foo] is
> [int Foo.foo t -> unit] so [foo B] is always rejected. But you get no
> warning about an unusable match case with:
>
> let foo = function A -> () | B -> ()
>

This is because `foo B` may not always be rejected. Consider the
following code:

    module F (Foo : sig type 'a foo end) = struct

      type _ t = A : int Foo.foo t
           | B : float Foo.foo t

      let foo = function
        | A -> ()
        | B -> ()

      (* [foo B] would be an error here *)

    end

    module M = F(struct type 'a foo = unit end)

    (* But out here it is fine *)
    let () = M.foo M.B

> although
>
> let foo = function A | B -> ()
>
> unsurprisingly does not type.

That's due to GADTs not currently being compatible with or-patterns, it
is not related to the injectivity issues you are having.

>> In the second version, the compiler cheats. Since `bar` is definied in the
>> same module, it knows that the abstract `bar` type it can see is actually
>> the definition, not just an abstraction of a type defined elsewhere, so it
>> knows that `int bar` cannot equal `float bar`.
>> 
>> Personally, I dislike this behaviour and would prefer both cases to give
>> an error.
>
> What's to dislike?

Giving different behviour based on whether something is defined in the
same module is anti-modular, and confuses people when they try to split
their code across different modules.

> It's rather useful - the constructors of a GADT are
> partionable by type.

That is indeed useful, but only really works in OCaml if you expose the
definitions of these types, so that OCaml can see that they are
definitely not the same type. This is why you see people use:

    type z = Z

    type 'a s = S

when doing type-level arithmetic. By giving types with incompatible
definitions, rather than abstract types, OCaml can safely conclude they
are not the same type.

> What caught me out was that two constructors with
> distinct types (in my actual use with different instantiations of 'a
> BatSet.t) were being regarded as equal in terms of pattern matching. I'd
> be quite happy if a type annotation were necessary to make it explicit
> that I meant to do it:
>
> let foo (key : int Foo.foo t) =
>   match key with A -> ()
>
> Especially with that explicit type, I find it very odd to get a warning
> about a constructor whose type is not permitted in this context and so
> cannot be matched.

The problem is that OCaml has no way to know that `int Foo.foo` is not
equal to `float Foo.foo` so it must insist that you include a case for
it. OCaml also doesn't know that `int Foo.foo` does equal `float
Foo.foo` so it can't let you call `foo Foo.B` either.

Regards,

Leo

  reply	other threads:[~2015-02-24 19:11 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-02-24 17:25 David Allsopp
2015-02-24 18:02 ` Leo White
2015-02-24 18:26   ` David Allsopp
2015-02-24 19:11     ` Leo White [this message]
2015-02-25  8:41       ` David Allsopp
2015-02-24 19:52     ` Jeremy Yallop
2015-02-24 20:03       ` Ben Millwood
2015-02-24 21:11         ` Leo White
2015-02-25  8:41           ` David Allsopp
2015-02-25  9:48             ` Gabriel Scherer

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=87sidv2kgj.fsf@study.localdomain \
    --to=lpw25@cam.ac.uk \
    --cc=caml-list@inria.fr \
    --cc=dra-news@metastack.com \
    /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).