caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: pierrchp@free.fr
To: David Allsopp <dra-news@metastack.com>
Cc: OCaml List <caml-list@inria.fr>
Subject: Re: [Caml-list] Module design
Date: Sat, 20 Aug 2011 18:39:43 +0200	[thread overview]
Message-ID: <1313858383.4e4fe34f2d35e@imp.free.fr> (raw)
In-Reply-To: <000701cc5f45$caa14310$5fe3c930$@metastack.com>

Sorry about the duplicates, but I didn't "reply all".

Here is how I would do it:

type 'a connection =  {connection_id:'a ;
                      (*some other things that needs to be defined*)
                       }
                      (* type of connections *)
let make_connection connid = {connection_id = connid;
                            (*some other things that needs to be defined*)
			     }

module type CONN =
  sig
  val connect: ('a connection -> bool) option
  val disconnect: ('a connection -> unit) option
end

module type BACKEND = functor (C:CONN) ->
sig
val connect:'a connection -> bool
val disconnect:'a connection -> unit
end


module Backend : BACKEND = functor (C:CONN) ->
 struct

   let default_connect c= false (*implement default connect*)
   let default_disconnect c= () (* implement default disconnect *)

   let connect c = match C.connect with
          None -> default_connect c
         |Some f -> f c
   let disconnect c= match C.disconnect with
          None -> default_disconnect c
         |Some f -> f c
 end

 Cheers

  -Pierre


Selon David Allsopp <dra-news@metastack.com>:

> I'm working on a new version of a framework for a server daemon which can
> have custom functionality plugged in through different backends. The present
> version works by passing a record containing lots of options along the lines
> of:
>
> type backend = {b_connect: (connectionID -> bool) option; b_disconnect:
> (connectionID -> unit) option}
>
> which is then passed to a function run which does the actual work, using the
> callbacks given in the backend record or substituting defaults where None is
> specified.
>
> I'm thinking that a functor would be a much neater way of doing this (and
> would also allow for passing around more than just a connectionID if
> required) but wondering what the best way of preserving the ability to have
> default handlers for functions which a given backend isn't interested in.
>
> I've not really used the module system beyond trivial functor applications
> (Set and Map, etc.) but I've come up with the following:
>
> (* Framework.ml *)
>
> (* Individual connection identifiers *)
> type connectionID = int
>
> (* Wrapper type for custom connections *)
> module type CONNECTION = sig
>   type t
>   val newConnection : connectionID -> t
> end
>
> (* Actual backend type *)
> module type BACKEND =
>   sig
>     include CONNECTION
>
>     (* Toy functions, obviously *)
>     val connect : t -> bool
>     val disconnect : t -> unit
>   end
>
> (* Default behaviour defined in these two modules *)
> module Default = struct
>   (* Default connection information is just the identifier *)
>   module Connection : CONNECTION = struct
>     type t = connectionID
>     let newConnection connectionID = connectionID
>   end
>
>   (* Default functions *)
>   module Backend (C : CONNECTION) = struct
>     let connect _ = (* ... *)
>     let disconnect _ = (* ... *)
>   end
> end
>
> module Make (Backend : BACKEND) = struct
>   let run () = (* ... *)
> end
>
> and so an implementation using default connection IDs could be written:
>
> module rec MySimpleBackend : Framework.BACKEND = struct
>   include Framework.Default.Connection
>
>   include Framework.Default.Backend(MySimpleBackend)
>
>   let connect _ = (* Alternate behaviour *)
>   (* Default disconnect is fine *)
> end
>
> and one with more complex connectionIDs could be written:
>
> module rec MyComplexBackend : Framework.BACKEND = struct
>   type t = {ci_id : Framework.connectionID; (* ... *) }
>   let newConnection id = {ci_id = id; (* ... *) }
>
>   include Framework.Default.Backend(MyComplexBackend)
>
>   let connect {ci_id; (* ... *)} = (* Alternate behaviour *)
> end
>
> This pattern seems to work OK but is there an even neater way I haven't
> spotted? I'm presuming that in the following:
>
> module Foo = struct let x = true let x = false end
>
> the compiler doesn't create a module with two fields one of which is
> inaccessible which would seem to be important (from an aesthetic sense) with
> having a module of default functions which get "overridden".
>
> Any guidance/comment appreciated!
>
>
> David
>
>
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa-roc.inria.fr/wws/info/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>
>



  reply	other threads:[~2011-08-20 16:39 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-08-20 14:31 David Allsopp
2011-08-20 16:39 ` pierrchp [this message]
     [not found] ` <1313857880.4e4fe158e4d24@imp.free.fr>
2011-08-20 20:47   ` David Allsopp

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1313858383.4e4fe34f2d35e@imp.free.fr \
    --to=pierrchp@free.fr \
    --cc=caml-list@inria.fr \
    --cc=dra-news@metastack.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).