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 mail3-relais-sop.national.inria.fr (mail3-relais-sop.national.inria.fr [192.134.164.104]) by sympa.inria.fr (Postfix) with ESMTPS id 7D1097F747 for ; Wed, 6 Aug 2014 17:36:45 +0200 (CEST) Received-SPF: None (mail3-smtp-sop.national.inria.fr: no sender authenticity information available from domain of edwin+ml-ocaml@etorok.net) identity=pra; client-ip=62.113.205.31; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="edwin+ml-ocaml@etorok.net"; x-sender="edwin+ml-ocaml@etorok.net"; x-conformance=sidf_compatible Received-SPF: Pass (mail3-smtp-sop.national.inria.fr: domain of edwin+ml-ocaml@etorok.net designates 62.113.205.31 as permitted sender) identity=mailfrom; client-ip=62.113.205.31; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="edwin+ml-ocaml@etorok.net"; x-sender="edwin+ml-ocaml@etorok.net"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: Pass (mail3-smtp-sop.national.inria.fr: domain of postmaster@mx.etorok.net designates 62.113.205.31 as permitted sender) identity=helo; client-ip=62.113.205.31; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="edwin+ml-ocaml@etorok.net"; x-sender="postmaster@mx.etorok.net"; x-conformance=sidf_compatible; x-record-type="v=spf1" X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AmQFAH1K4lM+cc0f/2dsb2JhbABRCYMNUleCd8kuh0wBgRQWd4QDAQEEASMEGTgCBAsLGAICBRMDCwICCQMCAQIBRRMIAheIHwyrY3eFCJFAEQaBLIhThHIoOoJ5gVKLEooehmeBVJMRgV0bgV86Lw X-IPAS-Result: AmQFAH1K4lM+cc0f/2dsb2JhbABRCYMNUleCd8kuh0wBgRQWd4QDAQEEASMEGTgCBAsLGAICBRMDCwICCQMCAQIBRRMIAheIHwyrY3eFCJFAEQaBLIhThHIoOoJ5gVKLEooehmeBVJMRgV0bgV86Lw X-IronPort-AV: E=Sophos;i="5.01,812,1400018400"; d="scan'208";a="74131890" Received: from mx.etorok.net ([62.113.205.31]) by mail3-smtp-sop.national.inria.fr with ESMTP/TLS/DHE-RSA-AES256-SHA; 06 Aug 2014 17:36:44 +0200 Received: by mx.etorok.net (OpenSMTPD) with ESMTP id aea44da6; for ; Wed, 6 Aug 2014 18:36:42 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=etorok.net; h= message-id:date:from:mime-version:to:references:in-reply-to :content-type:content-transfer-encoding; s=ml; l=3806; bh=eUPfEi DYdsayDZDgA5p+5TaWeuU=; b=ThQ1eZ/ByQ6TA/r9JjKqRWRg2EJ754p7Eb5CJJ UilqyxkhiMFpxKpMgwEvyvIK29wLNYDIJjT5GtXT78aPHz3TF2BnNtjM1m0CRCB2 Z0LDETVR23iS7WUyT+nBpgBQXet/uTq9P2uKjkkbN410i/xYkAxFd+9eIEprPX01 vjfiY= Received: by mx.etorok.net (OpenSMTPD) with ESMTPSA id 0b390673; TLS version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NO; for ; Wed, 6 Aug 2014 18:36:41 +0300 (EEST) Message-ID: <53E24B96.8080402@etorok.net> Date: Wed, 06 Aug 2014 18:36:54 +0300 From: =?UTF-8?B?VMO2csO2ayBFZHdpbg==?= User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Icedove/31.0 MIME-Version: 1.0 To: caml-list@inria.fr References: In-Reply-To: Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Subject: Re: [Caml-list] Type Constraints and .mli 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