aboutsummaryrefslogtreecommitdiffstats
path: root/src/letsqlite.mli
blob: c0ecc452f271b34b5396bc277c54e64ef797ea6d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
(** Letsqlite main interface. *)

(** {1 Type definitions}

Types used for monad composition.
*)

type 'a stmt_m =
| Failed of Sqlite3.Rc.t
| Norm of ('a * Sqlite3.stmt)

(** The ['a] value in the [stmt_m] is referred to as the bundled value.
    A function may "fail with [e]", in which it returns [Failed e],
    or it may "return a value", in which it returns [Norm v s].
    They are collectively refered to as the monad's values.

    The bundled {!Sqlite.stmt} value is refered to as [S], and the
    value bundled is refered to as [V].

    {i Never directly use [Failed].} You run the likelihood of leaking
    memory.
*)

type ('a,'b) monad_fun = ('a * Sqlite3.stmt) -> 'b stmt_m

type rowdata = (string, Sqlite3.Data.t) Hashtbl.t

(** {1 Primary Functions} *)

val (>>$) : 'a stmt_m -> ('a,'b) monad_fun -> 'b stmt_m
(** [>>$] is the monadic composition function.

    [s >>$ f] return [s] if [s] is [Failed] and [f V S] otherwise.
*)

val (let$) : 'a stmt_m -> ('a,'b) monad_fun -> 'b stmt_m
(** [let$ (v,s) = f (V,S) in ...] is equivalent to
    [f (V,S) >>$ (fun (v,s) -> ...)]. *)

val (@>$) : ('a,'b) monad_fun -> ('b,'c) monad_fun -> ('a,'c) monad_fun
(** [f @>$ g] composes to monadic functions so they become another
    monadic function. *)

val (>-$) : 'a stmt_m -> ('a,'b) monad_fun -> 'a stmt_m
(** [>-$] is the passthrough function.

    [s >-$ f] is equivalent to [s >>$ f] except that the bundled
    value from [f (V,S)] is discarded and replaced with [V]. This function
    passes the original [V] through.
*)

(** {1 Non-Execution Functions}

These functions do not execute any SQL queries. They exist to make dealing
with the monad easier.
*)

val transform : ('a -> 'b) -> ('a,'b) monad_fun
(** [transform f] maps [V] to [f V]. *)

val inject : 'a -> ('b,'a) monad_fun
(** [inject x] returns [Norm x S], discarding [V]. *)

val fail : Sqlite3.Rc.t -> ('a,'b) monad_fun
(** [fail e] finalizes [S] and returns [Failure], where the error
    code is [e] if [e] is not a success error code, and the return code
    of {!Sqlite3.finalize} otherwise.
*)

val stmtfail : Sqlite3.Rc.t -> Sqlite3.stmt -> unit stmt_m
(** [stmtfail e s] is equivalent to [fail] except it assembles a statement
    with unit type. *)

val lift_err : Sqlite3.Rc.t -> ('a,'a) monad_fun
(** [lift_err e] is equivalent to [fail e] when [e] is not a success
    value, and otherwise returns the monad's values unchanged.

    {b NOTE}: You may be tempted to define a function like
    [lift : (Sqlite3.stmt -> Sqlite3.Rc.t) -> ('a,'a) monad_fun]
    which would take a function and apply [lift_err (f S) V S]
    to the function. However, the partial application [lift f]
    would leave you with a {i weak type}, which will force your
    type to have a fixed type. *)

(** {1 Execution functions}

All of these functions will (effectively) exceute {!fail} and return
[Failed] on failure. All functions will omit error behavior unless
there is something different across any of them. *)

val prepare : Sqlite3.db -> string -> 'a -> 'a stmt_m
(** [prepare db stmt v] returns a monad with bundled value [v] and
    the statement in [s].
*)

val reprepare : Sqlite3.db -> string -> ('a,'a) monad_fun
(** [reprepare db stmt] finalizes [S] and prepares [stmt] in
    its place. *)

val reset : ('a,'a) monad_fun
(** [reset] resets [S] so that it may be executed again. *)

val step : ('a,'a) monad_fun
(** [step] is equivalent to {!Sqlite3.step}.

    {b NOTE}: [step] will fail when the statement returns a row.
*)

val rowfold : ('a -> rowdata -> ('a,Sqlite3.Rc.t) Result.t) -> ('a,'a) monad_fun
(** [rowfold f] iterates over each row returns by [S] and applies
    [f] to it, returning some value. *)

val iter : (rowdata -> Sqlite3.Rc.t) -> ('a,unit) monad_fun
(** [rowfold_unit f] is equivalent to [rowfold_init f ()] but [f]
    is not passed a unit argument. *)

val map : ('a -> ('b,'c) monad_fun) -> 'a list -> ('b,'c list) monad_fun
(** [map f l] applies the monadic function [f] to each element of [l],
    returning all the list of all their values. *)

val fold : ('a -> ('b,'b) monad_fun) -> 'a list -> ('b,'b) monad_fun
(** [fold f l] applies each element of [l] to make a monadic function [f]. *)

val get_exactly_one_row : ('a,(unit * rowdata)) monad_fun
(** [get_exactly_one_row] steps [S] and stores the returned row. The
    value in [S] is discarded. The function fails if there is not exactly
    one row.

    The function returns a tuple with the unit type to interact with
    {!extract}.
*)

val exec_extract : ('a -> 'b option) -> ('b -> 'c option)
                   -> (('d * 'a),('c * 'a)) monad_fun
(** [exec_extract extr conv] extracts a value from the second element in
    [V], and then applies [conv] to it. If both were successful, the
    function replaces the first element of [V] with the converted
    value. *)

val extract : string -> (Sqlite3.Data.t -> 'b option)
              -> (('a * rowdata),('b * rowdata)) monad_fun
(** [extract name f] finds the value in the row with header name [name]
    and applies [f] to it. If [f] succeeds then its return value is
    stored in [V]. The rowdata is preserved.
*)

val finalize : 'a stmt_m -> 'a
(** [finalize] raises an exception if the execution failed, and [V]
    otherwise. *)

val bind_values : Sqlite3.Data.t list -> ('a,'a) monad_fun
(** [bind_values l] binds each index of [l] to a positional parameter
    in [S]. *)

val clear_bindings : ('a,'a) monad_fun
(** [clear_bindings] clears any values bound to [S]. *)

val exec : Sqlite3.db -> string -> unit stmt_m
(** [exec db s] is equivalent to a call to {!prepare} followed by {!step}. *)

val reexec : Sqlite3.db -> string -> ('a,'a) monad_fun
(** [reexec db s] is equivalent to [reprepare >>$ step]. *)

val bsrc : Sqlite3.Data.t list -> ('a,'a) monad_fun
(** [bsrc l] is equivalent to [bind_values >>$ step >>$ reset >>$ clear_bindings]. *)