From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail2-relais-roc.national.inria.fr (mail2-relais-roc.national.inria.fr [192.134.164.83]) by sympa.inria.fr (Postfix) with ESMTPS id C51E27F747 for ; Fri, 8 Aug 2014 00:06:35 +0200 (CEST) Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of trevorsummerssmith@gmail.com) identity=pra; client-ip=209.85.214.179; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="trevorsummerssmith@gmail.com"; x-sender="trevorsummerssmith@gmail.com"; x-conformance=sidf_compatible Received-SPF: Pass (mail2-smtp-roc.national.inria.fr: domain of trevorsummerssmith@gmail.com designates 209.85.214.179 as permitted sender) identity=mailfrom; client-ip=209.85.214.179; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="trevorsummerssmith@gmail.com"; x-sender="trevorsummerssmith@gmail.com"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of postmaster@mail-ob0-f179.google.com) identity=helo; client-ip=209.85.214.179; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="trevorsummerssmith@gmail.com"; x-sender="postmaster@mail-ob0-f179.google.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: ArEBABv441PRVdazlGdsb2JhbABRCYNfVwSCK0iteTKZVoFjh0gBgREIFhABAQEBBwsLCRIrhAMBAQEDAQwGEQQZARsSCwEDAQsGAwILDQ0dAgIiAREBBQEKEgYTCAoJB4gLAQMJCA2RTJAhaospgXKDEIo+ChknAwpmhRERAQUOiBCBYYRyVwQHgnmBUgWFAAWJe4YzhmiBVJEqGCmDEByBeCEv X-IPAS-Result: ArEBABv441PRVdazlGdsb2JhbABRCYNfVwSCK0iteTKZVoFjh0gBgREIFhABAQEBBwsLCRIrhAMBAQEDAQwGEQQZARsSCwEDAQsGAwILDQ0dAgIiAREBBQEKEgYTCAoJB4gLAQMJCA2RTJAhaospgXKDEIo+ChknAwpmhRERAQUOiBCBYYRyVwQHgnmBUgWFAAWJe4YzhmiBVJEqGCmDEByBeCEv X-IronPort-AV: E=Sophos;i="5.01,820,1400018400"; d="scan'208";a="88651162" Received: from mail-ob0-f179.google.com ([209.85.214.179]) by mail2-smtp-roc.national.inria.fr with ESMTP/TLS/RC4-SHA; 08 Aug 2014 00:06:34 +0200 Received: by mail-ob0-f179.google.com with SMTP id wn1so3434539obc.24 for ; Thu, 07 Aug 2014 15:06:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=9W9edcWAgXBk2cq2RujP7bvgpi8NtMsCFiT9ynMDpXQ=; b=bLe+aqirpKxT9fSzmTt8wCJ1U44fVNZHi0YyLM/n5cHaCLDPuw+KzUvDtadfPDGE2b 30PI+UqRjYeoWDCYKly4HXRK/YbXS4NtwJny6bPpRKJkbEY9AzSR3mrc/lp7wIV3HiFH 2GReVvVD9WIscKzHBZEhitc9SmSmZ6eboBijmbiB4bYGdWclp2wSut2Us9kEnnsiTYV0 u8KzCY3o0iW3IMKFtildJ5ntWg9WoSpeUTGltqbVlJRZ5SLu+uUoDExW92z/2uqiZpgE leaRHoCGJPVXT+5JQBp7Pu8ofk3yIB/gQKfAyDRFy/mjzuHR4Yqb2O9qsc+VELTgOw6W jxyw== MIME-Version: 1.0 X-Received: by 10.182.118.193 with SMTP id ko1mr26190478obb.45.1407449193410; Thu, 07 Aug 2014 15:06:33 -0700 (PDT) Received: by 10.182.128.100 with HTTP; Thu, 7 Aug 2014 15:06:33 -0700 (PDT) In-Reply-To: <53E24B96.8080402@etorok.net> References: <53E24B96.8080402@etorok.net> Date: Thu, 7 Aug 2014 18:06:33 -0400 Message-ID: From: Trevor Smith To: =?UTF-8?B?VMO2csO2ayBFZHdpbg==?= Cc: caml-list@inria.fr Content-Type: multipart/alternative; boundary=089e0115ea303674db0500114dc3 Subject: Re: [Caml-list] Type Constraints and .mli --089e0115ea303674db0500114dc3 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Edwin, Thank you for your response. You are correct that the "val t" in my example should read "type t". My main goal is one of readability: I want to have large swathes of the codebase only use the immutable interface. A few, but very few, parts of the codebase will use the mutable interface. Ideally, I want the developer reading the .mli files to quickly and easily understand what interfaces do what. The standard way of adding the type constraints add a lot of syntax and make it (slightly) harder to read. My entire question is sort of a nitpick. I am interested in your use of polymorphic variants to deal with this issue: that would actually give better compiler errors than what I am currently implemented. Thank you for the suggestion. I will mull that and see if I think of any downsides to that approach. Trevor On Wed, Aug 6, 2014 at 11:36 AM, T=C3=B6r=C3=B6k Edwin wrote: > On 08/06/2014 03:14 PM, Trevor Smith wrote: > > Hello, > > > > I have a question about using .mli files for increased readability. I > think my question boils down to: "Can one tersely add type constraints to= a > signature defined in a .mli in that same .mli file?" > > > > Detailed problem: You want to have a read interface and a write > interface for the same implementation. > > > > We'll use a trivial example with a character and a name. > > > > module type CharacterSig =3D sig > > val t > > val create : string -> t > > val name : t -> string > > end > > > > module type MutableCharacterSig =3D sig > > val t > > val create : string -> t > > val name : t -> string > > val set_name : t -> string -> unit > > end > > > > module CharacterImpl =3D struct > > type t =3D {name : string ref} > > let create name =3D > > {name =3D ref name } > > let name c =3D !(c.name ) > > let set_name c name =3D > > c.name :=3D name > > end > > > > module Character =3D (CharacterImpl : CharacterSig with type t =3D > CharacterImpl.t) > > module MutableCharacter =3D (CharacterImpl : MutableCharacterSig with t= ype > t =3D CharacterImpl.t) > > > > But what I would like is to specify the read and write signatures in > .mli files for a more readable codebase. > > > > So: > > > > character.mli: > > val t > > shouldn't this be a type? > > > val create : string -> t > > val name : t -> string > > > > mCharacter.mli: > > val t > > val create : string -> t > > val name : t -> string > > val set_name : t -> string -> unit > > > > characterImpl.ml (* ... implementation as above ... *) > > > > However, it is not clear to me that there is a way to attach the type > constraint to character.mli and mCharacter.mli, while keeping the terse > readability of the .mli file. One idea for a solution, would be to > reference a "this" so that the interface could show that it was being > implemented by CharacterImpl, and include the type constraint. > > Not sure I understood exactly what you want to do, but using 'include > module type of' and 'type t =3D CharacterImpl.t' should work: > > character.mli > type t =3D CharacterImpl.t > val create : string -> t > val name : t -> string > > character.ml: > include CharacterImpl > > mCharacter.mli: > include module type of Character > val set_name : t -> string -> unit > > mCharacter.ml: > include CharacterImpl > > characterImpl.ml: > type t =3D {name : string ref} > let create name =3D > {name =3D ref name } > let name c =3D !(c.name) > let set_name c name =3D > c.name :=3D name > > However in this case Character.t =3D MCharacter.t =3D CharacterImpl.t, so= you > won't get the type safety you want > (A Character.t can still be modified by MCharacter.set_name). > > Perhaps it'd be better to use different types, though to_character is not > the identity function: > > character.mli: > type t > val create : string -> t > val name : t -> string > > mCharacter.mli: > include module type of Character > val set_name : t -> string -> unit > val to_character : t -> Character.t > > character.ml: > include MCharacter > > mCharacter.ml: > type t =3D {name : string ref} > let create name =3D > {name =3D ref name } > let name c =3D !(c.name) > let set_name c name =3D > c.name :=3D name > > let to_character x =3D Character.create (name x) > > In fact you should probably take a look at String and Bytes type in OCaml > 4.02 (and the ocaml-bytes compatibility lib for <4.0.2). > > I'd prefer something simpler though: > > character.ml: > type u =3D {name : string ref} > type 'a t =3D u > > let create name =3D > {name =3D ref name } > let create_ro =3D create > let create_rw =3D create > let name c =3D !(c.name) > let set_name c name =3D > c.name :=3D name > let readonly x =3D x > > character.mli: > type 'a t constraint 'a =3D [< `W | `R] > val create_ro : string -> [`R] t > val create_rw : string -> [`R | `W] t > val name : 'a t -> string > val set_name : [> `W] t -> string -> unit > val readonly : [> `R] t -> [`R] t > > Best regards, > --Edwin > > -- > 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 > --089e0115ea303674db0500114dc3 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
Edwin,

Thank you for your response.

You are correct that the "val t" in my exam= ple should read "type t".

My main goal i= s one of readability: I want to have large swathes of the codebase only use= the immutable interface. A few, but very few, parts of the codebase will u= se the mutable interface. Ideally, I want the developer reading the .mli fi= les to quickly and easily understand what interfaces do what. The standard = way of adding the type constraints add a lot of syntax and make it (slightl= y) harder to read. My entire question is sort of a nitpick.

I am interested in your use of polymorphic variants to = deal with this issue: that would actually give better compiler errors than = what I am currently implemented. Thank you for the suggestion. I will mull = that and see if I think of any downsides to that approach.

Trevor


On Wed, Aug 6, 2014 at 11:36 AM, T=C3=B6r=C3=B6k E= dwin <edwin+ml-ocaml@etorok.net> wrote:
On 08/06/2014 03:14 PM, Trev= or Smith wrote:
> Hello,
>
> I have a question about using .mli files for increased readability. I = think my question boils down to: "Can one tersely add type constraints= to a signature defined in a .mli in that same .mli file?"
>
> Detailed problem: You want to have a read interface and a write interf= ace for the same implementation.
>
> We'll use a trivial example with a character and a name.
>
> module type CharacterSig =3D sig
> =C2=A0 val t
> =C2=A0 val create : string -> t
> =C2=A0 val name : t -> string
> end
>
> module type MutableCharacterSig =3D sig
> =C2=A0 val t
> =C2=A0 val create : string -> t
> =C2=A0 val name : t -> string
> =C2=A0 val set_name : t -> string -> unit
> end
>
> module CharacterImpl =3D struct
> =C2=A0 type t =3D {name : string ref}
> =C2=A0 let create name =C2=A0=3D
> =C2=A0 =C2=A0 {name =3D ref name }
> =C2=A0 let name c =3D !(c.name <http://c.na= me/>)
> =C2=A0 let set_name c name =3D
> =C2=A0 =C2=A0 c.name= <http://c.name/>= ; :=3D name
> end
>
> module Character =3D (CharacterImpl : CharacterSig with type t =3D Cha= racterImpl.t)
> module MutableCharacter =3D (CharacterImpl : MutableCharacterSig with = type t =3D CharacterImpl.t)
>
> But what I would like is to specify the read and write signatures in .= mli files for a more readable codebase.
>
> So:
>
> character.mli:
> =C2=A0 val t

shouldn't this be a type?

> =C2=A0 val create : string -> t
> =C2=A0 val name : t -> string
>
> mCharacter.mli:
> =C2=A0 val t
> =C2=A0 val create : string -> t
> =C2=A0 val name : t -> string
> =C2=A0 val set_name : t -> string -> unit
>
> characterImpl.ml (* ... implementation as above ... *)
>
> However, it is not clear to me that there is a way to attach the type = constraint to character.mli and mCharacter.mli, while keeping the terse rea= dability of the .mli file. One idea for a solution, would be to reference a= "this" so that the interface could show that it was being implem= ented by CharacterImpl, and include the type constraint.

Not sure I understood exactly what you want to do, but using 'inc= lude module type of' and 'type t =3D CharacterImpl.t' should wo= rk:

character.mli
type t =3D CharacterImpl.t
val create : string -> t
val name : t -> string

character.ml: include CharacterImpl

mCharacter.mli:
include module type of Character
val set_name : t -> string -> unit

mCharacter.ml:
include CharacterImpl

characterImpl.ml:
type t =3D {name : string ref}
let create name =C2=A0=3D
=C2=A0 {name =3D ref name }
let name c =3D !(c.name)
let set_name c name =3D
=C2=A0 c.name :=3D name

However in this case Character.t =3D MCharacter.t =3D CharacterImpl.t= , so you won't get the type safety you want
(A Character.t can still be modified by MCharacter.set_name).

Perhaps it'd be better to use different types, though to_character is n= ot the identity function:

character.mli:
type t
val create : string -> t
val name : t -> string

mCharacter.mli:
include module type of Character
val set_name : t -> string -> unit
val to_character : t -> Character.t

character.ml:
include MCharacter

mCharacter.ml:
type t =3D {name : string ref}
let create name =C2=A0=3D
=C2=A0 {name =3D ref name }
let name c =3D !(c.name)
let set_name c name =3D
=C2=A0 c.name :=3D name

let to_character x =3D Character.create (name x)

In fact you should probably take a look at String and Bytes type in OCaml 4= .02 (and the ocaml-bytes compatibility lib for <4.0.2).

I'd prefer something simpler though:

character.ml:
type u =3D {name : string ref}
type 'a t =3D u

let create name =C2=A0=3D
=C2=A0 {name =3D ref name }
let create_ro =3D create
let create_rw =3D create
let name c =3D !(c.name)
let set_name c name =3D
=C2=A0 c.name :=3D name
let readonly x =3D x

character.mli:
type 'a t constraint 'a =3D [< `W | `R]
val create_ro : string -> [`R] t
val create_rw : string -> [`R | `W] t
val name : 'a t -> string
val set_name : [> `W] t -> string -> unit
val readonly : [> `R] t -> [`R] t

Best regards,
--Edwin

--
Caml-list mailing list. =C2=A0Subscription management and archives:
ht= tps://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

--089e0115ea303674db0500114dc3--