-
Notifications
You must be signed in to change notification settings - Fork 0
/
optional.clj
111 lines (102 loc) · 4.15 KB
/
optional.clj
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
(ns fr33m0nk.optional
(:require
[fr33m0nk.utility :as util])
(:import (java.util Optional))
(:refer-clojure :exclude [= get filter map]))
(defn ^Optional optional-of
"returns an Optional
Zero arity version returns an empty Optional
Single arity version returns an Optional of value"
([]
(optional-of nil))
([value]
(if value
(Optional/of value)
(Optional/empty))))
(defn has-value?
"returns a boolean.
checks if supplied Optional has any value"
[^Optional optional]
(.isPresent optional))
(defn =
"returns a boolean
checks equality for Optionals"
([_optional] true)
([^Optional optional ^Optional another-optional]
(.equals optional another-optional))
([^Optional optional ^Optional another-optional & more-optionals]
(if (= optional another-optional)
(if-let [tail (next more-optionals)]
(recur another-optional (first more-optionals) tail)
(.equals another-optional (first more-optionals)))
false)))
(defn get
"return nil for empty Optional
returns value contained in Optional
Two arity version also takes another value that would be returned if Optional is empty"
([^Optional optional]
(get optional nil))
([^Optional optional other-value]
(.orElse optional other-value)))
(defn filter
"returns empty Optional if predicate evaluates to false or predicate throws Exception or
returns Optional with value if predicate evaluates to true
Multi arity versions also take an exception handler. This can be a logger as well.
If logger is a macro e.g. clojure.tools.logging, create logger-fn
e.g. (def logger-fn #(clojure.tools.logging/info % \"Exception occurred\"))
The reason is Clojure functions can't take macros as argument"
([predicate optional]
(filter predicate optional nil))
([predicate ^Optional optional exception-handler]
(->> (util/fn->predicate predicate exception-handler)
(.filter optional))))
(defn map
"returns empty Optional if f throws Exception or returns Optional with transformed value if f was applied successfully
Multi arity versions also take an exception handler. This can be a logger as well.
If logger is a macro e.g. clojure.tools.logging, create logger-fn
e.g. (def logger-fn #(clojure.tools.logging/info % \"Exception occurred\"))
The reason is Clojure functions can't take macros as argument"
([f optional]
(map f optional nil))
([f ^Optional optional exception-handler]
(->> (util/fn->function f exception-handler)
(.map optional))))
(defn flat-map
"returns empty Optional if f throws Exception or returns Optional with transformed value if f transforms value
Un-nests Optionals and returns a single Optional e.g. Optional<Optional<T>> -> Optional<T>
This is useful when working with fn that return Optional values
Multi arity versions also take an exception handler. This can be a logger as well.
If logger is a macro e.g. clojure.tools.logging, create logger-fn
e.g. (def logger-fn #(clojure.tools.logging/info % \"Exception occurred\"))
The reason is Clojure functions can't take macros as argument"
([f optional]
(flat-map f optional nil))
([f optional exception-handler]
(->> (util/fn->function f exception-handler true)
(.flatMap optional))))
(defn optional->sequence
"returns Clojure sequence from Optional<T>"
[^Optional optional]
(-> optional
.stream
.iterator
iterator-seq))
(defn wrap-fn
"takes a Clojure Fn and returns a Clojure Fn that is wrapped in try catch block
returned fn when executed returns Optional of value if application was successful
or empty Optional when execution of supplied function throws an Exception
Multi arity versions also take a logger
If logger is a macro e.g. clojure.tools.logging, create logger-fn
e.g. (def logger-fn #(clojure.tools.logging/info % \"Exception occurred\"))
The reason is Clojure functions can't take macros as argument"
([f]
(wrap-fn f nil))
([f exception-handler]
(fn [& args]
(try
(optional-of (apply f args))
(catch Exception ex
(let [handled-val (some-> exception-handler (#(% ex)))]
(if handled-val
(optional-of handled-val)
(optional-of))))))))