caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: David Allsopp <dra-news@metastack.com>
To: OCaml List <caml-list@inria.fr>
Subject: RE: [Caml-list] GADTs + Phantom types
Date: Wed, 3 Jul 2013 17:11:02 +0000	[thread overview]
Message-ID: <E51C5B015DBD1348A1D85763337FB6D9CC897BF6@Remus.metastack.local> (raw)
In-Reply-To: <CAPFanBEb4TknmGzbtc2bh9W2MDJ0B8o=J9-zfc-u=qQNt9EVEA@mail.gmail.com>

Thanks for looking at this...

Gabriel Scherer wrote:
> In fact, you can write a coercion function by hand (because in fact it is
> sound to declare the parameter covariant in your type-declaration, but the
> type-checker doesn't see that):
> 
> # let coerce : type a . (a, [ `Before ]) t -> (a, [ `Before | `After
> ]) t = function A -> A | C -> C;;
> val coerce : ('a, [ `Before ]) t -> ('a, [ `After | `Before ]) t = <fun>

The problem with that in my actual use case the type is buried inside a list - so that effectively requires the list to be copied just to coerce each value to the other type. There are also a large number of constructors and other options in addition to `Before and `After! It's a lot of code just for a type coercion...

When you say that the type checker can't see that the covariant declaration is sound, is it one of those instances from your previous thread where you'd reckoned that the type-checker *could* be altered to see it, but the current implementation doesn't or is it not possible in general for it to detect that kind of declaration?

> I personally try to avoid using polymorphic variants as phantom types when
> I use GADTs. The two features are subtle enough in isolation to become a
> potential nightmare when put together.

Actually, I think that's the best lesson here, thanks! :o) While the initial functions are straightforward, it seems complexity is always lurking around the corner as soon as you try to combine them.

> Given that GADTs allow a cleaner
> approach than phantom types, I would encourage you to try GADT-using,
> polymorphic-variant-free approaches.
> 
> (Of course that doesn't negate the usefulness of polymorphic variants when
> actually used as values, instead of weird type-level stuff only.)
> 
> type tag_b = TagB
> type tag_a = TagA
> type tag_c = TagC
> 
> type (_, _) t =
>   | A : (bool, tag_a) t
>   | B : (int, tag_b) t
>   | C : (string, tag_c) t
> 
> type _ before =
>   | BA : tag_a before
>   | BC : tag_c before
> 
> type _ after =
>   | AA : tag_a after
>   | AB : tag_b after
> 
> let f1 : type s tag . (s, tag) t * s -> unit  = function
>   | A, b -> if b then ()
>   | B, i -> if i = 0 then ()
>   | C, s -> if s = "" then ()
> 
> let f2 : type s tag . tag before * (s, tag) t * s -> string = function
>   | BA, A, b -> string_of_bool b
>   | BC, C, s -> s

I don't follow here the equivalence to my original example - this requires that BA or BC in the call which kind of negates the point that all of the checking is embedded in the type system. However, what it did show me was that I was missing an obvious trick to allow for tagging both `Before and `After which is to have the tags:

type 'a connect
type before = A
type after = B

and then the GADT can be written:

type (_, _) t = A : (bool, _ connect) t
              | B : (int, after connect) t
              | C : (string, before connect) t

and then with a few more type variables in [f1] and [f2] you can have:

let f1 : type s a . (s, a connect) t -> s -> unit = fun attr value ->
  match attr with
    A -> Printf.printf "Bool given: %b\n" value
  | B -> Printf.printf "Int given: %d\n" value
  | C -> Printf.printf "String given: %S\n" value

let f2 : type s . (s, before connect) t -> s -> unit = fun attr value ->
  f1 attr value


David

  reply	other threads:[~2013-07-03 17:11 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-07-03 13:59 David Allsopp
2013-07-03 14:47 ` Gabriel Scherer
2013-07-03 17:11   ` David Allsopp [this message]
2013-07-04  0:42     ` Jacques Garrigue
2013-07-05 10:21 ` oleg

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=E51C5B015DBD1348A1D85763337FB6D9CC897BF6@Remus.metastack.local \
    --to=dra-news@metastack.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;
as well as URLs for NNTP newsgroup(s).