On 08/06/2014 03:14 PM, Trevor Smith wrote:> let name c = !(c.name <http://c.name/>)
> 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 set_name c name => c.name <http://c.name/> := name
> endshouldn't this be a type?
>
> 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
Not sure I understood exactly what you want to do, but using 'include module type of' and 'type t = CharacterImpl.t' should work:
> 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.
character.mli
type t = CharacterImpl.t
val create : string -> tcharacter.ml:
val name : t -> string
include CharacterImpl
mCharacter.mli:
include module type of Character
val set_name : t -> string -> unitmCharacter.ml:
include CharacterImpl
characterImpl.ml:
type t = {name : string ref}However in this case Character.t = MCharacter.t = CharacterImpl.t, so you won't get the type safety you want
let create name =
{name = ref name }
let name c = !(c.name)
let set_name c name =
c.name := name
(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 tmCharacter.mli:
val create : string -> t
val name : t -> string
include module type of Character
val set_name : t -> string -> unitval to_character : t -> Character.t
character.ml:
include MCharacter
mCharacter.ml:
type t = {name : string ref}let to_character x = Character.create (name x)
let create name =
{name = ref name }
let name c = !(c.name)
let set_name c name =
c.name := name
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_ro = create
let create name =
{name = ref name }
let create_rw = create
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