Mailing list for all users of the OCaml language and system.
 help / color / mirror / Atom feed
From: Markus Mottl <markus.mottl@gmail.com>
To: "Petter A. Urkedal" <paurkedal@gmail.com>
Cc: OCaml List <caml-list@inria.fr>
Subject: Re: [Caml-list] Covariant GADTs
Date: Sun, 18 Sep 2016 21:52:24 -0400	[thread overview]
Message-ID: <CAP_800q7jRazR95e4PiFA-9K_3+G2aok_Cvt_8+9bMfVZs9RbQ@mail.gmail.com> (raw)
In-Reply-To: <CALa9pHQ7366ZY7T6f8UT23LjcqUrbwxyoZwYvNxaL+aGx83mfg@mail.gmail.com>

Hi Petter,

thanks, the above approach obviously works with my previous example,
which I had sadly oversimplified.  In my case I was actually dealing
with inlined, mutable records where the above won't work, because then
I can't overwrite fields.  The somewhat more complete example also
contains strange type system behavior I don't understand that may even
be a bug.

The example below shows how GADTs + inline records + (if available)
covariance could help implement doubly linked lists much more
efficiently.  The memory representation using mutable inline records
is essentially optimal.  Taking an element link can be done without
having to introduce superfluous pattern matches, because the GADT
guarantees via the type system that a link is non-empty.

I had to use "Obj.magic" due to the lack of covariance support with
GADTs in the "coerce" function, which I believe to be sound.

The strange behavior is in the "insert_first" function and also the
"create" function: "Empty" is not of covariant type, but nevertheless
can be used for allocation in that way (e.g. in the "let res = ..."
part).  But the commented out lines show that I cannot overwrite the
exact same field in the just allocated value with the exact same
value.  I can understand the reason why the latter is not possible,
but why is allocation allowed that way?  Maybe a type system guru can
explain what's going on.

----------
module Link = struct
  type kind = [ `empty | `elt ]

  type ('el, _) t =
    | Empty : ('el, [ `empty ]) t
    | Elt : {
        mutable prev : ('el, [< kind ]) t;
        el : 'el;
        mutable next : ('el, [< kind ]) t;
      } -> ('el, [ `elt ]) t

  let coerce (t : ('el, [< kind ]) t) : ('el, [< kind ]) t = Obj.magic t

  let get_opt_elt : type a. ('el, a) t -> ('el, [ `elt ]) t option = function
    | Empty -> None
    | Elt _ as elt -> Some elt

  let cut : type a. ('el, a) t -> unit = function
    | Empty -> ()
    | Elt { prev = prev_elt; next = next_elt } ->
        match prev_elt, next_elt with
        | Empty, Empty -> ()
        | Empty, Elt next -> next.prev <- coerce Empty
        | Elt prev, Empty -> prev.next <- coerce Empty
        | Elt prev, Elt next ->
            prev.next <- coerce next_elt;
            next.prev <- coerce prev_elt
end  (* Link *)

module Doubly_linked : sig
  type 'el t
  type 'el elt

  val create : unit -> 'el t
  val first : 'el t -> 'el elt option
  val insert_first : 'el t -> 'el -> unit
  val remove : 'el elt -> unit
end = struct
  open Link

  type 'el t = Head : { mutable head : ('el, [< Link.kind ]) Link.t } -> 'el t
  type 'el elt = ('el, [ `elt ]) Link.t

  let create () = Head { head = Empty }

  let first (Head h) = Link.get_opt_elt h.head

  let insert_first (Head h) el =
    h.head <-
      match h.head with
      | Empty ->
          let res = Elt { prev = Empty; el; next = Empty } in
          (* let Elt foo = res in *)
          (* foo.prev <- Empty; *)
          coerce res
      | Elt _ as next -> coerce (Elt { prev = Empty; el; next })

  let remove = Link.cut
end  (* Doubly_linked *)
----------

Regards,
Markus

On Sun, Sep 18, 2016 at 4:17 AM, Petter A. Urkedal <paurkedal@gmail.com> wrote:
> Hi Markus,
>
> On 17 September 2016 at 19:38, Markus Mottl <markus.mottl@gmail.com> wrote:
>> Hi,
>>
>> GADTs currently do not allow for variance annotations so I wondered
>> whether there are any workarounds for what I want:
>>
>> -----
>> type +_ t =
>>   | Z : [ `z ] t
>>   | S : [ `z | `s ] t -> [ `s ] t
>>
>> let get_next (s : [ `s ] t) =
>>   match s with
>>   | S next -> next
>> -----
>>
>> The above compiles without the variance annotation, but then you
>> cannot express "S Z", because there is no way to coerce Z to be of
>> type [`z | `s] t.
>>
>> Another approach I tried is the following:
>>
>> -----
>> type _ t =
>>   | Z : [ `z ] t
>>   | S : [< `z | `s ] t -> [ `s ] t
>>
>> let get_next (s : [ `s ] t) : [< `z | `s ] t =
>>   match s with
>>   | S next -> next
>> -----
>>
>> The above gives the confusing error:
>>
>> -----
>> Error: This expression has type [< `s | `z ] t
>>        but an expression was expected of type [< `s | `z ] t
>>        The type constructor $S would escape its scope
>> -----
>>
>> There are apparently two type variables associated with [< `s | `z ]
>> t: the locally existential one introduced by matching the GADT, and
>> the one in the type restriction of the function, but the compiler
>> cannot figure out that these can be safely unified.  There is
>> currently no way of specifying locally abstract types that have type
>> constraints, which could possibly also help here.
>
> In this case, you can supply the needed type information to `get_next`
> with structural type matching:
>
>   type _ t = Z : [`z] t | S : 'a t -> [`s of 'a] t
>   let get_next : [`s of 'a] t -> 'a t = function S next -> next
>
> This gives you a richer type which also allows defining get_next_pair, etc.
>
>> Are there workarounds to achieve the above?  Are there any plans to
>> add variance annotations for GADTs?
>
> I had a similar problem myself and found a paper [1] explaining the
> issue and a possible solution.  Haven't really studied it, but as I
> understand the issue is that the current GADTs only introduce type
> equality, which is too strict to verify variance.  What is needed is
> more like (my ASCIIfication)
>
>   type +'a t =
>     | Z when 'a >= [`z]
>     | S when 'a >= [`s] of [< `z | `s] t
>
> allowing 'a in each case to be a supertype rather than equal to the
> specified type.  In that spirit, I tried
>
>   type +_ t =
>     | Z : [> `z] t
>     | S : [< `s | `z] t -> [> `s] t;;
>
> but that does not check for variance either.
>
> Best,
> Petter
>
> [1] https://arxiv.org/abs/1301.2903



-- 
Markus Mottl        http://www.ocaml.info        markus.mottl@gmail.com

  reply	other threads:[~2016-09-19  1:52 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-17 17:38 Markus Mottl
2016-09-18  8:17 ` Petter A. Urkedal
2016-09-19  1:52   ` Markus Mottl [this message]
2016-09-19  8:58     ` octachron
2016-09-19 10:18       ` Mikhail Mandrykin
2016-09-19 13:37         ` Mikhail Mandrykin
2016-09-19 14:46         ` Markus Mottl
2016-09-19 14:53           ` Mikhail Mandrykin
2016-09-19 15:03             ` Markus Mottl
2016-09-20 21:07               ` Markus Mottl
2016-09-21 10:11                 ` Lukasz Stafiniak
2016-09-21 10:14                   ` Lukasz Stafiniak
2016-09-21 17:04                     ` Markus Mottl
2016-09-21 21:40                       ` Gabriel Scherer
2016-09-22  0:39                         ` Markus Mottl
2016-09-24  5:09                           ` Yaron Minsky
2016-10-04 10:33                 ` Jacques Garrigue
2016-09-19 14:39       ` Markus Mottl
2016-09-19 10:05     ` Goswin von Brederlow

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=CAP_800q7jRazR95e4PiFA-9K_3+G2aok_Cvt_8+9bMfVZs9RbQ@mail.gmail.com \
    --to=markus.mottl@gmail.com \
    --cc=caml-list@inria.fr \
    --cc=paurkedal@gmail.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