open Common

(*****************************************************************************)
(* Collection *)
(*****************************************************************************)
(*
 * The derived classes of collections:
 * - sequence(next, nth): array, list, stack, queue, and mixed
 *   (fast cons, fast snoc, fast append, cf okasaki)
 * - set(union): setl, setb, seti, seth
 * - assoc(find): assocl, mapb, hash, btree, multimap (mais bof, can do
 *   with map of set)
 * - graph: graph1way, graph2way, graphref, graphmatrix?
 *
 * Some features/notes:
 * - views a la wadler to make it cool (I hate get/set).
 * - take list in parameters to be able to construct value as is easily
 * - take the comparison function in parameters (=> functorial set made cool)
 *   make l [], h [], ... as in perl, and pass the func from pervasive
 *   in oo form (list, ...)
 * - pure/impure: could put 2 interface, with one that show that inpure
 *   by making the operation return unit, but simpler to have one interface.
 * - the core method and default method (via virtual classes)
 *   better to use virtual than typeclass, virtual play both roles:
 *   an interface and default code
 *
 * - pb binary methods: use tosetb tricks, or via (not safe) Obj.magic.
 * - array/list are both a sequence _and_ a dictionnary, so are both
 *   a collection(a) and a collection(i,a) at the same time. But cannot do that.
 *   So for array, I see it mainly as an assoc => favor assoc, and
 *   for list, I see it mainly as a collection => favor collection
 *
 * ??mixins: comparable, iterator, via virtual class in ocaml
 * ?? kind of haskell class + default value
 *
 * ?? persistence, caching, peut prendre en param le type de map qu'il cache,
 * comme en perl, evite du marshalling kan wrapped = bdb.
 *
 * ?? lazy wrapper,  how avoid complexity of having to define each time
 * a hashP, hashC, hashL, hashPCL, ... ?
 *
 * ?? I define those classes cos their name are cool, say what is intended to
 * do with
 *
 * todo: cf book on algorithm, a la rivest/sedgewick
 * todo: recreate collection hierarchy, inspire smalltalk ? haskell ? merd ?
 * todo: put a clean sequence (inherit collection) and make array a special
 * class
 * todo: make ostack (FIFO), oqueue (LIFO)
 *
 *
 * influences: okasaki, merd (pixel), java classes, smalltalk classes
 *)

(*---------------------------------------------------------------------------*)
type ('a, 'b) view = Empty | Cons of 'a * 'b

class virtual ['a] ocollection =
object(o: 'o)
  inherit Objet.objet

  method virtual empty: 'o
  method virtual add: 'a -> 'o

  method virtual iter: ('a -> unit) -> unit
  method virtual view: ('a, 'o) view

  (* no need virtual, but better to redefine for efficiency *)
  method virtual del: 'a -> 'o   (* can do default with: view+iter *)
  method virtual mem: 'a -> bool (* can do default with: mem(tolist) *)
  method virtual null: bool      (* can do default with: lenght(tolist)= 0 *)


  method add2: 'a -> unit = fun a ->
    o#add a +> ignore;
    ()
  method del2: 'a -> unit = fun a ->
    o#del a +> ignore;
    ()
  method clear: unit =
    o#iter (fun e -> o#del2 e);




  method fold: 'b. ('b -> 'a -> 'b) -> 'b -> 'b = fun f a ->
    let a = ref a in
    o#iter (fun e -> a := f !a e);
    !a

  method tolist: 'a list =
    List.rev (o#fold (fun acc e -> e::acc) [])
  method fromlist: 'a list -> 'o =
    fun xs -> xs +> List.fold_left (fun o e -> o#add e) o#empty

  method length: int =
    (* oldsimple: o#tolist +> List.length *)
    (* opti: *)
    let count = ref 0 in
    o#iter (fun e -> incr count);
    !count

  method exists: ('a -> bool) -> bool = fun f ->
    o#tolist +> List.exists f

  method filter: ('a -> bool) -> 'o = fun f ->
    (* iter and call add from empty, or del *)
    o#tolist +> List.filter f +> o#fromlist

  (* forall, fold, map *)

  method getone: 'a =
    match o#view with Cons (e,tl) -> e  | Empty -> failwith "no head"
  method others: 'o =
    match o#view with Cons (e,tl) -> tl | Empty -> failwith "no tail"

end
