* [Caml-list] Clever typing for client-server communication?
@ 2015-07-24 11:01 Markus Weißmann
2015-07-24 11:20 ` Nicolas Ojeda Bar
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Markus Weißmann @ 2015-07-24 11:01 UTC (permalink / raw)
To: caml-list
Hello OCaml list,
I'm trying to do something clever regarding the interface for a
communication library:
There is a server and a client which can only send "client" (client to
server) and "server" (server to client) messages.
The current idea is to use a phantom type to annotate the socket as
either being "client to server" or "server to client";
type client
type server
type ('a, 'b) socket
type 'a message
I've got a bunch of functions that only work on either "client
messages", "server messages" or some on both. Something like:
val p1 : 'a message -> int
val p2 : server message -> float
val p3 : client message -> char
You can only send "client messages" on the "client socket" and "server
messages" on the "server socket".
val send : ('a, _) socket -> 'a message -> unit
You can get these messages only on the respective other side.
val recv : (_, 'b) socket -> 'b message
but is there some clever way to only have the socket annotated with one
type while keeping only one send and one recv function?
Something in the spirit of this:
type 'a socket
val send : 'a socket -> 'a message -> unit
val recv : [server socket -> client message | client socket -> server
message]
there is no "(client, client) socket" or "(server, server) socket";
regards
-Markus
PS: Under the hood its basically
type 'a message = bytes
type ('a, 'b) socket = mysocket
But the underlying system allows me to add filters that guarantee me
the aforementioned properties of send/receive.
regards
Markus
--
Markus Weißmann, M.Sc.
Technische Universität München
Institut für Informatik
Boltzmannstr. 3
D-85748 Garching
Germany
http://wwwknoll.in.tum.de/
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Clever typing for client-server communication?
2015-07-24 11:01 [Caml-list] Clever typing for client-server communication? Markus Weißmann
@ 2015-07-24 11:20 ` Nicolas Ojeda Bar
2015-07-24 18:41 ` Mikhail Mandrykin
` (2 subsequent siblings)
3 siblings, 0 replies; 10+ messages in thread
From: Nicolas Ojeda Bar @ 2015-07-24 11:20 UTC (permalink / raw)
To: Markus Weißmann; +Cc: caml-list
[-- Attachment #1: Type: text/plain, Size: 2472 bytes --]
Hi,
This is not exactly what you asked, but you can use GADTs to constrain the
phantom type (_, _) socket:
type client
type server
type (_, _) socket =
| ClientServer : mysocket -> (client, server) socket
| ServerClient : mysocket -> (server, client) socket
Best wishes,
Nicolas
On Fri, Jul 24, 2015 at 1:01 PM, Markus Weißmann <markus.weissmann@in.tum.de
> wrote:
> Hello OCaml list,
>
> I'm trying to do something clever regarding the interface for a
> communication library:
> There is a server and a client which can only send "client" (client to
> server) and "server" (server to client) messages.
> The current idea is to use a phantom type to annotate the socket as either
> being "client to server" or "server to client";
>
> type client
> type server
>
> type ('a, 'b) socket
> type 'a message
>
> I've got a bunch of functions that only work on either "client messages",
> "server messages" or some on both. Something like:
>
> val p1 : 'a message -> int
> val p2 : server message -> float
> val p3 : client message -> char
>
> You can only send "client messages" on the "client socket" and "server
> messages" on the "server socket".
>
> val send : ('a, _) socket -> 'a message -> unit
>
> You can get these messages only on the respective other side.
>
> val recv : (_, 'b) socket -> 'b message
>
> but is there some clever way to only have the socket annotated with one
> type while keeping only one send and one recv function?
> Something in the spirit of this:
>
> type 'a socket
> val send : 'a socket -> 'a message -> unit
> val recv : [server socket -> client message | client socket -> server
> message]
>
> there is no "(client, client) socket" or "(server, server) socket";
>
>
> regards
> -Markus
>
> PS: Under the hood its basically
>
> type 'a message = bytes
> type ('a, 'b) socket = mysocket
>
> But the underlying system allows me to add filters that guarantee me the
> aforementioned properties of send/receive.
>
> regards
> Markus
>
> --
> Markus Weißmann, M.Sc.
> Technische Universität München
> Institut für Informatik
> Boltzmannstr. 3
> D-85748 Garching
> Germany
> http://wwwknoll.in.tum.de/
>
>
> --
> Caml-list mailing list. Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>
[-- Attachment #2: Type: text/html, Size: 3650 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Clever typing for client-server communication?
2015-07-24 11:01 [Caml-list] Clever typing for client-server communication? Markus Weißmann
2015-07-24 11:20 ` Nicolas Ojeda Bar
@ 2015-07-24 18:41 ` Mikhail Mandrykin
2015-07-24 20:23 ` octachron
2015-07-24 20:25 ` Jeremy Yallop
3 siblings, 0 replies; 10+ messages in thread
From: Mikhail Mandrykin @ 2015-07-24 18:41 UTC (permalink / raw)
To: Markus Weißmann; +Cc: caml-list
Hello,
> but is there some clever way to only have the socket annotated with
> one type while keeping only one send and one recv function?
> Something in the spirit of this:
>
> type 'a socket
> val send : 'a socket -> 'a message -> unit
> val recv : [server socket -> client message | client socket -> server
> message]
>
> there is no "(client, client) socket" or "(server, server) socket";
>
The closest solution I can suggest is with a recv signature
val recv : ('a, 'b) recv_t -> 'a socket -> 'b message
(one extra argument while socket is annotated with only one type and
there's only one send and recv function). This uses GADTs:
type 'a socket =
| Client_socket : ... -> client socket
| Server_socket : ... -> server socket
type 'a message =
| Client_message : ... -> client message
| Server_message : ... -> server message
let send = ...
type (_, _) recv_t =
| From_client : (server, client) recv_t
| From_server : (client, server) recv_t
let recv : type a b. (a, b) recv_t -> a socket -> b message =
function
| From_client -> fun Server_socket -> ... Client_message (...)
| From_server -> fun Client_socket -> ... Server_message (...)
The "[server socket -> client message | client socket -> server message]
" (without an extra argument) looks like a dependent type (in spirit of
" 'a socket -> (reverse 'a) message"), which makes me doubt about
whether it's possible in OCaml.
Regards,
Mikhail
On 07/24/2015 02:01 PM, Markus Weißmann wrote:
> Hello OCaml list,
>
> I'm trying to do something clever regarding the interface for a
> communication library:
> There is a server and a client which can only send "client" (client to
> server) and "server" (server to client) messages.
> The current idea is to use a phantom type to annotate the socket as
> either being "client to server" or "server to client";
>
> type client
> type server
>
> type ('a, 'b) socket
> type 'a message
>
> I've got a bunch of functions that only work on either "client
> messages", "server messages" or some on both. Something like:
>
> val p1 : 'a message -> int
> val p2 : server message -> float
> val p3 : client message -> char
>
> You can only send "client messages" on the "client socket" and "server
> messages" on the "server socket".
>
> val send : ('a, _) socket -> 'a message -> unit
>
> You can get these messages only on the respective other side.
>
> val recv : (_, 'b) socket -> 'b message
>
> but is there some clever way to only have the socket annotated with
> one type while keeping only one send and one recv function?
> Something in the spirit of this:
>
> type 'a socket
> val send : 'a socket -> 'a message -> unit
> val recv : [server socket -> client message | client socket -> server
> message]
>
> there is no "(client, client) socket" or "(server, server) socket";
>
>
> regards
> -Markus
>
> PS: Under the hood its basically
>
> type 'a message = bytes
> type ('a, 'b) socket = mysocket
>
> But the underlying system allows me to add filters that guarantee me
> the aforementioned properties of send/receive.
>
> regards
> Markus
>
--
Mikhail Mandrykin
Linux Verification Center, ISPRAS
web: http://linuxtesting.org
e-mail: mandrykin@ispras.ru
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Clever typing for client-server communication?
2015-07-24 11:01 [Caml-list] Clever typing for client-server communication? Markus Weißmann
2015-07-24 11:20 ` Nicolas Ojeda Bar
2015-07-24 18:41 ` Mikhail Mandrykin
@ 2015-07-24 20:23 ` octachron
2015-07-24 20:25 ` Jeremy Yallop
3 siblings, 0 replies; 10+ messages in thread
From: octachron @ 2015-07-24 20:23 UTC (permalink / raw)
To: Markus Weißmann, caml-list
Hello,
> but is there some clever way to only have the socket annotated with
> one type while keeping only one send and one recv function?
> Something in the spirit of this:
>
> type 'a socket
> val send : 'a socket -> 'a message -> unit
> val recv : [server socket -> client message | client socket -> server
> message]
>
> there is no "(client, client) socket" or "(server, server) socket";
>
I see one way to do this, but I am not sure I would call it a clever way.
The idea is to try to encode (-1),(1) and (~-) at the type level. With
this encoding, it is possible
to define send and recv as
type 'a socket
val send : 'n socket -> 'n message -> unit
val recv : 'n socket -> '-n message
A working encoding would be:
type head
type tail
type 'a socket = ... (constraint 'a = 'b * 'c)
type 'a message = ... (constraint 'a = 'b * 'c)
where you can use either gadt or abstract value to enforce that 'a is
only head*tail ( = 1 ) or tail*head ( = -1 ).
Then -( 'a * 'b ) can be translated to ('b *' a), i.e.
val send : 'n socket -> 'n message -> unit
val recv: ('a * 'b) socket -> ('b * 'a) message
With this encoding, you only need to decompose the type parameter of
socket or message when you need to flip it. However, I am not sure that
this slim advantage is worth the added complexity (and one could argue
that I added a type parameter to message rather than removed one from
socket).
Regards,
octachron.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Clever typing for client-server communication?
2015-07-24 11:01 [Caml-list] Clever typing for client-server communication? Markus Weißmann
` (2 preceding siblings ...)
2015-07-24 20:23 ` octachron
@ 2015-07-24 20:25 ` Jeremy Yallop
2015-07-24 20:57 ` Török Edwin
2015-08-08 21:39 ` Markus Mottl
3 siblings, 2 replies; 10+ messages in thread
From: Jeremy Yallop @ 2015-07-24 20:25 UTC (permalink / raw)
To: Markus Weißmann; +Cc: Caml List
On 24 July 2015 at 12:01, Markus Weißmann <markus.weissmann@in.tum.de> wrote:
> but is there some clever way to only have the socket annotated with one type
> while keeping only one send and one recv function?
One way to achieve this is to add some structure to the 'server' and
'client' types, by turning your descriptions of the desired behaviour
into code. For example, you say
> There is a server and a client which can only send "client" (client to
> server) and "server" (server to client) messages.
which you might encode as follows:
type server = < src: server; dst: client; name: [`server] >
and client = < src: client; dst: server; name: [`client] >
These are object types, but there's no need to actually use object
values in your code. The object type syntax is simply a convenient
way to label the different components of the type so that they can be
retrieved later.
Once you have these types you might write type-level functions to
retrieve the components. Here's a function that retrieves the 'src'
component:
type 'a src = 's constraint 'a = < src: 's; .. >
This can be read as follows: src is a function that maps 'a to 's,
where 'a is an object type with a src key, and 's is the value of that
key.
Similarly, here's a function to retrieve the 'dst' component:
type 'a dst = 'd constraint 'a = < dst: 'd; .. >
> You can only send "client messages" on the "client socket" and "server
> messages" on the "server socket".
>
> val send : ('a, _) socket -> 'a message -> unit
>
> You can get these messages only on the respective other side.
>
> val recv : (_, 'b) socket -> 'b message
>
> Something in the spirit of this:
>
> type 'a socket
> val send : 'a socket -> 'a message -> unit
> val recv : [server socket -> client message | client socket -> server
> message]
You might then write
val send : 's socket -> 's src message -> unit
val recv : 's socket -> 's dst message
and OCaml will enforce the constraints you describe.
Kind regards,
Jeremy.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Clever typing for client-server communication?
2015-07-24 20:25 ` Jeremy Yallop
@ 2015-07-24 20:57 ` Török Edwin
2015-07-25 12:42 ` Oleg
2015-08-08 21:39 ` Markus Mottl
1 sibling, 1 reply; 10+ messages in thread
From: Török Edwin @ 2015-07-24 20:57 UTC (permalink / raw)
To: caml-list
On 07/24/2015 11:25 PM, Jeremy Yallop wrote:
> Once you have these types you might write type-level functions to
> retrieve the components. Here's a function that retrieves the 'src'
> component:
>
> type 'a src = 's constraint 'a = < src: 's; .. >
This is very useful to know, thanks!
It'd be interesting to know how you come up with these solutions :)
They are easy to understand as you've explained, but I wouldn't have thought of this solution
although I guessed there had to be a solution with objects (after reading [1]), and I understand (simple) phantom types and (mostly) understand object types and GADTs by now.
Is there some (fundamental) knowledge I'm missing that would allow me to construct solutions to problems like these
(if so could you point to some papers, lecture notes or books please), or is it just that I very rarely use objects/GADTs and lack the experience?
Best regards,
--Edwin
[1] http://stackoverflow.com/questions/10779283/when-should-objects-be-used-in-ocaml/10780681#10780681
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Clever typing for client-server communication?
2015-07-24 20:57 ` Török Edwin
@ 2015-07-25 12:42 ` Oleg
2015-07-25 15:55 ` mandrykin
0 siblings, 1 reply; 10+ messages in thread
From: Oleg @ 2015-07-25 12:42 UTC (permalink / raw)
To: edwin+ml-ocaml; +Cc: caml-list
> > Once you have these types you might write type-level functions to
> > retrieve the components. Here's a function that retrieves the 'src'
> > component:
> >
> > type 'a src = 's constraint 'a = < src: 's; .. >
> This is very useful to know, thanks!
> It'd be interesting to know how you come up with these solutions :)
This is probably a folklore, but it was mentioned on this list more than
a decade ago by Jacques Garrigue. We have used this trick extensively
in
http://okmij.org/ftp/meta-programming/HPC.html#GE-generation
(see the description on p11 of the SCP-metamonads.pdf paper, and many
applications starting from p21)
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Clever typing for client-server communication?
2015-07-25 12:42 ` Oleg
@ 2015-07-25 15:55 ` mandrykin
0 siblings, 0 replies; 10+ messages in thread
From: mandrykin @ 2015-07-25 15:55 UTC (permalink / raw)
To: Oleg, edwin+ml-ocaml, caml-list; +Cc: caml-list-request
> This is probably a folklore, but it was mentioned on this list more
> than
> a decade ago by Jacques Garrigue. We have used this trick extensively
> in
>
> http://okmij.org/ftp/meta-programming/HPC.html#GE-generation
>
For me actually knowing the trick used in the paper alone was not enough
to quickly come up with this solution. I had already used this object
constraints several times in even in real-life code, but that didn't
help much(. To me the key idea here (in the Jeremy Yallop's solution) is
the ability to express the "negation" function on type level with a pair
of mutually-recursive types and a constraint. This can actually be also
done with polymorphic variants instead of object types:
type client = [`cs of server * [`client]]
and server = [`cs of client * [`server]]
type 'a rev = 'b constraint 'a = [`cs of 'b * _] (* rev is the function
*)
This ensures the relations client rev = server, server rev = client and
client <> server. This approach seems powerful enough to express any
finite permutation group, e.g. for 2 elements:
type z = [`t of [`e1] * [`e2] * z] (* initial permutation *)
type 'e e1 = [`t of 'e1 * 'e2 * 'e e1] (* neutral permutation *)
constraint 'e = [`t of 'e1 * 'e2 * _ ]
and 'e e2 = [`t of 'e2 * 'e1 * 'e e2] (* reverse permutation *)
constraint 'e = [`t of 'e1 * 'e2 * _]
type 'a inv = 'b constraint 'a = [`t of _ * _ * 'b]
Here "-e1 - e2 - e3 - ... - en + e1' + e2' + ... + em'" (using `+' for
the group operation and `-' for the inverse) can be encoded as "z e1 e2
... en inv e1' e2' ... em'". By Cayley's theorem (states that every
finite group G is isomorphic to a subgroup of the symmetric group acting
on G) this means (if I got it right) that in fact any finite group
operation (and the corresponding inverse) is encodiable in OCaml as
type-level function!
Regards,
Mikhail
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Clever typing for client-server communication?
2015-07-24 20:25 ` Jeremy Yallop
2015-07-24 20:57 ` Török Edwin
@ 2015-08-08 21:39 ` Markus Mottl
2015-08-09 13:04 ` Jacques Garrigue
1 sibling, 1 reply; 10+ messages in thread
From: Markus Mottl @ 2015-08-08 21:39 UTC (permalink / raw)
To: Jeremy Yallop; +Cc: Markus Weißmann, Caml List
The object type trick is pretty neat and can actually be used with
module types, too. The advantage there is that whereas object types
are "flat", module types support more structure via submodules, e.g.:
module type Bla = sig module Foo : sig type t end end
type 'a t = 'foo_t constraint 'a = (module Bla with type Foo.t = 'foo_t)
Sadly, there is one significant shortcoming here: there does not seem
to be any way to exploit subtyping with module types. Not sure, but
the only reason for this seems to be that values of module type (i.e.
first-class modules) cannot be reasonably implemented with subtyping,
because unlike objects they don't carry around a dictionary at runtime
for looking up entries in the module.
Please correct me if I'm wrong, but from a pure typing perspective I
think that there should be no reason to disallow support for
expressing subtyping with module types. The compiler is already able
to infer whether module signatures satisfy subtyping relationships
anyway.
Would it be possible to support this in the type system? Or is there
already some ingenious hack that can give comparable results with the
existing type system?
Regards,
Markus
On Fri, Jul 24, 2015 at 4:25 PM, Jeremy Yallop <yallop@gmail.com> wrote:
> On 24 July 2015 at 12:01, Markus Weißmann <markus.weissmann@in.tum.de> wrote:
>> but is there some clever way to only have the socket annotated with one type
>> while keeping only one send and one recv function?
>
> One way to achieve this is to add some structure to the 'server' and
> 'client' types, by turning your descriptions of the desired behaviour
> into code. For example, you say
>
>> There is a server and a client which can only send "client" (client to
>> server) and "server" (server to client) messages.
>
> which you might encode as follows:
>
> type server = < src: server; dst: client; name: [`server] >
> and client = < src: client; dst: server; name: [`client] >
>
> These are object types, but there's no need to actually use object
> values in your code. The object type syntax is simply a convenient
> way to label the different components of the type so that they can be
> retrieved later.
>
> Once you have these types you might write type-level functions to
> retrieve the components. Here's a function that retrieves the 'src'
> component:
>
> type 'a src = 's constraint 'a = < src: 's; .. >
>
> This can be read as follows: src is a function that maps 'a to 's,
> where 'a is an object type with a src key, and 's is the value of that
> key.
>
> Similarly, here's a function to retrieve the 'dst' component:
>
> type 'a dst = 'd constraint 'a = < dst: 'd; .. >
>
>> You can only send "client messages" on the "client socket" and "server
>> messages" on the "server socket".
>>
>> val send : ('a, _) socket -> 'a message -> unit
>>
>> You can get these messages only on the respective other side.
>>
>> val recv : (_, 'b) socket -> 'b message
>>
>> Something in the spirit of this:
>>
>> type 'a socket
>> val send : 'a socket -> 'a message -> unit
>> val recv : [server socket -> client message | client socket -> server
>> message]
>
> You might then write
>
> val send : 's socket -> 's src message -> unit
> val recv : 's socket -> 's dst message
>
> and OCaml will enforce the constraints you describe.
>
> Kind regards,
>
> Jeremy.
>
> --
> Caml-list mailing list. Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
--
Markus Mottl http://www.ocaml.info markus.mottl@gmail.com
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Caml-list] Clever typing for client-server communication?
2015-08-08 21:39 ` Markus Mottl
@ 2015-08-09 13:04 ` Jacques Garrigue
0 siblings, 0 replies; 10+ messages in thread
From: Jacques Garrigue @ 2015-08-09 13:04 UTC (permalink / raw)
To: Markus Mottl; +Cc: OCaML List Mailing
On 2015/08/09 06:39, Markus Mottl wrote:
>
> The object type trick is pretty neat and can actually be used with
> module types, too. The advantage there is that whereas object types
> are "flat", module types support more structure via submodules, e.g.:
>
> module type Bla = sig module Foo : sig type t end end
> type 'a t = 'foo_t constraint 'a = (module Bla with type Foo.t = 'foo_t)
>
> Sadly, there is one significant shortcoming here: there does not seem
> to be any way to exploit subtyping with module types. Not sure, but
> the only reason for this seems to be that values of module type (i.e.
> first-class modules) cannot be reasonably implemented with subtyping,
> because unlike objects they don't carry around a dictionary at runtime
> for looking up entries in the module.
>
> Please correct me if I'm wrong, but from a pure typing perspective I
> think that there should be no reason to disallow support for
> expressing subtyping with module types. The compiler is already able
> to infer whether module signatures satisfy subtyping relationships
> anyway.
The problem is that if you allow expression subtyping for module types,
then I see no way to avoid its being applied to … modules. Which we want to
avoid, since the semantics of subtyping for modules is not the same as
for expressions (i.e., modules require coercions).
By the way, you can also define deep structures with object types, so
I don’t see the advantage of using module types. For an example of
nested object structures, you can see lablgtk, with its sub-object approach
for signals.
Jacques Garrigue
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2015-08-09 13:04 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-24 11:01 [Caml-list] Clever typing for client-server communication? Markus Weißmann
2015-07-24 11:20 ` Nicolas Ojeda Bar
2015-07-24 18:41 ` Mikhail Mandrykin
2015-07-24 20:23 ` octachron
2015-07-24 20:25 ` Jeremy Yallop
2015-07-24 20:57 ` Török Edwin
2015-07-25 12:42 ` Oleg
2015-07-25 15:55 ` mandrykin
2015-08-08 21:39 ` Markus Mottl
2015-08-09 13:04 ` 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).