Skip to content

Commit 17e2a42

Browse files
authored
adds the monad choice interface to the knowledge monad (#1428)
These operations, e.g., `guard`, `unless`, `on`, and `reject` greatly improves legibility of promises by straghtening their control flow structure.
1 parent 6dac0a9 commit 17e2a42

File tree

2 files changed

+66
-5
lines changed

2 files changed

+66
-5
lines changed

lib/knowledge/bap_knowledge.ml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2663,10 +2663,22 @@ module Knowledge = struct
26632663
let pids = ref Pid.zero
26642664

26652665
type conflict += Empty : ('a,'b) slot -> conflict
2666+
| Reject : conflict
2667+
2668+
let reject () = Knowledge.fail Reject
2669+
let guard cnd = if not cnd
2670+
then reject ()
2671+
else Knowledge.return ()
2672+
let on cnd yes = if cnd
2673+
then yes
2674+
else reject ()
2675+
let unless cnd no = if cnd
2676+
then reject ()
2677+
else no
26662678

26672679
let with_empty ~missing scope =
26682680
Knowledge.catch (scope ())
2669-
(function Empty _ -> Knowledge.return missing
2681+
(function Empty _ | Reject -> Knowledge.return missing
26702682
| other -> Knowledge.fail other)
26712683

26722684
let register_watcher (type a b)(s : (a,b) slot) run =

lib/knowledge/bap_knowledge.mli

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ module Knowledge : sig
218218
@since 2.4.0 if [require] is called in the scope of the promise
219219
and fails, the the whole promise immediately returns the empty
220220
value of the property domain, i.e., [f] is wrapped into
221-
[with_missing].
221+
[with_empty].
222222
*)
223223
val promise : ('a,'p) slot -> ('a obj -> 'p t) -> unit
224224

@@ -234,7 +234,7 @@ module Knowledge : sig
234234
@since 2.4.0 if [require] is called in the scope of the promise
235235
and fails, the the whole promise immediately returns the empty
236236
value of the property domain, i.e., [promise] (not [f]) wrapped
237-
into [with_missing].
237+
into [with_empty].
238238
*)
239239
val promising : ('a,'p) slot -> promise:('a obj -> 'p t) ->
240240
(unit -> 's t) -> 's t
@@ -247,7 +247,7 @@ module Knowledge : sig
247247
@since 2.4.0 if [require] is called in the scope of the promise
248248
and fails, the the whole promise immediately returns the empty
249249
value of the property domain, i.e., [f] is wrapped into
250-
[with_missing].
250+
[with_empty].
251251
252252
*)
253253
val propose : agent -> ('a, 'p opinions) slot -> ('a obj -> 'p t) -> unit
@@ -263,7 +263,7 @@ module Knowledge : sig
263263
@since 2.4.0 if [require] are called in the scope of the proposal
264264
and fails, the the whole proposal immediately returns the empty
265265
value of the property domain, i.e., [propose] (not [f]) wrapped
266-
into [with_missing].
266+
into [with_empty].
267267
268268
*)
269269
val proposing : agent -> ('a, 'p opinions) slot ->
@@ -296,11 +296,60 @@ module Knowledge : sig
296296
(** [with_empty ~missing f x] evaluates [f ()] and if it fails on an empty
297297
immediately evaluates to [return missing].
298298
299+
Inside of [with_empty] it is possible to use the choice monad
300+
operations, like [reject], [guard], [on], and [unless], in
301+
addition to the knowledge specialized choice operators, such
302+
as [require] and various [*?] operators.
303+
304+
Note, that promised computations are invoked in the [with_empty]
305+
scope.
306+
299307
@since 2.4.0
300308
*)
301309
val with_empty : missing:'r -> (unit -> 'r knowledge) -> 'r knowledge
302310

303311

312+
(** [reject ()] rejects a promised computation.
313+
314+
When in the scope of the [with_empty] function, e.g., in a
315+
promise or proposal, aborts the computation of the promise
316+
and immediately returns an empty value.
317+
318+
@since 2.5.0 *)
319+
val reject : unit -> 'a t
320+
321+
(** [guard cnd] rejects the rest of compuation if [cnd] is [false].
322+
323+
When in the scope of the [with_empty] function, e.g., in a
324+
promise or proposal, aborts the computation of the promise
325+
and immediately returns an empty value.
326+
327+
@since 2.5.0
328+
*)
329+
val guard : bool -> unit t
330+
331+
332+
(** [on cnd x] evaluates to [x] if [cnd], otherwise rejects.
333+
334+
When in the scope of the [with_empty] function, e.g., in a
335+
promise or proposal, aborts the computation of the promise
336+
and immediately returns an empty value if [cnd] is [false].
337+
If it is not, then evaluates to [x].
338+
339+
@since 2.5.0 *)
340+
val on : bool -> unit t -> unit t
341+
342+
(** [unless cnd x] evaluates to [x] if [not cnd], otherwise rejects.
343+
344+
When in the scope of the [with_empty] function, e.g., in a
345+
promise or proposal, aborts the computation of the promise
346+
and immediately returns an empty value if [cnd] is [true].
347+
If it is [false], then evaluates to [x].
348+
349+
@since 2.5.0 *)
350+
val unless : bool -> unit t -> unit t
351+
352+
304353
(** state with no knowledge *)
305354
val empty : state
306355

0 commit comments

Comments
 (0)