[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