[Pkg-ocaml-maint-commits] [reactivedata] 01/07: Imported Upstream version 0.2

Stéphane Glondu glondu at moszumanska.debian.org
Wed Aug 24 09:45:03 UTC 2016


This is an automated email from the git hooks/post-receive script.

glondu pushed a commit to branch master
in repository reactivedata.

commit 7ac6a5a5995be08aef4e472ee62009bf3d272ade
Author: Stephane Glondu <steph at glondu.net>
Date:   Thu Aug 11 10:15:26 2016 +0200

    Imported Upstream version 0.2
---
 .gitignore           |   2 +
 .merlin              |   5 ++
 CHANGES              |  11 +++
 Makefile             |  37 ++++++++
 README.md            |   9 +-
 _tags                |   4 +
 opam                 |  16 +++-
 pkg/build.ml         |   6 +-
 src/api.odocl        |   1 +
 src/reactiveData.ml  | 246 ++++++++++++++++++++++++++++++++++++++++++---------
 src/reactiveData.mli | 206 ++++++++++++++++++++++++++++++++++++------
 11 files changed, 470 insertions(+), 73 deletions(-)

diff --git a/.gitignore b/.gitignore
index 12d3fc2..6ca76e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@ _build
 \#*#
 .#*
 reactiveData.install
+api.docdir
+doc/html
diff --git a/.merlin b/.merlin
new file mode 100644
index 0000000..468fe6b
--- /dev/null
+++ b/.merlin
@@ -0,0 +1,5 @@
+S src/*
+B _build/*
+
+PKG react
+FLG -warn A-4-40-42-44-48
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..75d4299
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,11 @@
+===== 0.2 =====
+
+  * Add `from_signal`, which converts React signals to ReactiveData
+    containers. `from_signal` uses a diffing algorithm to detect what
+    changes, thus minimizing the updates needed downstream.
+  * Optimize common cases of `merge`.
+  * Provide documentation and make the naming more consistent.
+
+===== 0.1 ======
+
+  * First public release
diff --git a/Makefile b/Makefile
index 9861dc6..05b4833 100644
--- a/Makefile
+++ b/Makefile
@@ -5,3 +5,40 @@ build:
 
 clean:
 	ocamlbuild -clean
+
+doc: build
+	ocamlbuild -use-ocamlfind src/api.docdir/index.html
+
+
+NAME=reactiveData
+
+doc/html/.git:
+	mkdir -p doc/html
+	cd doc/html && (\
+		git init && \
+		git remote add origin git at github.com:ocsigen/$(NAME).git && \
+		git checkout -b gh-pages \
+	)
+
+gh-pages: doc/html/.git doc
+	cd doc/html && git checkout gh-pages
+	rm -f doc/html/dev/*
+	cp _build/src/api.docdir/* doc/html/dev/
+	cd doc/html && git add dev
+	cd doc/html && git commit -a -m "Doc updates"
+	cd doc/html && git push origin gh-pages
+
+release: doc/html/.git
+	@if [ -z "$(VERSION)" ]; then echo "Usage: make release VERSION=1.0.0"; exit 1; fi
+	cd doc/html && cp -r dev $(VERSION)
+	cd doc/html && git add $(VERSION)
+	cd doc/html && git commit -m "Doc updates $(VERSION)"
+	cd doc/html && git push origin gh-pages
+	sed -i "s/= dev =/= $(VERSION) =/" CHANGES
+	git add CHANGES
+	git commit -m "Version $(VERSION)."
+	git tag -a $(VERSION) -m "Version $(VERSION)."
+
+archive:
+	@if [ -z "$(VERSION)" ]; then echo "Usage: make archive VERSION=1.0.0"; exit 1; fi
+	wget "https://github.com/mirage/$(NAME)/archive/$(VERSION).tar.gz"
diff --git a/README.md b/README.md
index 7d4e271..e605083 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,8 @@
-Reactive Data
+**Reactive Data**
+
+*Functional reactive programming with incremental changes in data structures*
+
+ReactiveData is an OCaml module for functional reactive
+programming (FRP) based on React. It adds support to incremental
+changes in data structures by reasoning on patches instead of absolute
+values.
diff --git a/_tags b/_tags
index f040420..8eedfb3 100644
--- a/_tags
+++ b/_tags
@@ -1 +1,5 @@
 <src/reactiveData.*>: package(react)
+true: warn(A-4-40-42-44-48)
+true: warn_error(+1..49), warn_error(-45-3)
+true: strict_sequence, safe_string, short_paths
+true: bin_annot
diff --git a/opam b/opam
index bb5d962..372129d 100644
--- a/opam
+++ b/opam
@@ -1,13 +1,23 @@
-opam-version: "1"
+opam-version: "1.2"
+version: "dev"
+
 maintainer: "Hugo Heuzard <hugo.heuzard at gmail.com>"
-homepage: "https://github.com/hhugo/reactiveData"
 authors: ["Hugo Heuzard <hugo.heuzard at gmail.com>"]
+homepage: "https://github.com/ocsigen/reactiveData"
+dev-repo: "https://github.com/ocsigen/reactiveData.git"
+bug-reports: "https://github.com/ocsigen/reactiveData/issues"
+
+doc:"http://ocsigen.github.io/reactiveData/dev/"
+
 tags: [ "reactive" "declarative" "signal" "event" "frp" ]
 license: "LGPL-3.0 with OCaml linking exception"
+
 depends: ["ocamlfind" "react"]
-ocaml-version: [>= "3.11.0"]
 build:
 [
   [ "ocaml" "pkg/build.ml" "native=%{ocaml-native}%"
                            "native-dynlink=%{ocaml-native}%" ]
 ]
+doc: ["ocamlbuild" "-use-ocamlfind" "src/api.docdir/index.html"]
+
+available: ocaml-version >= "3.11.0"
diff --git a/pkg/build.ml b/pkg/build.ml
index 6db2440..5b66c59 100644
--- a/pkg/build.ml
+++ b/pkg/build.ml
@@ -5,4 +5,8 @@
 let () =
   Pkg.describe "reactiveData" ~builder:`OCamlbuild [
     Pkg.lib "pkg/META";
-    Pkg.lib ~exts:Exts.module_library "src/reactiveData" ]
+    Pkg.lib ~exts:Exts.module_library "src/reactiveData";
+
+    Pkg.doc "README.md";
+    Pkg.doc "CHANGES";
+  ]
diff --git a/src/api.odocl b/src/api.odocl
new file mode 100644
index 0000000..bfafd57
--- /dev/null
+++ b/src/api.odocl
@@ -0,0 +1 @@
+reactiveData
\ No newline at end of file
diff --git a/src/reactiveData.ml b/src/reactiveData.ml
index 24dbcc7..d76c75c 100644
--- a/src/reactiveData.ml
+++ b/src/reactiveData.ml
@@ -24,16 +24,19 @@ module type DATA = sig
   val map_patch : ('a -> 'b) -> 'a patch -> 'b patch
   val map_data : ('a -> 'b) -> 'a data -> 'b data
   val empty : 'a data
+  val equal : ('a -> 'a -> bool) -> 'a data -> 'a data -> bool
+  val diff : eq:('a -> 'a -> bool) -> 'a data -> 'a data -> 'a patch
 end
 module type S = sig
+  type 'a t
   type 'a data
   type 'a patch
   type 'a msg = Patch of 'a patch | Set of 'a data
   type 'a handle
-  type 'a t
   val empty : 'a t
-  val make : 'a data -> 'a t * 'a handle
-  val make_from : 'a data -> 'a msg React.E.t -> 'a t
+  val create : 'a data -> 'a t * 'a handle
+  val from_event : 'a data -> 'a msg React.E.t -> 'a t
+  val from_signal : ?eq:('a -> 'a -> bool) -> 'a data React.S.t -> 'a t
   val const : 'a data -> 'a t
   val patch : 'a handle -> 'a patch -> unit
   val set   : 'a handle -> 'a data -> unit
@@ -41,9 +44,10 @@ module type S = sig
   val map : ('a -> 'b) -> 'a t -> 'b t
   val value : 'a t -> 'a data
   val fold : ('a -> 'b msg -> 'a) -> 'b t -> 'a -> 'a React.signal
-  val value_s : 'a t -> 'a data React.S.t
+  val signal : 'a t -> 'a data React.S.t
   val event : 'a t -> 'a msg React.E.t
 end
+
 module Make(D : DATA) :
   S with type 'a data = 'a D.data
      and type 'a patch = 'a D.patch = struct
@@ -71,7 +75,7 @@ module Make(D : DATA) :
 
   let empty = Const D.empty
 
-  let make l =
+  let create l =
     let initial_event,send = React.E.create () in
     let current = ref l in
     let event = React.E.map (fun msg ->
@@ -82,7 +86,7 @@ module Make(D : DATA) :
         msg) initial_event in
     Mut {current;event},send
 
-  let make_from l initial_event =
+  let from_event l initial_event =
     let current = ref l in
     let event = React.E.map (fun msg ->
         begin match msg with
@@ -131,7 +135,7 @@ module Make(D : DATA) :
       let acc = f acc (Set (!(s.current))) in
       React.S.fold f acc s.event
 
-  let value_s (s : 'a t) : 'a data React.S.t =
+  let signal (s : 'a t) : 'a data React.S.t =
     match s with
     | Const c -> React.S.const c
     | Mut s ->
@@ -140,9 +144,13 @@ module Make(D : DATA) :
           | Set l -> l
           | Patch p -> merge p l) (!(s.current)) s.event
 
+  let from_signal ?(eq = (=)) s =
+    let f d' d = Patch (D.diff ~eq d d') in
+    from_event (React.S.value s) (React.S.diff f s)
+
 end
 
-module DataList =   struct
+module DataList = struct
   type 'a data = 'a list
   type 'a p =
     | I of int * 'a
@@ -165,14 +173,14 @@ module DataList =   struct
       let i = if i' < 0 then List.length l + 1 + i' else i' in
       let rec aux acc n l = match n,l with
         | 0,l -> List.rev_append acc (x::l)
-        | _,[] -> assert false
+        | _,[] -> failwith "ReactiveData.Rlist.merge"
         | n,x::xs -> aux (x::acc) (pred n) xs
       in aux [] i l
     | R i' ->
       let i = if i' < 0 then List.length l + i' else i' in
       let rec aux acc n l = match n,l with
-        | 0,x::l -> List.rev_append acc l
-        | _,[] -> assert false
+        | 0,_::l -> List.rev_append acc l
+        | _,[] -> failwith "ReactiveData.Rlist.merge"
         | n,x::xs -> aux (x::acc) (pred n) xs
       in aux [] i l
     | U (i',x) ->
@@ -187,22 +195,141 @@ module DataList =   struct
       let v = a.(i) in
       if offset > 0
       then begin
+        if (i + offset >= len) then failwith "ReactiveData.Rlist.merge";
         for j = i to i + offset - 1 do
-          assert (j + 1 < len);
           a.(j) <- a.(j + 1)
         done;
         a.(i+offset) <- v
       end
       else begin
+        if (i + offset < 0) then failwith "ReactiveData.Rlist.merge";
         for j = i downto i + offset + 1 do
-          assert (j - 1 >= 0);
           a.(j) <- a.(j - 1)
         done;
         a.(i+offset) <- v
       end;
       Array.to_list a
 
-  let merge p l = List.fold_left (fun l x -> merge_p x l) l p
+  (* accumulates into acc i unmodified elements from l *)
+  let rec linear_merge_fwd ~acc i l =
+    assert (i >= 0);
+    if i > 0 then
+      match l with
+      | h :: l ->
+        let acc = h :: acc in
+        linear_merge_fwd ~acc (i - 1) l
+      | [] ->
+        invalid_arg "invalid index"
+    else
+      l, acc
+
+  let rec linear_merge ~acc i0 p l =
+    let l, acc =
+      match p with
+      | (I (i, _) | R i | U (i, _)) :: _ when i > i0 ->
+        linear_merge_fwd ~acc (i - i0) l
+      | _ ->
+        l, acc
+    in
+    match p, l with
+    | I (i, x) :: p, _ ->
+      linear_merge ~acc i p (x :: l)
+    | R i :: p, _ :: l ->
+      linear_merge ~acc i p l
+    | R _ :: _, [] ->
+      invalid_arg "merge: invalid index"
+    | U (i, x) :: p, _ :: l ->
+      linear_merge ~acc i p (x :: l)
+    | U (_, _) :: _, [] ->
+      invalid_arg "merge: invalid index"
+    | [], l ->
+      List.rev_append acc l
+    | X (_, _) :: _, _ ->
+      failwith "linear_merge: X not supported"
+
+  let rec linear_mergeable ~n p =
+    assert (n >= 0);
+    match p with
+    | (I (i, _) | R i | U (i, _)) :: p when i >= n ->
+      (* negative i's ruled out (among others) *)
+      linear_mergeable ~n:i p
+    | _ :: _ ->
+      false
+    | [] ->
+      true
+
+  let merge p l =
+    if linear_mergeable ~n:0 p then
+      linear_merge ~acc:[] 0 p l
+    else
+      List.fold_left (fun l x -> merge_p x l) l p
+
+  let rec equal f l1 l2 =
+    match l1, l2 with
+    | x1 :: l1, x2 :: l2 when f x1 x2 ->
+      equal f l1 l2
+    | [], [] ->
+      true
+    | _ :: _ , _ :: _
+    | _ :: _ , []
+    | []     , _ :: _ ->
+      false
+
+  let mem l =
+    let h = Hashtbl.create 1024 in
+    List.iter (fun x -> Hashtbl.add h x ()) l;
+    Hashtbl.mem h
+
+  let fold_diff ?(eq = (=)) ~acc ~remove ~add lx ly =
+    let memx = mem lx
+    and memy = mem ly in
+    let rec f ~acc ~left lx ly n =
+      match lx, ly with
+      (* trailing elements to be removed *)
+      | _ :: lx, [] ->
+        let acc = remove acc n in
+        f ~acc ~left lx [] n
+      (* trailing elements to be added *)
+      | [], y :: ly ->
+        let acc = add acc n y in
+        f ~acc ~left [] ly (n + 1)
+      (* done! *)
+      | [], [] ->
+        acc
+      (* same *)
+      | x :: lx, y :: ly when eq x y ->
+        f ~acc ~left lx ly (n + 1)
+      (* x needs to be removed for sure *)
+      | x :: lx, _ :: _ when not (memy x) ->
+        let acc = remove acc n in
+        f ~acc ~left lx ly n
+      (* y needs to be added for sure *)
+      | _ :: _, y :: ly when not (memx y) ->
+        let acc = add acc n y in
+        f ~acc ~left lx ly (n + 1)
+      (* no more certainty, ~left decides what to recur on *)
+      | _ :: lx, _ :: _ when left ->
+        let acc = remove acc n in
+        f ~acc ~left:false lx ly n
+      | _ :: _, y :: ly ->
+        let acc = add acc n y in
+        f ~acc ~left:true lx ly (n + 1)
+    in
+    f ~acc ~left:true lx ly 0
+
+  let rec list_rev ?(acc = []) = function
+    | h :: t ->
+      let acc = h :: acc in
+      list_rev ~acc t
+    | [] ->
+      acc
+
+  let diff ~eq x y =
+    let add acc i v = I (i, v) :: acc
+    and remove acc i = R i :: acc
+    and acc = [] in
+    list_rev (fold_diff ~eq ~acc ~add ~remove x y)
+
 end
 
 module RList = struct
@@ -214,9 +341,8 @@ module RList = struct
     | U of int * 'a
     | X of int * int
 
-  let nil = empty
-  let append x s = patch s [D.I (-1,x)]
   let cons x s = patch s [D.I (0,x)]
+  let snoc x s = patch s [D.I (-1,x)]
   let insert x i s = patch s [D.I (i,x)]
   let update x i s = patch s [D.U (i,x)]
   let move i j s = patch s [D.X (i,j)]
@@ -227,15 +353,15 @@ module RList = struct
   let singleton_s s =
     let first = ref true in
     let e,send = React.E.create () in
+    let result = from_event [] e in
     let _ = React.S.map (fun x ->
         if !first
-        then send (Patch [I(0,x)])
-        else begin
+        then begin
           first:=false;
-          send (Patch [U(0,x)])
-        end) s in
-    make_from [] e
-
+          send (Patch [I(0,x)])
+        end
+        else send (Patch [U(0,x)])) s in
+    result
 
   let concat : 'a t -> 'a t -> 'a t = fun x y ->
     let v1 = value x
@@ -276,8 +402,10 @@ module RList = struct
           match acc,x with
           | (None,p2),`E1 x -> Some x,p2
           | (p1,None),`E2 x -> p1, Some x
-          | _ -> assert false) (None,None) [React.E.map (fun e -> `E1 e) (event x);
-                                            React.E.map (fun e -> `E2 e) (event y)] in
+          | _ -> assert false)
+        (None,None)
+        [React.E.map (fun e -> `E1 e) (event x);
+         React.E.map (fun e -> `E2 e) (event y)] in
     let merged_ev = React.E.map (fun p ->
         match p with
         | Some (Set p1), Some (Set p2) ->
@@ -296,19 +424,19 @@ module RList = struct
           Patch (p1 @ p2)
         | Some (Patch p1), None -> Patch (update_patch1 p1)
         | None, Some (Patch p2) -> Patch (update_patch2 p2)
-        | Some (Patch p1), Some (Set s2) ->
+        | Some (Patch _), Some (Set s2) ->
           let s1 = value x in
           size_with_set size1 s1;
           size_with_set size2 s2;
           Set(s1 @ s2)
-        | Some (Set s1), Some (Patch p2) ->
+        | Some (Set s1), Some (Patch _) ->
           size_with_set size1 s1;
           let s2 = value y in
           size_with_set size2 s2;
           Set(s1 @ s2)
         | None,None -> assert false
       ) tuple_ev in
-    make_from (v1 @ v2) merged_ev
+    from_event (v1 @ v2) merged_ev
 
   let inverse : 'a . 'a p -> 'a p = function
     | I (i,x) -> I(-i-1, x)
@@ -321,32 +449,68 @@ module RList = struct
         | Set l -> Set (List.rev l)
         | Patch p -> Patch (List.map inverse p))  (event t)
     in
-    make_from (List.rev (value t)) e
-
-  let sort eq t = `Not_implemented
-    (* let e = React.E.map (function *)
-    (*     | Set l -> Set (List.sort eq l) *)
-    (*     | Patch p -> Patch p)  (event t) *)
-    (* in *)
-    (* make_from (List.sort eq (value t)) e *)
-
-  let filter f t = `Not_implemented
+    from_event (List.rev (value t)) e
 
 end
 
 module RMap(M : Map.S) = struct
+
   module Data = struct
+
     type 'a data = 'a M.t
-    type 'a patch = [`Add of (M.key * 'a) | `Del of M.key]
-    let merge p s =
+
+    type 'a p = [`Add of (M.key * 'a) | `Del of M.key]
+
+    type 'a patch = 'a p list
+
+    let merge_p p s =
       match p with
       | `Add (k,a) -> M.add k a s
       | `Del k -> M.remove k s
-    let map_patch f = function
+
+    let merge p acc = List.fold_left (fun acc p -> merge_p p acc) acc p
+
+    let map_p f = function
       | `Add (k,a) -> `Add (k,f a)
       | `Del k -> `Del k
+
+    let map_patch f = List.map (map_p f)
+
     let map_data f d = M.map f d
+
     let empty = M.empty
+
+    let equal f = M.equal f
+
+    let diff ~eq x y =
+      let m =
+        let g _key v w =
+          match v, w with
+          | Some v, Some w when eq v w ->
+            None
+          | Some _, Some w ->
+            Some (`U w)
+          | Some _, None ->
+            Some `D
+          | None, Some v ->
+            Some (`A v)
+          | None, None ->
+            None
+        in
+        M.merge g x y
+      and g key x acc =
+        match x with
+        | `U v ->
+          `Del key :: `Add (key, v) :: acc
+        | `D ->
+          `Del key :: acc
+        | `A v ->
+          `Add (key, v) :: acc
+      and acc = [] in
+      List.rev (M.fold g m acc)
+
   end
-  include Make (Data)
+
+  include Make(Data)
+
 end
diff --git a/src/reactiveData.mli b/src/reactiveData.mli
index 698f2ed..c08719b 100644
--- a/src/reactiveData.mli
+++ b/src/reactiveData.mli
@@ -1,5 +1,5 @@
 (* ReactiveData
- * https://github.com/hhugo/reactiveData
+ * https://github.com/ocsigen/reactiveData
  * Copyright (C) 2014 Hugo Heuzard
  *
  * This program is free software; you can redistribute it and/or modify
@@ -17,61 +17,213 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *)
 
-module type DATA = sig
-  type 'a data
-  type 'a patch
-  val merge : 'a patch -> 'a data -> 'a data
-  val map_patch : ('a -> 'b) -> 'a patch -> 'b patch
-  val map_data : ('a -> 'b) -> 'a data -> 'b data
-  val empty : 'a data
-end
+(** ReactiveData is a module for data-aware functional reactive
+    programming (FRP). It adds support to incremental changes in data
+    structures by reasoning on patches instead of absolute
+    values. ReactiveData is based on and inter-operates with React.
+
+    You are most likely interested in the sub-module [RList], which
+    implements a superset of the signature [S]. *)
+
+(** Signature describing a reactive data structure (['a t]).
+
+    Most functions in [S] are not safe to call during a React update
+    step. *)
 module type S = sig
+
+  (** Reactive version of the data container *)
+  type 'a t
+
+  (** Raw (non-reactive) version of the data container *)
   type 'a data
+
+  (** Patch format *)
   type 'a patch
-  type 'a msg = Patch of 'a patch | Set of 'a data
+
+  (** Message format *)
+  type 'a msg =
+    | Patch of 'a patch (** [Patch p] triggers the application of [p]
+                            on the current contents *)
+    | Set of 'a data    (** With [Set d], [d] becomes the new
+                            content *)
+
+  (** Handle that permits applying incremental updates *)
   type 'a handle
-  type 'a t
+
+  (** Empty data structure *)
   val empty : 'a t
-  val make : 'a data -> 'a t * 'a handle
-  val make_from : 'a data -> 'a msg React.E.t -> 'a t
+
+  (** Build a container from initial contents. The handle can be used
+      for performing reactive updates. *)
+  val create : 'a data -> 'a t * 'a handle
+
+  (** [from_event d e] is a container whose initial value is [d], and
+      which gets updated for every occurrence of [e] *)
+  val from_event : 'a data -> 'a msg React.E.t -> 'a t
+
+  (** Convert a React signal into a ReactiveData container.
+
+      Whenever the signal changes from value [v] to value [v'], we
+      detect the differences between [v] and [v'], and perform
+      downstream computation (e.g., for [map]) only on the new and
+      modified elements. *)
+  val from_signal :
+    ?eq:('a -> 'a -> bool) -> 'a data React.S.t -> 'a t
+
+  (** Produce a constant container *)
   val const : 'a data -> 'a t
+
+  (** [patch h p] applies [p] on the container corresponding to [h] *)
   val patch : 'a handle -> 'a patch -> unit
+
+  (** [set h d] sets the contents of the container corresponding to
+      [h], disregarding previous contents *)
   val set : 'a handle -> 'a data -> unit
+
+  (** Transform a message *)
   val map_msg : ('a -> 'b) -> 'a msg -> 'b msg
+
+  (** [map f c] applies [f] on all elements of [c], producing a new
+      reactive container [c']. Modifying the contents of [c] leads to
+      modifications of [c']. [f] is applied only on the new or
+      modified elements of [c]. *)
   val map : ('a -> 'b) -> 'a t -> 'b t
+
+  (** Return current contents *)
   val value : 'a t -> 'a data
+
+  (** [fold f c v] accumulates the updates on [c] with [f] starting
+      from [v].
+
+      The result is a signal of value [f m_n (f ... (f m_1 v))], where
+      [m_1] ... [m_n] are the messages that have been applied since
+      the beginning of [fold]. [m_1] is a pseudo-message [Set l],
+      accounting for the contents [l] of [c] at the time when
+      accumulation starts. *)
   val fold : ('a -> 'b msg -> 'a) -> 'b t -> 'a -> 'a React.signal
-  val value_s : 'a t -> 'a data React.S.t
+
+  (** Signal corresponding to contents *)
+  val signal : 'a t -> 'a data React.S.t
+
+  (** Event whose occurrences correspond to container updates *)
   val event : 'a t -> 'a msg React.E.t
+
 end
 
-module Make(D : DATA) : S with type 'a data = 'a D.data
-                           and type 'a patch = 'a D.patch
+(** Reactive list data structure *)
 module RList :
 sig
+
+  (** Patch operation on lists. All operations are of linear
+      complexity. *)
   type 'a p =
-      I of int * 'a
-    | R of int
-    | U of int * 'a
-    | X of int * int
+    | I of int * 'a  (** [I (i, v)] adds [v] at position [i] *)
+    | R of int       (** [R i] removes [i]-th element *)
+    | U of int * 'a  (** [U (i, v)] substitutes [i]-th element with [v] *)
+    | X of int * int (** [X (i, j)] swaps the [i]-th and [j]-th elements *)
+
+  (** A patch is a list of patch operations. The operations are
+      applied in the order they appear in the list.
+
+      The indices correspond to list contents after the operations
+      that appear earlier in the list have been applied, not to the
+      contents before the whole patch operation.
+
+      A patch comprised of [I], [R], and [U] steps with increasing
+      indices can be applied in time O(m + n), where m is the patch
+      length and n is the current size of the list. (Arbitrary patches
+      are slower, requiring O(m * n).) *)
+  type 'a patch = 'a p list
+
   include S with type 'a data = 'a list
-             and type 'a patch = 'a p list
+             and type 'a patch := 'a patch
 
-  val nil : 'a t
-  val append : 'a -> 'a handle -> unit
+  (** Add element to the beginning *)
   val cons : 'a -> 'a handle -> unit
+
+  (** Add element to the end *)
+  val snoc : 'a -> 'a handle -> unit
+
+  (** [insert v i h] adds [v] as the [i]-th position in the container
+      corresponding to [h]. The indices of the subsequent elements
+      change. *)
   val insert : 'a -> int -> 'a handle -> unit
+
+  (** [remove i h] removes the [i]-th position from the container
+      corresponding to [h]. The indices of the subsequent elements
+      change. *)
   val remove : int -> 'a handle -> unit
+
+  (** [update v i h] substitutes the [i]-th element of the container
+      corresponding to [h] with [v] *)
   val update : 'a -> int -> 'a handle -> unit
+
+  (** [move i j h] moves the [i]-th element of the container
+      corresponding to [h] to the [j]-th position, modifying the
+      indices of other elements *)
   val move : int -> int -> 'a handle -> unit
 
+  (** Produce container list containing a single, constant element *)
   val singleton : 'a -> 'a t
+
+  (** Produce reactive list containing a single element that gets
+      updated based on a signal *)
   val singleton_s : 'a React.S.t -> 'a t
+
+  (** [concat a b] is the concatenation of [a] and [b], and it gets
+      updated whenever [a] and [b] change *)
   val concat : 'a t -> 'a t -> 'a t
+
+  (** [rev a] is the reversal of [a]; [rev a] gets updated along with
+      [a] *)
   val rev : 'a t -> 'a t
-  val sort : ('a -> 'a -> int) -> 'a t -> [`Not_implemented]
-  val filter : ('a -> unit) -> 'a t -> [`Not_implemented]
+
+end
+
+(** Reactive map data structure *)
+module RMap(M : Map.S) : S
+  with type 'a data = 'a M.t
+   and type 'a patch = [ `Add of M.key * 'a | `Del of M.key ] list
+
+(** Signature describing a raw data container (['a data]).
+
+    Given an implementation of [DATA], an incremental version of the
+    container can be produced (via [Make]). *)
+module type DATA = sig
+
+  (** Data container *)
+  type 'a data
+
+  (** Patch format for modifying the container *)
+  type 'a patch
+
+  (** Applicative merge operation: [merge p d] is a new container
+      produced by applying [p] on [d]. [d] does not change. *)
+  val merge : 'a patch -> 'a data -> 'a data
+
+  (** Transform a patch *)
+  val map_patch : ('a -> 'b) -> 'a patch -> 'b patch
+
+  (** [map f d] applies [f] on all the elements of [d], producing a
+      new container in an applicative way *)
+  val map_data : ('a -> 'b) -> 'a data -> 'b data
+
+  (** Empty container *)
+  val empty : 'a data
+
+  (** Lift an equality operator over atoms of type ['a] to an equality
+      operator over ['a data] *)
+  val equal : ('a -> 'a -> bool) -> 'a data -> 'a data -> bool
+
+  (** [diff ?eq d1 d2] produces a patch describing the differences
+      between [d1] and [d2].
+
+      The optional [?eq] argument is used for comparing the atoms
+      inside [d1] and [d2]. (The default value for [eq] is [(=)].) *)
+  val diff : eq:('a -> 'a -> bool) -> 'a data -> 'a data -> 'a patch
+
 end
 
-module RMap(M : Map.S) : S with type 'a data = 'a M.t
-                            and type 'a patch = [ `Add of M.key * 'a | `Del of M.key ]
+(** Functor for turning a plain container into an incremental one *)
+module Make(D : DATA) : S with type 'a data = 'a D.data
+                           and type 'a patch = 'a D.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ocaml-maint/packages/reactivedata.git



More information about the Pkg-ocaml-maint-commits mailing list