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
>
>
next prev parent 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).