Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A design of functors in OCaml

Tags:

functor

ocaml

I have defined 2 modules Zone and Zones, Zones is a list of Zone, of cause functions of Zones need to call functions of Zone:

module Zone = struct
 type t = 
 { ...
   prop: bool }
 ...
end

modules Zones = struct
  type t =
  | ZSbot
  | ZS of Zone.t list
  | ZStop
  ...
end

A principle file all.ml uses Zones and Mis modules, mis.ml contains functions which work on both Zone.t and Zones.t, for instance val Mis.make : Zone.t -> Zones.t.

open Zones
open Mis
type t = { zs: Zones.t }
...
Mis.make z

Now, I would like to have more options for the prop of Zone. So I define an interface PROPERTY and 2 modules Type and Formula matching it, so that I can make functors for Zone and others where ... prop: Property.t .... Now I can imagine several possibilities for the new all.ml:

(* 1 *)
open Zones
module ZonesType = ZonesFun(Type)
module ZonesFormula = ZonesFun(Formula)
type t = { zstype: ZonesType.t; zsformula: ZonesFormula }

(* 3 *)
open Zones
module ZonesType = ZonesFun(ZoneFun(Type))
module ZonesFormula = ZonesFun(ZoneFun(Formula))
type t = { zstype: ZonesType.t; zsformula: ZonesFormula }

(* 4 *)
open Zones
module ZoneType = ZoneFun(Type)
module ZoneFormula = ZoneFun(Formula)
module ZonesType = ZonesFun(ZoneType)
module ZonesFormula = ZonesFun(ZoneFormula)
type t = { zstype: ZonesType.t; zsformula: ZonesFormula }

Although the signatures of ZonesFun and ZoneFun are different among the 3 options, this implementation can make sure ZoneXXX.t and ZonesXXX.t are coherent. Now one big problem is how to change Mis:

1) If I make a functor MisFun: PROPERTY -> MIS, and build the ZoneXXX and ZonesXXX inside. It cannot know MisXXX.Zone.t is same as Zone.t of all.ml, or MisXXX.Zones.t is same as Zones.t of all.ml.

2) If I make a functor MisFun: Zone -> Zones -> MIS, it cannot know MisXXX.Zone.t and MisXXX.Zones.t are coherent.

Does anyone know how to solve both 1) and 2)?

like image 616
SoftTimur Avatar asked Oct 21 '22 21:10

SoftTimur


1 Answers

In Option (1), do you apply ZoneFun inside ZonesFun?

Assuming that, I think the choice is between (1) and (3)/(4) (which seem to be the same). Which one to pick depends on whether you need to be able to access the created Zone modules outside ZoneFun (you need (4)) or not ((1) works fine).

Update in reply to update:

If I understand your question correctly, then it seems to me that Mis has to become a functor as well. Moreover, its signature can specify types like

val make: ZoneFun(X).t -> ZonesFun(X).t

where X is the functor parameter.

(Btw, I still see no difference between (3) and (4), except that you name the auxiliary modules.)

Update 2:

My guess is that you are running into an old and unfortunate bug in OCaml's module type checker (see this discussion on the caml list). The following ought to work, but doesn't:

module type PROP = sig type t end
module type ZONE = sig type t end
module MakeZone (P : PROP) = struct type t = {p : P.t} end
module MakeZones (Z : ZONE) = struct type t = ZS of Z.t list end

module MakeMisc (P : PROP) :
sig
  val make : MakeZone(P).t -> MakeZones(MakeZone(P)).t
end =
struct
  module Zone = MakeZone(P)
  module Zones = MakeZones(Zone)
  let make z = Zones.ZS [z]
end

module Type = struct type t = T end
module Formula = struct type t = F end
module ZoneType = MakeZone(Type)
module ZoneFormula = MakeZone(Formula)
module ZonesType = MakeZones(ZoneType)
module ZonesFormula = MakeZones(ZoneFormula)
module MiscType = MakeMisc(Type)
module MiscFormula = MakeMisc(Formula)
let zst = MiscType.make {ZoneType.p = Type.T}
let zsf = MiscFormula.make {ZoneFormula.p = Formula.F}

You can force to make it work by putting in type annotations in MakeMisc as follows:

module MakeMisc (P : PROP) :
sig
  val make : MakeZone(P).t -> MakeZones(MakeZone(P)).t
end =
struct
  module Zone : sig type t = MakeZone(P).t end = MakeZone(P)
  module Zones :
    sig type t = MakeZones(MakeZone(P)).t = ZS of MakeZone(P).t list end =
    MakeZones(MakeZone(P))
  let make z = Zones.ZS [z]
end

But obviously, that's not very pleasant.

However, the common way to express sharing in ML, anyway, is by fibration, where you name the types or modules you want to share with abstractly in the signatures and refine them accordingly. Then you can turn Zone and Zones into additional parameters of the Misc functor:

module type PROP = sig type t end
module type ZONE = sig type prop type t = {p : prop} end
module type ZONES = sig type zone type t = ZS of zone list end
module MakeZone (P : PROP) = struct type prop = P.t type t = {p : prop} end
module MakeZones (Z : ZONE) = struct type zone = Z.t type t = ZS of zone list end

module MakeMisc
  (P : PROP) (Z : ZONE with type prop = P.t) (Zs : ZONES with type zone = Z.t) :
sig
  val make : Z.t -> Zs.t
end =
struct
  let make z = Zs.ZS [z]
end

module Type = struct type t = T end
module Formula = struct type t = F end
module ZoneType = MakeZone(Type)
module ZoneFormula = MakeZone(Formula)
module ZonesType = MakeZones(ZoneType)
module ZonesFormula = MakeZones(ZoneFormula)
module MiscType = MakeMisc(Type)(ZoneType)(ZonesType)
module MiscFormula = MakeMisc(Formula)(ZoneFormula)(ZonesFormula)
let zst = MiscType.make {ZoneType.p = Type.T}
let zsf = MiscFormula.make {ZoneFormula.p = Formula.F}
like image 131
Andreas Rossberg Avatar answered Nov 11 '22 07:11

Andreas Rossberg