caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] Encoding "links" with the type system
@ 2016-09-21 19:12 Andre Nathan
  2016-09-21 22:22 ` Jeremy Yallop
  0 siblings, 1 reply; 9+ messages in thread
From: Andre Nathan @ 2016-09-21 19:12 UTC (permalink / raw)
  To: caml-list


[-- Attachment #1.1: Type: text/plain, Size: 2209 bytes --]

Hi

I'm trying to encode links (an example would be directed graph edges)
using the type system.

The idea is to have types such as

  module Source = struct
    type 'a t = { name : string }
    let create name = { name }
  end

  module Sink = struct
    type 'a t = { name : string }
    let create name = { name }
  end

  module Link = struct
    type ('a, 'b) t = 'a Source.t * 'b Sink.t
  end


and then define a "link set" with the following characteristics:

* You initialize a link set with a sink;
* You can add links to set such that
    * The first link's sink must be of the type of the sink;
    * Additional links can have as sink types the original sink type
      from the set creation, or the source types of previously added
      links.

In other words, if a set is created from a "t1 Sink.t", the first link
on the set must be of type "t2 Source.t * t1 Sink.t", the second link
can be either "t3 Source.t * t1 Sink.t" or "t3 Source.t * t2 Sink.t" and
so on.

Is it possible at all to encode such invariants using the type system? I
couldn't get past something like

  module Set = struct
    type _ t =
      | S : 'a Sink.t -> 'a t
      | L : 'a t * ('b, 'a) Link.t -> ('a * 'b) t
    let create sink = S sink
    let add_link link set = L (set, link)
  end

This allows me to insert the first link:

  type t1
  type t2
  type t3

  let _ =
    let snk1 : t1 Sink.t = Sink.create "sink1" in
    let set = Set.create snk1 in

    let src1 : t2 Source.t = Source.create "source1" in
    let lnk1 = (src1, snk1) in
    let set1 = Set.add_link lnk1 set in
    ...

but now "set1" has type "(t1 * t2) Set.t" and I can't call "add_link" on
it anymore:

    ...
    let src2 : t3 Source.t = Source.create "source2" in
    let lnk2 = (src2, snk1) in
    let set2 = Set.add_link lnk2 set1 in
    ...

which gives me "This expression has type (t1 * t2) Set.t but an
expression was expected of type t1 Set.t" when passing "set1" to "add_link".

I also tried to come up with a solution using difference lists for the
set but hit basically the same problem as above.

Any help would be appreciated.

Thanks,
Andre


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Encoding "links" with the type system
  2016-09-21 19:12 [Caml-list] Encoding "links" with the type system Andre Nathan
@ 2016-09-21 22:22 ` Jeremy Yallop
  2016-09-22  0:50   ` Andre Nathan
  2016-10-07 17:02   ` Shayne Fletcher
  0 siblings, 2 replies; 9+ messages in thread
From: Jeremy Yallop @ 2016-09-21 22:22 UTC (permalink / raw)
  To: Andre Nathan; +Cc: caml-list

Dear Andre,

On 21 September 2016 at 20:12, Andre Nathan <andre@digirati.com.br> wrote:
> I'm trying to encode links (an example would be directed graph edges)
> using the type system.
>
> The idea is to have types such as
>
>   module Source = struct
>     type 'a t = { name : string }
>     let create name = { name }
>   end
>
>   module Sink = struct
>     type 'a t = { name : string }
>     let create name = { name }
>   end
>
>   module Link = struct
>     type ('a, 'b) t = 'a Source.t * 'b Sink.t
>   end
>
>
> and then define a "link set" with the following characteristics:
>
> * You initialize a link set with a sink;
> * You can add links to set such that
>     * The first link's sink must be of the type of the sink;
>     * Additional links can have as sink types the original sink type
>       from the set creation, or the source types of previously added
>       links.
>
> In other words, if a set is created from a "t1 Sink.t", the first link
> on the set must be of type "t2 Source.t * t1 Sink.t", the second link
> can be either "t3 Source.t * t1 Sink.t" or "t3 Source.t * t2 Sink.t" and
> so on.
>
> Is it possible at all to encode such invariants using the type system?

It is!  Here's one approach, which is based on two ideas:

   1. generating a fresh, unique type for each set
      (the paper "Lightweight Static Capabilities" calls these "type
eigenvariables")

   2. an encoding of a set as a collection of evidence about membership.

I'll describe an interface based on these ideas that maintains the
properties you stipulate.  I'll leave the problem of building an
implementation that satisfies the interface to you.  (In fact, the
interface is the tricky part and the implementation is straightforward
if you can treat all the type parameters as phantom.)

First, we'll need types for the objects in the domain: sources, sinks,
sets and links.

  type _ source and _ sink and _ set and ('source, 'sink) link

Besides these we'll also need a type of membership evidence.  A value
of type '(sink, set) accepts' is evidence that 'set' will accept a
link with sink type 'sink':

  type ('sink, 'set) accepts

Then there are two operations on sets: creating a fresh set and adding
a link to a set.  In both cases the operation returns multiple values,
so I'll use records for the return types.  Furthermore, both
operations create fresh types (since sets have unique types), so I'll
use records with existential types.

First, here's the type of the result of the create_set operation:

  type 'sink fresh_set = Fresh_set :{    (* :{  *)
      set: 't set;
      accepts: ('sink, 't) accepts;
    } -> 'sink fresh_set

There's a type parameter 'sink, which'll be instantiated with the type
of the sink used to create the set, and two fields:

   1. 'set' is the new set
   2. 'accepts' is evidence that 'set' will accept a link with sink type 'sink'
      (or, if you like, a capability that will allow us to add such a link)

Note that 'sink is a type parameter, since we need to ensure that it
is the same as another type in the program (the sink used to create
the set), but 't is existential, since we need to ensure that it is
distinct from every other type in the program (since it uniquely
identifies 'set').

The create_set operation builds a fresh set from a sink:

  val create_set : 'sink sink -> 'sink fresh_set

Next, here's the type of the result of the add_link operation:

  type ('sink, 'parent) augmented_set = Augmented_set :{
      set: 't set;
      accepts: ('sink, 't) accepts;
      cc: 's. ('s, 'parent) accepts -> ('s, 't) accepts;
    } -> ('sink, 'parent) augmented_set

This time there are three elements to the result:
   1. 'set' is the new set (as before)
   2. 'accepts' is evidence that 'set' will accept a link with sink
type 'sink' (as before)
   3. 'cc' is a capability converter, that turns evidence that the
"parent" set will accept a link into evidence that 'set' will accept
the same link.

(Another of looking at this is that 'augmented_set' bundles the new
set up along with evidence that 'sink' is a member and evidence that
every member of the parent set is also a member.)

(And it's again worth looking at the scope of the type variables:
'sink and 'parent are parameters, since they correspond to the types
of inputs to the operation;  't is fresh, and 's is
universally-quantified, since the capability converter must work for
any piece of evidence involving the parent.)

The insert_link operation takes three arguments: the link to insert,
the set into which the link is inserted, and evidence that the
insertion is acceptable:

  val insert_link :
    ('source, 'sink) link ->  (* the link *)
    't set ->                 (* the set *)
    ('sink, 't) accepts ->    (* evidence that the set accepts the link *)
    ('source, 't) augmented_set

Here's the interface in action.  Let's assume that we have your sinks,
sources and links:

    type t1 and t2 and t3
    val snk1 : t1 sink
    val src1 : t2 source
    val src2 : t3 source
    val lnk1 : (t2, t1) link
    val lnk2 : (t3, t1) link
    val lnk3 : (t3, t2) link

Let's start by building a set from snk1:

    let Fresh_set { set = set; accepts = a } =
        create_set snk1 in

Now, since 'set' accepts links with sink type 't1' (as attested by
'a') we can insert a new link:

    let Augmented_set { set = set1; accepts = a1; cc = cc1 } =
        insert_link lnk1 set a in

We now have the following evidence available:

    'a' says that 'set' accepts 't1'
    'a1' says that 'set1' accepts 't2'
    'cc1' says that 'set1' accepts anything that 'set' accepts, and so
    'cc1 a' says that set1' accepts 't1'

and so we can insert links with sink type either 't1' or 't2' into 'set1':

    insert_link lnk3 set1 a1
    insert_link lnk2 set1 (cc1 a)

And, of course, since we can only insert links when we have evidence
that the set will accept them, there's no way to perform invalid
insertions.

One drawback of the above is a possible lack of efficiency, mostly
depending on how 'cc' is implemented.  In fact, there's also a
cost-free approach to capability conversions based on evidence
subtyping and private types, but I'll leave it as an exercise for the
reader.

I hope that helps!

Kind regards,

Jeremy.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Encoding "links" with the type system
  2016-09-21 22:22 ` Jeremy Yallop
@ 2016-09-22  0:50   ` Andre Nathan
  2016-09-30 13:54     ` Gabriel Scherer
  2016-10-07 17:02   ` Shayne Fletcher
  1 sibling, 1 reply; 9+ messages in thread
From: Andre Nathan @ 2016-09-22  0:50 UTC (permalink / raw)
  To: Jeremy Yallop; +Cc: caml-list

[-- Attachment #1: Type: text/plain, Size: 336 bytes --]

> Em 21 de set de 2016, às 19:22, Jeremy Yallop <yallop@gmail.com> escreveu:
> It is!  Here's one approach, which is based on two ideas:

Wow, there's a lot to digest here :) Most of it is new to me, including the use of "evidence" types... I'll have to re-read it and try to work it out after a night of sleep.

Thanks,
Andre 

[-- Attachment #2: Type: text/html, Size: 1432 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Encoding "links" with the type system
  2016-09-22  0:50   ` Andre Nathan
@ 2016-09-30 13:54     ` Gabriel Scherer
  2016-10-05 19:46       ` Andre Nathan
  0 siblings, 1 reply; 9+ messages in thread
From: Gabriel Scherer @ 2016-09-30 13:54 UTC (permalink / raw)
  To: Andre Nathan; +Cc: Jeremy Yallop, caml users

[-- Attachment #1: Type: text/plain, Size: 2380 bytes --]

I came back to this thread thanks to Alan Schmitt's Caml Weekly News.

Here is a fairly systematic approach to the problem, contrasting Jeremy's
more virtuoso approach. It works well when the whole set is statically
defined in the source code, but I'm not sure about more dynamic scenarios
(when a lot of the type variables involved become existentially quantified.

The idea is to first formulate a solution using logic programming. A link
set can be represented as a list of accepted sinks, and I can write a small
Prolog program, "insert" that takes a new link, and a link set, and
traverses the link set to find the link's sink in the list, and add the
link's source to the list in passing.

   set([sink]) :- link(source, sink).
   set(sinks) :-
     set(prev_sinks),
     link(source, sink),
     insert(prev_sinks, source, sink, sinks).

   (* either the sink we want to insert is first in the list *)
   insert([sink | _], source, sink, [source, sink | sinks]).

   (* or it is to be found somewhere, recursively, under some other sink
[s] *)
   insert([s | prev_sinks], source, sink, [s | sinks]) :-
     insert(prev_sinks, source, sink, sinks).

Once you have this logic program, it is straightforward to translate it to
a GADT declaration:

type 's linkset =
  | Init : ('source, 'sink) link -> ('source -> ('sink -> unit)) linkset
  | Insert : ('source, 'sink) link * 'set1 linkset * ('set1, 'source,
'sink, 'set2) insert -> 'set2 linkset
and ('set1, 'source, 'sink, 'set2) insert =
  | Here : (('sink -> _) as 'set1, 'source, 'sink, 'source -> 'set1) insert
  | Next : ('set1, 'source, 'sink, 'set2) insert -> ('a -> 'set1, 'source,
'sink, 'a -> 'set2) insert

let _ = fun (type t1 t2 t3) (lnk1 : (t2, t1) link) (lnk2 : (t3, t1) link)
(lnk3 : (t3, t2) link) ->
  let set = Init lnk1 in
  let set = Insert (lnk2, set, Next Here) in
  let set = Insert (lnk3, set, Here) in
  set


On Wed, Sep 21, 2016 at 8:50 PM, Andre Nathan <andre@digirati.com.br> wrote:

> Em 21 de set de 2016, às 19:22, Jeremy Yallop <yallop@gmail.com> escreveu:
>
> It is!  Here's one approach, which is based on two ideas:
>
>
> Wow, there's a lot to digest here :) Most of it is new to me, including
> the use of "evidence" types... I'll have to re-read it and try to work it
> out after a night of sleep.
>
> Thanks,
> Andre
>
>

[-- Attachment #2: Type: text/html, Size: 4041 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Encoding "links" with the type system
  2016-09-30 13:54     ` Gabriel Scherer
@ 2016-10-05 19:46       ` Andre Nathan
  2016-10-05 20:15         ` Daniel Bünzli
  0 siblings, 1 reply; 9+ messages in thread
From: Andre Nathan @ 2016-10-05 19:46 UTC (permalink / raw)
  To: caml-list


[-- Attachment #1.1: Type: text/plain, Size: 3814 bytes --]

Hi Gabriel

This is an interesting way to derive the GADT, at least for me, as I
have never did any logic programming. The resulting API is a bit easier
to use than the one from Jeremy's idea, at least for my use case.

I'm using this idea to experiment with a type-safe SQL query builder,
which so far looks like this:

  from Team.table
    |> belonging_to Team.owner   Here
    |> having_one Project.leader Here
    |> select
        |> fields [field User.id; field User.name] (Next Here)
        |> fields [field Team.id; field Team.name] (Next (Next Here))
        |> fields [all Project.table]              Here

This generates the following query:

  SELECT
    users.id, users.name,
    teams.id, teams.name,
    projects.*
  FROM
    teams
  LEFT JOIN
    users ON users.id = teams.owner_id
  LEFT JOIN
    projects ON projects.leader_id = users.id

I'm not sure if this will ever be of use in practice, as the Next/Here
stuff might be too complicated for users, but overall I'm quite happy
with the result and what I've learned, though of course it's far from
complete (e.g. no support for WHERE clauses).

Thanks for the hints!

Andre

On 09/30/2016 10:54 AM, Gabriel Scherer wrote:
> I came back to this thread thanks to Alan Schmitt's Caml Weekly News.
> 
> Here is a fairly systematic approach to the problem, contrasting
> Jeremy's more virtuoso approach. It works well when the whole set is
> statically defined in the source code, but I'm not sure about more
> dynamic scenarios (when a lot of the type variables involved become
> existentially quantified.
> 
> The idea is to first formulate a solution using logic programming. A
> link set can be represented as a list of accepted sinks, and I can write
> a small Prolog program, "insert" that takes a new link, and a link set,
> and traverses the link set to find the link's sink in the list, and add
> the link's source to the list in passing.
> 
>    set([sink]) :- link(source, sink).
>    set(sinks) :-
>      set(prev_sinks),
>      link(source, sink),
>      insert(prev_sinks, source, sink, sinks).
> 
>    (* either the sink we want to insert is first in the list *)
>    insert([sink | _], source, sink, [source, sink | sinks]).
> 
>    (* or it is to be found somewhere, recursively, under some other sink
> [s] *)
>    insert([s | prev_sinks], source, sink, [s | sinks]) :-
>      insert(prev_sinks, source, sink, sinks).
> 
> Once you have this logic program, it is straightforward to translate it
> to a GADT declaration:
> 
> type 's linkset =
>   | Init : ('source, 'sink) link -> ('source -> ('sink -> unit)) linkset
>   | Insert : ('source, 'sink) link * 'set1 linkset * ('set1, 'source,
> 'sink, 'set2) insert -> 'set2 linkset
> and ('set1, 'source, 'sink, 'set2) insert =
>   | Here : (('sink -> _) as 'set1, 'source, 'sink, 'source -> 'set1) insert
>   | Next : ('set1, 'source, 'sink, 'set2) insert -> ('a -> 'set1,
> 'source, 'sink, 'a -> 'set2) insert
> 
> let _ = fun (type t1 t2 t3) (lnk1 : (t2, t1) link) (lnk2 : (t3, t1)
> link) (lnk3 : (t3, t2) link) ->
>   let set = Init lnk1 in
>   let set = Insert (lnk2, set, Next Here) in
>   let set = Insert (lnk3, set, Here) in
>   set
> 
> 
> On Wed, Sep 21, 2016 at 8:50 PM, Andre Nathan <andre@digirati.com.br
> <mailto:andre@digirati.com.br>> wrote:
> 
>     Em 21 de set de 2016, às 19:22, Jeremy Yallop <yallop@gmail.com
>     <mailto:yallop@gmail.com>> escreveu:
>>     It is!  Here's one approach, which is based on two ideas:
> 
>     Wow, there's a lot to digest here :) Most of it is new to me,
>     including the use of "evidence" types... I'll have to re-read it and
>     try to work it out after a night of sleep.
> 
>     Thanks,
>     Andre 
> 
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Encoding "links" with the type system
  2016-10-05 19:46       ` Andre Nathan
@ 2016-10-05 20:15         ` Daniel Bünzli
  0 siblings, 0 replies; 9+ messages in thread
From: Daniel Bünzli @ 2016-10-05 20:15 UTC (permalink / raw)
  To: Andre Nathan; +Cc: caml-list

On Wednesday 5 October 2016 at 21:46, Andre Nathan wrote:
> I'm using this idea to experiment with a type-safe SQL query builder,
> which so far looks like this:

You may be interested in looking at this 

http://okmij.org/ftp/meta-programming/#QUEL

Best,

Daniel

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Encoding "links" with the type system
  2016-09-21 22:22 ` Jeremy Yallop
  2016-09-22  0:50   ` Andre Nathan
@ 2016-10-07 17:02   ` Shayne Fletcher
  2016-10-08 21:51     ` Shayne Fletcher
  1 sibling, 1 reply; 9+ messages in thread
From: Shayne Fletcher @ 2016-10-07 17:02 UTC (permalink / raw)
  To: Jeremy Yallop; +Cc: Andre Nathan, caml-list@inria.fr users

[-- Attachment #1: Type: text/plain, Size: 1677 bytes --]

On Wed, Sep 21, 2016 at 6:22 PM, Jeremy Yallop <yallop@gmail.com> wrote:

> I'll describe an interface based on these ideas that maintains the
> properties you stipulate.  I'll leave the problem of building an
> implementation that satisfies the interface to you.  (In fact, the
> interface is the tricky part and the implementation is straightforward
> if you can treat all the type parameters as phantom.)
>

​How's this?

module M : S = struct

  type _ sink = { name : string }
  type _ source = { name : string }
  type _ set = (string * string) list
  type ('source, 'sink) link = ('source source * 'sink sink)

  type ('sink, 'set) accepts =
  | Accepts : ('sink, 'set) accepts

  type 'sink fresh_set =
  | Fresh_set : {
    set : 't set;
    accepts : ('sink, 't) accepts;
    }                        -> 'sink fresh_set

  let create_set (s : 'sink sink) : 'sink fresh_set =
    Fresh_set { set = ([] : 't set);
                accepts = (Accepts : ('sink, 't) accepts) }

  type ('sink, 'parent) augmented_set =
  | Augmented_set : {
    set : 't set;
    accepts: ('sink, 't) accepts;
    cc : 's. ('s, 'parent) accepts -> ('s, 't) accepts
  } -> ('sink, 'parent) augmented_set

  let insert_link
      (l : ('source, 'sink) link)
      (s : 't set)
      (a : ('sink, 't) accepts)  : ('source, 't) augmented_set =
    let ({name = src}, {name = dst}) = l in
    Augmented_set {
      set : 'tt set = (src, dst) :: s;
      accepts = (Accepts : ('source, 'tt) accepts);
      cc = fun (_ : (_, 't) accepts) -> (Accepts : (_, 't) accepts)
    }

end
​
​This melts my brain Jeremy! :)

-- 
Shayne Fletcher

[-- Attachment #2: Type: text/html, Size: 5339 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Encoding "links" with the type system
  2016-10-07 17:02   ` Shayne Fletcher
@ 2016-10-08 21:51     ` Shayne Fletcher
  2016-10-11  0:16       ` Jacques Garrigue
  0 siblings, 1 reply; 9+ messages in thread
From: Shayne Fletcher @ 2016-10-08 21:51 UTC (permalink / raw)
  To: Jeremy Yallop; +Cc: Andre Nathan, caml-list@inria.fr users

[-- Attachment #1: Type: text/plain, Size: 5922 bytes --]

On Fri, Oct 7, 2016 at 1:02 PM, Shayne Fletcher <
shayne.fletcher.50@gmail.com> wrote:

> On Wed, Sep 21, 2016 at 6:22 PM, Jeremy Yallop <yallop@gmail.com> wrote:
>
>> I'll describe an interface based on these ideas that maintains the
>> properties you stipulate.  I'll leave the problem of building an
>> implementation that satisfies the interface to you.  (In fact, the
>> interface is the tricky part and the implementation is straightforward
>> if you can treat all the type parameters as phantom.)
>>
>
> ​How's this?
>
> module M : S = struct
> ...
>
​
I've hit a problem with my attempt at an implementation actually :(

The issue is, the program below as written seems to work, but, if the type
[_ set] is made abstract in the signature [S], typing breaks. Can somebody
explain where I'm going wrong?

module type S = sig

  type _ sink
  type _ source
  type _ set = (string * string) list (*Why can't this be abstract?*)
  type ('source, 'sink) link

  type ('sink, 'set) accepts

  val mk_sink : string -> 'sink sink
  val mk_source : string -> 'source source
  val mk_link : 'source source * 'sink sink -> ('source, 'sink) link

  type 'sink fresh_set =
  | Fresh_set : {
    set : 'set set;
    accepts : ('sink, 'set) accepts;
  } -> 'sink fresh_set

  val create_set : 'sink sink -> 'sink fresh_set

  type ('sink, 'parent) augmented_set =
  | Augmented_set : {
    set : 'set set;
    accepts: ('sink, 'set) accepts;
    cc : 's. ('s, 'parent) accepts -> ('s, 'set) accepts
  } -> ('sink, 'parent) augmented_set

  val insert_link :
    ('source, 'sink) link ->
    'parent set ->
    ('sink, 'parent) accepts ->
    ('source, 'parent) augmented_set

end

module M : S = struct

  type 'sink sink = { name : string }
  type 'source source = { name : string }

  type 'set set = (string * string) list
  type ('source, 'sink) link = ('source source * 'sink sink)

  let mk_sink (name : string) : 'sink sink = {name}
  let mk_source (name : string) : 'source source = {name}
  let mk_link ((source, sink) : 'source source * 'sink sink)
      : ('source, 'sink) link = (source, sink)

  type ('sink, 'set) accepts =
  | Accepts : ('sink, 'set) accepts

  type 'sink fresh_set =
  | Fresh_set : {
    set : 'set set;
    accepts : ('sink, 'set) accepts;
    }                        -> 'sink fresh_set

  let create_set (s : 'sink sink) : 'sink fresh_set =
    Fresh_set { set = ([] : 'set set);
                accepts = (Accepts : ('sink, 'set) accepts) }

  type ('sink, 'parent) augmented_set =
  | Augmented_set : {
    set : 't set;
    accepts: ('sink, 't) accepts;
    cc : 's. ('s, 'parent) accepts -> ('s, 't) accepts
  } -> ('sink, 'parent) augmented_set

  let insert_link
      (l : ('source, 'sink) link)
      (s : 'parent set)
      (a : ('sink, 'parent) accepts)  : ('source, 'parent) augmented_set =
    let {name = src} : 'source source = fst l in
    let {name = dst} : 'sink sink  = snd l in
    Augmented_set {
      set : 't set = (src, dst) :: s;
      accepts = (Accepts : ('source, 't) accepts);
      cc = fun (_ : (_, 'parent) accepts) -> (Accepts : (_, 'parent)
accepts)
    }

end

module Test (E : S) = struct

  open E

  type t1 and t2 and t3 and t4

  let snk1 : t1 sink = mk_sink "sink1"
  let snk2 : t2 sink = mk_sink "sink2"
  let snk3 : t4 sink = mk_sink "sink3"

  let src1 : t2 source = mk_source "source1"
  let src2 : t3 source = mk_source "source2"

  let link1 : (t2,  t1) link = mk_link (src1, snk1) (*t2 src, t1 sink*)
  let link2 : (t3,  t1) link = mk_link (src2, snk1) (*t3 src, t1 sink*)
  let link3 : (t3,  t2) link = mk_link (src2, snk2) (*t3 src, t2 sink*)
  let link4 : (t3,  t4) link = mk_link (src2, snk3) (*t3 src, t4 sink*)

  let test () =

    (*Create a fresh set from a sink of type [t1]*)
    let (Fresh_set {set; accepts = a} : t1 fresh_set) =
      create_set snk1 in
    (*
      - [a] is evidence [set] accepts links with sink type [t1]
    *)

    (*Insert a [(t2, t1) link]*)
    let Augmented_set
        {set = set1; accepts = a1; cc = cc1} =
      insert_link link1 set a in
    (*
      - [a1] is evidence [set1] accepts links with sink type [t2] ([t2] is
        the source type of [link1])
      - [cc] says that [set1] accepts links with sink types that its
        parent [set] does:
        - [cc1 a] provides evidence that says that [set1] will accept
          [link2] which has sink type [t1] *)

    (*Insert a [(t3, t1)] link*)
    let Augmented_set
        {set = set2; accepts = a2; cc = cc2} =
      insert_link link2 set (cc1 a) in
    (*
      - [a2] says that [set2] accepts links with sink type [t3] ([t3] is
        the source type of [link2])
        - [cc2] says that [set2] accepts links with sink types that its
          parent does:
        - [cc2 a1] provides evidence that says that [set2] will accept
          [link3] which has sink type [t2]
    *)

    (*Insert a [(t3, t2)] link*)
    let (Augmented_set
        {set = set3; accepts = a3; cc = cc3} : (t3, _) augmented_set) =
      insert_link link3 set (cc2 a1) in
    (*
      - [a3] says that [set3] accepts links with sink type [t3] ([t3]is
        the source type of [link3])
      - [cc3] says that [set3] accepts links with sink types that its
        parent does (that is, any links with sink types [t1], [t2] or [t3])
    *)

    (*There is just no way we can get insert [link4] into [set3]. The
      is no evidence we can produce that will allow a link with sink
      type [t4]. Try the below with any of [a1], [a2], [a3])*)
    (*
    let (Augmented_set
       {set = set4; accepts = a4; cc = cc4} =
       insert_link link4 set (cc3 a3) : (t3, _) augmented_set) in
    *)

    ()
end

let _ = let module T = Test (M) in T.test ()

-- 
Shayne Fletcher

[-- Attachment #2: Type: text/html, Size: 19233 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Caml-list] Encoding "links" with the type system
  2016-10-08 21:51     ` Shayne Fletcher
@ 2016-10-11  0:16       ` Jacques Garrigue
  0 siblings, 0 replies; 9+ messages in thread
From: Jacques Garrigue @ 2016-10-11  0:16 UTC (permalink / raw)
  To: Shayne Fletcher; +Cc: Jeremy Yallop, Andre Nathan, OCaML List Mailing

On 2016/10/09 06:51, Shayne Fletcher wrote:
> 
> On Fri, Oct 7, 2016 at 1:02 PM, Shayne Fletcher <shayne.fletcher.50@gmail.com> wrote:
> On Wed, Sep 21, 2016 at 6:22 PM, Jeremy Yallop <yallop@gmail.com> wrote:
> I'll describe an interface based on these ideas that maintains the
> properties you stipulate.  I'll leave the problem of building an
> implementation that satisfies the interface to you.  (In fact, the
> interface is the tricky part and the implementation is straightforward
> if you can treat all the type parameters as phantom.)
> 
> ​How's this?
> 
> module M : S = struct
> ...
> ​
> I've hit a problem with my attempt at an implementation actually :(
> 
> The issue is, the program below as written seems to work, but, if the type [_ set] is made abstract in the signature [S], typing breaks. Can somebody explain where I'm going wrong?

Making a type abstract loses some information (independently of hiding the precise definition):
* its uniqueness (i.e. it could be equal to any other type)
* its injectivity (i.e. whether "t1 t = t2 t” implies “t1 = t2”)
* its contractiveness (i.e. whether “t1 t” might actually be equal to to “t1”)
All of these properties have an impact of the behavior of type indices of GADTs.

If you want to use a type as GADT index, it is fine to hide the actual implementation, but it
is better to have a visible constructor.
For instance, you could define in the interface:

	type set_impl
	type _ set = private Set of set_impl  (* private ensure that the type parameter is invariant *)

and in the implementation:

	type set_impl = (string * string) list
	type _ set = Set of set_impl

Jacques

> module type S = sig
> 
>   type _ sink
>   type _ source
>   type _ set = (string * string) list (*Why can't this be abstract?*)
>   type ('source, 'sink) link
> 
>   type ('sink, 'set) accepts 
> 
>   val mk_sink : string -> 'sink sink
>   val mk_source : string -> 'source source
>   val mk_link : 'source source * 'sink sink -> ('source, 'sink) link
> 
>   type 'sink fresh_set = 
>   | Fresh_set : {
>     set : 'set set;
>     accepts : ('sink, 'set) accepts;
>   } -> 'sink fresh_set
> 
>   val create_set : 'sink sink -> 'sink fresh_set
> 
>   type ('sink, 'parent) augmented_set =
>   | Augmented_set : {
>     set : 'set set;
>     accepts: ('sink, 'set) accepts;
>     cc : 's. ('s, 'parent) accepts -> ('s, 'set) accepts
>   } -> ('sink, 'parent) augmented_set
> 
>   val insert_link : 
>     ('source, 'sink) link ->
>     'parent set -> 
>     ('sink, 'parent) accepts ->
>     ('source, 'parent) augmented_set
> 
> end
> 
> module M : S = struct
> 
>   type 'sink sink = { name : string }
>   type 'source source = { name : string }
> 
>   type 'set set = (string * string) list
>   type ('source, 'sink) link = ('source source * 'sink sink)
> 
>   let mk_sink (name : string) : 'sink sink = {name}
>   let mk_source (name : string) : 'source source = {name}
>   let mk_link ((source, sink) : 'source source * 'sink sink) 
>       : ('source, 'sink) link = (source, sink)
> 
>   type ('sink, 'set) accepts = 
>   | Accepts : ('sink, 'set) accepts
> 
>   type 'sink fresh_set = 
>   | Fresh_set : {
>     set : 'set set;
>     accepts : ('sink, 'set) accepts; 
>     }                        -> 'sink fresh_set
> 
>   let create_set (s : 'sink sink) : 'sink fresh_set =
>     Fresh_set { set = ([] : 'set set); 
>                 accepts = (Accepts : ('sink, 'set) accepts) }
>   
>   type ('sink, 'parent) augmented_set =
>   | Augmented_set : {
>     set : 't set;
>     accepts: ('sink, 't) accepts;
>     cc : 's. ('s, 'parent) accepts -> ('s, 't) accepts
>   } -> ('sink, 'parent) augmented_set
> 
>   let insert_link 
>       (l : ('source, 'sink) link) 
>       (s : 'parent set)
>       (a : ('sink, 'parent) accepts)  : ('source, 'parent) augmented_set =
>     let {name = src} : 'source source = fst l in
>     let {name = dst} : 'sink sink  = snd l in
>     Augmented_set {
>       set : 't set = (src, dst) :: s;
>       accepts = (Accepts : ('source, 't) accepts);
>       cc = fun (_ : (_, 'parent) accepts) -> (Accepts : (_, 'parent) accepts)
>     }
> 
> end
> 
> module Test (E : S) = struct
> 
>   open E
> 
>   type t1 and t2 and t3 and t4
> 
>   let snk1 : t1 sink = mk_sink "sink1"
>   let snk2 : t2 sink = mk_sink "sink2"
>   let snk3 : t4 sink = mk_sink "sink3"
> 
>   let src1 : t2 source = mk_source "source1"
>   let src2 : t3 source = mk_source "source2"
> 
>   let link1 : (t2,  t1) link = mk_link (src1, snk1) (*t2 src, t1 sink*)
>   let link2 : (t3,  t1) link = mk_link (src2, snk1) (*t3 src, t1 sink*)
>   let link3 : (t3,  t2) link = mk_link (src2, snk2) (*t3 src, t2 sink*)
>   let link4 : (t3,  t4) link = mk_link (src2, snk3) (*t3 src, t4 sink*)
> 
>   let test () = 
> 
>     (*Create a fresh set from a sink of type [t1]*)
>     let (Fresh_set {set; accepts = a} : t1 fresh_set) = 
>       create_set snk1 in
>     (*
>       - [a] is evidence [set] accepts links with sink type [t1]
>     *)
> 
>     (*Insert a [(t2, t1) link]*)
>     let Augmented_set 
>         {set = set1; accepts = a1; cc = cc1} = 
>       insert_link link1 set a in
>     (*
>       - [a1] is evidence [set1] accepts links with sink type [t2] ([t2] is
>         the source type of [link1])
>       - [cc] says that [set1] accepts links with sink types that its
>         parent [set] does:
>         - [cc1 a] provides evidence that says that [set1] will accept
>           [link2] which has sink type [t1] *)
> 
>     (*Insert a [(t3, t1)] link*)
>     let Augmented_set
>         {set = set2; accepts = a2; cc = cc2} =
>       insert_link link2 set (cc1 a) in
>     (*
>       - [a2] says that [set2] accepts links with sink type [t3] ([t3] is
>         the source type of [link2])
>         - [cc2] says that [set2] accepts links with sink types that its
>           parent does:
>         - [cc2 a1] provides evidence that says that [set2] will accept
>           [link3] which has sink type [t2]
>     *)
> 
>     (*Insert a [(t3, t2)] link*)
>     let (Augmented_set
>         {set = set3; accepts = a3; cc = cc3} : (t3, _) augmented_set) =
>       insert_link link3 set (cc2 a1) in
>     (*
>       - [a3] says that [set3] accepts links with sink type [t3] ([t3]is
>         the source type of [link3])
>       - [cc3] says that [set3] accepts links with sink types that its
>         parent does (that is, any links with sink types [t1], [t2] or [t3])
>     *)
> 
>     (*There is just no way we can get insert [link4] into [set3]. The
>       is no evidence we can produce that will allow a link with sink
>       type [t4]. Try the below with any of [a1], [a2], [a3])*)
>     (*
>     let (Augmented_set
>        {set = set4; accepts = a4; cc = cc4} =
>        insert_link link4 set (cc3 a3) : (t3, _) augmented_set) in
>     *)
> 
>     ()
> end
> 
> let _ = let module T = Test (M) in T.test ()




^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2016-10-11  0:16 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-21 19:12 [Caml-list] Encoding "links" with the type system Andre Nathan
2016-09-21 22:22 ` Jeremy Yallop
2016-09-22  0:50   ` Andre Nathan
2016-09-30 13:54     ` Gabriel Scherer
2016-10-05 19:46       ` Andre Nathan
2016-10-05 20:15         ` Daniel Bünzli
2016-10-07 17:02   ` Shayne Fletcher
2016-10-08 21:51     ` Shayne Fletcher
2016-10-11  0:16       ` Jacques Garrigue

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