caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] Avoiding ml/mli code duplication
@ 2010-12-28 14:02 Lauri Alanko
  2010-12-28 14:16 ` Richard W.M. Jones
  2010-12-28 14:59 ` Alain Frisch
  0 siblings, 2 replies; 4+ messages in thread
From: Lauri Alanko @ 2010-12-28 14:02 UTC (permalink / raw)
  To: caml-list

A common grievance with ML-style module systems is duplication of code
between the interface and the implementation of a module. In
particular, transparent type and signature definitions must be present
in both. This is annoying because of the extra work required to keep
both copies in sync.

To an extent, this can be avoided. Consider the following:


(*** Foo.mli ***)
(* Transparent definitions *)
type t = ...
module type S = sig ... end
(* Opaque definitions depending on the above *)
val x : ... t ...
module M : ... S ... t ...

(*** Foo.ml ***)
(* Transparent definitions duplicated *)
type t = ...
module type S = sig ... end
(* Definitions for the opaques *)
let x = ...
module M = ...


Here the common transparent definitions can be moved into a separate
wholly transparent module:


(*** FooDefs.ml ***)
type t = ...
module type S = sig ... end

(*** Foo.ml ***)
include module type of FooDefs (* or open FooDefs *)
val x : ... t ...
module M : ... S ... t ...

(*** Foo.mli ***)
include FooDefs (* or open FooDefs *)
let x = ...
module M = ...


So far so good. But in the real world, the situation is often more
complicated: we have transparent definitions that depend on opaque
definitions. Like this:


(*** Bar.mli ***)
(* Opaque *)
type t
(* Transparent depending on above *)
type u = ... t ...
(* Opaque depending on above *)
val x : ... u ...

(*** Bar.ml ***)
type t = ...
type u = ... t ...
let x = ...


Keep in mind that in actuality the "type u" line can represent huge
amounts of type and signature definitions, so there is real need to
avoid duplication.

In this situation, I'm not quite sure what's the best way to
proceed. Here's what I have currently:

(*** BarDefs.ml ***)
module type CORE = sig
  (* Primitive opaque definitions that the transparents depend on *)
  type t
end
module Make(Core : CORE) = struct
  include Core
  (* Transparent definitions *)
  type u = ... t ...
end

(*** Bar.mli ***)
module Core : BarDefs.CORE
include module type of BarDefs.Make(Core)
val x : ... u ...

(*** Bar.ml ***)
module Core = struct
  type t = ...
end
include BarDefs.Make(Core)
let x = ...


This does the job, but I'm not quite satisfied. It feels wrong that I
have to define a functor when I will only ever apply it once. Also,
the "include module type of" is a recent feature, and it's hard to
believe there's no solution to this problem before OCaml 3.12.

I'd appreciate any suggestions for better solutions.


Lauri

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Caml-list] Avoiding ml/mli code duplication
  2010-12-28 14:02 [Caml-list] Avoiding ml/mli code duplication Lauri Alanko
@ 2010-12-28 14:16 ` Richard W.M. Jones
  2010-12-28 14:59 ` Alain Frisch
  1 sibling, 0 replies; 4+ messages in thread
From: Richard W.M. Jones @ 2010-12-28 14:16 UTC (permalink / raw)
  To: Lauri Alanko; +Cc: caml-list

On Tue, Dec 28, 2010 at 04:02:47PM +0200, Lauri Alanko wrote:
> This does the job, but I'm not quite satisfied. It feels wrong that I
> have to define a functor when I will only ever apply it once. Also,
> the "include module type of" is a recent feature, and it's hard to
> believe there's no solution to this problem before OCaml 3.12.

I think the solution (use of functors) is worse than the problem.
Like OO style, functors make it very hard to examine code and
understand which piece of code is going to run when you call some
function.

> I'd appreciate any suggestions for better solutions.

Just preprocess your code with /bin/cpp, using #include to include a
common file.  Or don't worry about the repeated code too much.

Rich.

-- 
Richard Jones
Red Hat

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Caml-list] Avoiding ml/mli code duplication
  2010-12-28 14:02 [Caml-list] Avoiding ml/mli code duplication Lauri Alanko
  2010-12-28 14:16 ` Richard W.M. Jones
@ 2010-12-28 14:59 ` Alain Frisch
  2010-12-28 16:54   ` Lauri Alanko
  1 sibling, 1 reply; 4+ messages in thread
From: Alain Frisch @ 2010-12-28 14:59 UTC (permalink / raw)
  To: Lauri Alanko; +Cc: caml-list

On 12/28/2010 3:02 PM, Lauri Alanko wrote:
> In this situation, I'm not quite sure what's the best way to
> proceed.

Here is another possibility:

===================================================================
(** BarDefs.ml **)
module type S = sig
   type t
   type u = A of t
end


(** Bar.mli **)
include BarDefs.S
val x: u list


(** Bar.ml **)
type t = X
module rec S_impl : BarDefs.S with type t := t = S_impl
include S_impl
let x = [A X]
===================================================================


I can also mention a local extension we use at LexiFi: a compilation 
unit (.ml) for which an explicit signature (.mli) exists is type-checked 
as a recursive module with itself. (Currently, we disallow recursive 
references to value-like components.)  So in the example above, the 
definition of the module type S could be put in Bar.mli:


===================================================================
(** Bar.mli **)
module type S = sig
   type t
   type u = A of t
end

include S
val x: u list

(** Bar.ml **)
module type S = Bar.S

type t = X
module rec S_impl : S with type t := t = S_impl
include S_impl

let x = [A X]
===================================================================



The same extension allows for easy recursion between, say, type and 
class type definitions:

===================================================================
(** Bar.mli **)
type t = A of Bar.u

class type u = object
   method foo: t
end

.....


(** Bar.ml **)
type t = A of Bar.u

class type u = Bar.u

......
===================================================================


What could be useful would be an extra extension to have an "include" 
statement for specific type declarations, so that the Bar.ml above could 
be written directly:

type t = include Bar.t  (* same as type t = Bar.t = A of Bar.u *)
class type u = Bar.u

.....





-- Alain

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Caml-list] Avoiding ml/mli code duplication
  2010-12-28 14:59 ` Alain Frisch
@ 2010-12-28 16:54   ` Lauri Alanko
  0 siblings, 0 replies; 4+ messages in thread
From: Lauri Alanko @ 2010-12-28 16:54 UTC (permalink / raw)
  To: caml-list

On Tue, Dec 28, 2010 at 03:59:32PM +0100, Alain Frisch wrote:
> (** BarDefs.ml **)
> module type S = sig
>   type t
>   type u = A of t
> end
> 
> 
> (** Bar.mli **)
> include BarDefs.S
> val x: u list
> 
> 
> (** Bar.ml **)
> type t = X
> module rec S_impl : BarDefs.S with type t := t = S_impl
> include S_impl
> let x = [A X]

That's neat. You are using the recursive module trick that allows one
to convert a value-less signature into a module:
<http://ocaml.janestreet.com/?q=node/84>. Effectively, you replace my
example's functor application with signature refining.

(Incidentally, your code can simplified somewhat by moving the
signature S into the body of BarDefs.mli and then using "module type
of BarDefs" instead of "BarDefs.S".)

However, the recursive module trick seems like quite a kludge. Perhaps
there should be a "legitimized" way of producing a module with a
certain value-less signature. For instance, instead of "module rec M :
S = M" just allow "module M : S" within a module definition if S has
no values?

There's another approach to my original problem that unfortunately
doesn't seem to work. In principle, we could have:

(*** BarCore.mli ***)
type t

(*** BarCore.ml ***)
type t = X

(*** BarDefs.ml ***)
include BarCore
type u = A of t

(*** Bar.mli ***)
include module type of BarDefs
val x : u list

(*** Bar.ml ***)
include BarDefs
let x = [A X]

Except, of course, that the constructor X is not visible in Bar.ml. I
can't immediately see a way to make it visible in Bar.ml without also
making it visible in Bar.mli. BarCore would need to have a separate
"package-internal" signature that Bar.ml could use. Is there any way
to achive this?


Lauri

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2010-12-28 16:54 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-28 14:02 [Caml-list] Avoiding ml/mli code duplication Lauri Alanko
2010-12-28 14:16 ` Richard W.M. Jones
2010-12-28 14:59 ` Alain Frisch
2010-12-28 16:54   ` Lauri Alanko

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).