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örök 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 = sig > > val t > > val create : string -> t > > val name : t -> string > > end > > > > module type MutableCharacterSig = sig > > val t > > val create : string -> t > > val name : t -> string > > val set_name : t -> string -> unit > > end > > > > module CharacterImpl = struct > > type t = {name : string ref} > > let create name = > > {name = ref name } > > let name c = !(c.name ) > > let set_name c name = > > c.name := name > > end > > > > module Character = (CharacterImpl : CharacterSig with type t = > CharacterImpl.t) > > module MutableCharacter = (CharacterImpl : MutableCharacterSig with type > t = 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 = CharacterImpl.t' should work: > > character.mli > type t = 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 = {name : string ref} > let create name = > {name = ref name } > let name c = !(c.name) > let set_name c name = > c.name := name > > However in this case Character.t = MCharacter.t = 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 = {name : string ref} > let create name = > {name = ref name } > let name c = !(c.name) > let set_name c name = > c.name := name > > let to_character x = 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 = {name : string ref} > type 'a t = u > > let create name = > {name = ref name } > let create_ro = create > let create_rw = create > let name c = !(c.name) > let set_name c name = > c.name := name > let readonly x = x > > character.mli: > type 'a t constraint 'a = [< `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 >