From: Goswin von Brederlow <goswin-v-b@web.de>
To: caml-list@inria.fr
Subject: Re: [Caml-list] Covariant GADTs
Date: Mon, 19 Sep 2016 12:05:58 +0200 [thread overview]
Message-ID: <20160919100558.GC27810@frosties> (raw)
In-Reply-To: <CAP_800q7jRazR95e4PiFA-9K_3+G2aok_Cvt_8+9bMfVZs9RbQ@mail.gmail.com>
On Sun, Sep 18, 2016 at 09:52:24PM -0400, Markus Mottl wrote:
> 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
I think here you are trying to encode into the type system if the
next/prev point to Empty or Elt. But you don't encode this information
into the resulting type at all. How will you know the type of prev/next
from an Elt?
Problem is that you then need to do that recursively. You not only
need to know if next is Empty or Elt but how many Elt there are before
you hit Empty and in both directions. E.g.
('el, [`elt] -> [`empty], [`elt] -> [`elt] -> [`empty]) t
Also inserting into the list would change the type of every item in
the list. Since some other item could be stored somewhere it's type
would change and we can't have that. That would rule out using
mutables and you would have to copy the whole list on every change.
Seems to me like you are on a dead end here.
> 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
MfG
Goswin
prev parent reply other threads:[~2016-09-19 10:06 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
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 [this message]
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=20160919100558.GC27810@frosties \
--to=goswin-v-b@web.de \
--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