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

Leo White wrote:
> >> 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

It turns out that this very helpful explanation has inadvertently answered an issue I had a while ago where I'd ended up leaving a comment in my code that I didn't fully understand why I needed type t = T, type s = S for doing this!

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

Ah, I see - thanks!


David

  reply	other threads:[~2015-02-25  8:41 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
2015-02-25  8:41       ` David Allsopp [this message]
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=E51C5B015DBD1348A1D85763337FB6D9E9947598@Remus.metastack.local \
    --to=dra-news@metastack.com \
    --cc=caml-list@inria.fr \
    --cc=lpw25@cam.ac.uk \
    /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).