Hi Ivan, 2012/10/31 Ivan Gotovchits > > Good day, > > 1. A problem in general > > In general, the problem is to compute a tuple of objects that have > types different, but conforming to the same interface, i.e., given a > group of modules (A,B,C), conforming to signature S to construct some > > module M: functor(A:S) -> functor(B:S) -> functor(C:S) -> S > > that will, in a some way, «map/reduce» computation on corresponding > modules. > Do you wish to have parallel computation done with these modules, or do you mean that you want them to be combined sequentially? > > The problem can be generalized further, but, it seems to me, that it is > already quite (too?) abstract. > > As far as I can see, there is no ad hoc solution for this problem in > OCaml, except, of course, objects with their late binding. But, in some > circumstances, the late binding can be avoided. In particular, when > types of modules and their amount are known and fixed at compile time. > > 2. Particular problem > > For example, we have several different data generator modules conforming > to the signature > > module type Generator = > sig > type t > type r > val create: unit -> t > val result: t -> r option > val step: t -> t option > end > > Each generator can be stepped forward and queried for a result. Generator > can expire, in such case it will return None as a result. I want to make a > «list» of different generators and do some arbitary iterations on them. > > The «list» can be made of pairs, using the cons (meta)constructor: > > module Cons : > functor(G1:Generator) -> functor(G2:Generator) -> Generator > > So, if I have three modules A,B,C, I can construct module > > M = Cons(A)(Cons(B)(C)) > > and call M.step, to move a chain of generators forward, or M.result to > get the current result. > > > 3. Particular solution > > Here is the implementation on Cons module (named ConsGenerator), that > "steps" > contained generators in a sequence (swapping them on each step). > > module ConsGenerator > (G1:Generator) > (G2:Generator with type r = G1.r) > : Generator with type r = G2.r = > struct > type t = > | G1G2 of (G1.t option * G2.t option ) > | G2G1 of (G2.t option * G1.t option ) > > type r = G2.r > > let create () = > G1G2 ((Some (G1.create ())), Some (G2.create ())) > > let step = function > | G1G2 (g1, Some g2) -> Some (G2G1 (G2.step g2, g1)) > | G2G1 (g2, Some g1) -> Some (G1G2 (G1.step g1, g2)) > | G2G1 (Some g2, None) -> Some (G2G1 (G2.step g2, None)) > | G1G2 (Some g1, None) -> Some (G1G2 (G1.step g1, None)) > | G1G2 (None,None) | G2G1 (None,None) -> None > > let result = function > | G1G2 (Some g1,_) -> G1.result g1 > | G2G1 (Some g2,_) -> G2.result g2 > | G1G2 (None,_) | G2G1 (None,_) -> None > end > > Function `proceed' iterates module M and print results > > let rec proceed oe = match M.step oe with > | Some oe -> begin > match M.result oe with > | Some r -> print_int r; proceed oe > | None -> proceed oe > end > | None -> print_newline (); > print_endline "generator finished" > > Just for a completness of the example a pair of simple generators: > > module Odd : Generator with type r = int = struct > type t = int > type r = int > let create () = -1 > let step v = if v < 10 then Some (v+2) else None > let result v = if v <= 9 then Some v else None > end > > module Even : Generator with type r = int = struct > type t = int > type r = int > let create () = 0 > let step v = if v < 10 then Some (v+2) else None > let result v = if v <= 8 then Some v else None > end > > and the result was > # module M = Cons(Even)(Cons(Cons(Odd)(Even))(Even)) > # let _ = proceed (M.create ()) > 22244618648365879 > generator finished > - : unit = () > > > 4. Question > > And now the questions! > > 1) Is there any idiomatic solution to this pattern? > > 2) If not, can my solution be improved in some way? And how? > Thanks to a very recent post, I rediscovered that OCaml can handle modules as first class values (since ver. 3.12) : - you can define types based on module types like this. module type M = sig ... end type m = (module M) and then you should be able to build simple lists of type m. > 3) My intution says that it can be solved with monads (Generator really > incapsulates side effects in a step. Several computation are combined in > one big chain... ). Am I right? If so, how to implement it in monads? > I guess that you could use the type above with regular monads too. If you don't have access to that feature, you could perhaps use higher order functors? Cheers, didier