0
# Functional Programming
1
2
Exception-safe functional programming constructs providing Result monad for robust error handling and memoization utilities for performance optimization. These utilities enable clean functional composition while maintaining Java's type safety and performance characteristics.
3
4
## Capabilities
5
6
### Result Monad
7
8
Functional result type representing either a successful value or an error, enabling exception-safe error handling patterns similar to Rust's Result or Haskell's Either.
9
10
```java { .api }
11
/**
12
* Functional result type representing either a value or an error
13
*/
14
public interface Result<V> {
15
/**
16
* Create result from value or error condition
17
* @param value Success value (null means error)
18
* @param error Error message (used if value is null)
19
* @return Result instance
20
*/
21
static <V> Result<V> of(V value, CharSequence error);
22
23
/**
24
* Create successful result
25
* @param value Success value
26
* @return Ok result containing value
27
*/
28
static <V> Result<V> ok(V value);
29
30
/**
31
* Create error result
32
* @param error Error message
33
* @return Error result
34
*/
35
static <V> Result<V> err(CharSequence error);
36
37
/**
38
* Create formatted error result
39
* @param format Error message format string
40
* @param args Format arguments
41
* @return Error result
42
*/
43
static <V> Result<V> err(String format, Object... args);
44
45
/**
46
* Check if result represents success
47
* @return true if successful, false if error
48
*/
49
boolean isOk();
50
51
/**
52
* Check if result represents error
53
* @return true if error, false if successful
54
*/
55
boolean isErr();
56
57
/**
58
* Get value as Optional
59
* @return Optional containing value if successful, empty if error
60
*/
61
Optional<V> value();
62
63
/**
64
* Get error as Optional
65
* @return Optional containing error message if error, empty if successful
66
*/
67
Optional<String> error();
68
69
/**
70
* Unwrap value or throw ResultException
71
* @return Success value
72
* @throws ResultException if result is error
73
*/
74
V unwrap();
75
76
/**
77
* Unwrap value or throw ResultException with custom message
78
* @param message Custom error message
79
* @return Success value
80
* @throws ResultException if result is error
81
*/
82
V unwrap(CharSequence message);
83
84
/**
85
* Get value or return default
86
* @param orElse Default value to return if error
87
* @return Success value or default
88
*/
89
V orElse(V orElse);
90
91
/**
92
* Get value or compute from supplier
93
* @param orElseSupplier Supplier to compute default value
94
* @return Success value or supplier result
95
*/
96
V orElseGet(SupplierWithException<? extends V> orElseSupplier);
97
98
/**
99
* Get value or throw custom exception
100
* @param throwableSupplier Function to create exception from error message
101
* @return Success value
102
* @throws R Custom exception type
103
*/
104
<R extends Throwable> V orElseThrow(FunctionWithException<? super String, ? extends R> throwableSupplier) throws R;
105
106
/**
107
* Transform success value using mapper function
108
* @param mapper Function to transform value
109
* @return Result with transformed value or original error
110
*/
111
<U> Result<U> map(FunctionWithException<? super V, ? extends U> mapper);
112
113
/**
114
* Transform error message using mapper function
115
* @param mapper Function to transform error message
116
* @return Result with transformed error or original value
117
*/
118
Result<V> mapErr(FunctionWithException<? super String, ? extends CharSequence> mapper);
119
120
/**
121
* Chain results together (monadic bind)
122
* @param mapper Function returning new Result
123
* @return Flattened result
124
*/
125
<U> Result<U> flatMap(FunctionWithException<? super V, ? extends Result<? extends U>> mapper);
126
127
/**
128
* Recover from error by computing new value
129
* @param recover Function to compute recovery value from error
130
* @return Result with recovered value or original success
131
*/
132
Result<V> recover(FunctionWithException<? super String, ? extends V> recover);
133
134
/**
135
* Recover from error by computing new Result
136
* @param recover Function to compute recovery Result from error
137
* @return Recovered result or original success
138
*/
139
Result<V> recoverWith(FunctionWithException<? super String, ? extends Result<? extends V>> recover);
140
141
/**
142
* Process result with separate handlers for success and error
143
* @param ok Handler for success value
144
* @param err Handler for error message
145
*/
146
void accept(ConsumerWithException<? super V> ok, ConsumerWithException<? super String> err);
147
148
/**
149
* Cast error result to different value type
150
* @return Error result with different type parameter
151
*/
152
<U> Result<U> asError();
153
}
154
155
/**
156
* Exception thrown when unwrapping failed results
157
*/
158
public class ResultException extends RuntimeException {
159
public ResultException(String message);
160
public ResultException(Exception cause);
161
}
162
```
163
164
**Usage Examples:**
165
166
```java
167
import aQute.bnd.result.Result;
168
169
// Basic usage
170
Result<Integer> divide(int a, int b) {
171
if (b == 0) {
172
return Result.err("Division by zero");
173
}
174
return Result.ok(a / b);
175
}
176
177
Result<Integer> result = divide(10, 2);
178
if (result.isOk()) {
179
System.out.println("Result: " + result.unwrap()); // Result: 5
180
} else {
181
System.out.println("Error: " + result.error().get());
182
}
183
184
// Functional composition
185
Result<String> processNumber(int input) {
186
return divide(input, 2)
187
.map(x -> x * 3)
188
.map(x -> "Processed: " + x)
189
.recover(err -> "Default processed value");
190
}
191
192
// Chain operations
193
Result<Integer> chainedOperation = Result.ok(10)
194
.flatMap(x -> divide(x, 2))
195
.flatMap(x -> divide(x, 2))
196
.recover(err -> 0);
197
198
// Error handling without exceptions
199
String result = divide(10, 0)
200
.map(x -> "Success: " + x)
201
.orElse("Operation failed");
202
```
203
204
### Memoization
205
206
Caching utilities for expensive computations with various eviction strategies and resource management options.
207
208
```java { .api }
209
/**
210
* Memoizing supplier interface that caches computed values
211
*/
212
public interface Memoize<S> extends Supplier<S> {
213
/**
214
* Create basic memoizing supplier
215
* @param supplier Supplier to memoize
216
* @return Memoizing supplier that computes once and caches result
217
*/
218
static <T> Memoize<T> supplier(Supplier<? extends T> supplier);
219
220
/**
221
* Create memoizing supplier from function and argument
222
* @param function Function to memoize
223
* @param argument Function argument
224
* @return Memoizing supplier
225
*/
226
static <T, R> Memoize<R> supplier(Function<? super T, ? extends R> function, T argument);
227
228
/**
229
* Create time-based refreshing memoizing supplier
230
* @param supplier Supplier to memoize
231
* @param time_to_live Time to live for cached value
232
* @param unit Time unit for TTL
233
* @return Refreshing memoizing supplier
234
*/
235
static <T> Memoize<T> refreshingSupplier(Supplier<? extends T> supplier, long time_to_live, TimeUnit unit);
236
237
/**
238
* Create reference-based memoizing supplier
239
* @param supplier Supplier to memoize
240
* @param reference Function to create reference for caching
241
* @return Reference-based memoizing supplier
242
*/
243
static <T> Memoize<T> referenceSupplier(Supplier<? extends T> supplier, Function<? super T, ? extends Reference<? extends T>> reference);
244
245
/**
246
* Create predicate-based memoizing supplier
247
* @param supplier Supplier to memoize
248
* @param predicate Predicate to test if cached value is still valid
249
* @return Predicate-based memoizing supplier
250
*/
251
static <T> Memoize<T> predicateSupplier(Supplier<? extends T> supplier, Predicate<? super T> predicate);
252
253
/**
254
* Get memoized value (inherited from Supplier)
255
* @return Cached or computed value
256
*/
257
S get();
258
259
/**
260
* Peek at memoized value without triggering computation
261
* @return Cached value or null if not computed
262
*/
263
S peek();
264
265
/**
266
* Check if value has been computed and cached
267
* @return true if value is cached, false otherwise
268
*/
269
boolean isPresent();
270
271
/**
272
* Transform memoized value using mapper function
273
* @param mapper Function to transform cached value
274
* @return New memoized supplier with transformed value
275
*/
276
<R> Memoize<R> map(Function<? super S, ? extends R> mapper);
277
278
/**
279
* Chain memoized suppliers together
280
* @param mapper Function returning new supplier
281
* @return Flattened memoized supplier
282
*/
283
<R> Memoize<R> flatMap(Function<? super S, ? extends Supplier<? extends R>> mapper);
284
285
/**
286
* Filter memoized value with predicate
287
* @param predicate Filter predicate
288
* @return Filtered memoized supplier
289
*/
290
Memoize<S> filter(Predicate<? super S> predicate);
291
292
/**
293
* Apply consumer to memoized value
294
* @param consumer Consumer to apply
295
* @return This memoized supplier
296
*/
297
Memoize<S> accept(Consumer<? super S> consumer);
298
299
/**
300
* Apply consumer if value is present
301
* @param consumer Consumer to apply
302
* @return This memoized supplier
303
*/
304
Memoize<S> ifPresent(Consumer<? super S> consumer);
305
}
306
307
/**
308
* Memoizing supplier for AutoCloseable resources
309
*/
310
public interface CloseableMemoize<S extends AutoCloseable> extends Memoize<S>, AutoCloseable {
311
/**
312
* Create closeable memoizing supplier
313
* @param supplier Supplier producing AutoCloseable resources
314
* @return Closeable memoizing supplier
315
*/
316
static <T extends AutoCloseable> CloseableMemoize<T> closeableSupplier(Supplier<? extends T> supplier);
317
318
/**
319
* Check if this memoizing supplier is closed
320
* @return true if closed, false otherwise
321
*/
322
boolean isClosed();
323
324
/**
325
* Get the memoized AutoCloseable value
326
* @return The memoized AutoCloseable value
327
* @throws IllegalStateException if this supplier is closed
328
*/
329
@Override
330
S get();
331
332
/**
333
* Apply consumer to memoized value (thread-safe with closing)
334
* @param consumer Consumer to apply
335
* @return This memoized supplier
336
* @throws IllegalStateException if this supplier is closed
337
*/
338
@Override
339
CloseableMemoize<S> accept(Consumer<? super S> consumer);
340
341
/**
342
* Apply consumer if value is present (thread-safe with closing)
343
* @param consumer Consumer to apply
344
* @return This memoized supplier
345
*/
346
@Override
347
CloseableMemoize<S> ifPresent(Consumer<? super S> consumer);
348
349
/**
350
* Close the cached resource if present
351
* @throws Exception if closing fails
352
*/
353
void close() throws Exception;
354
}
355
```
356
357
**Usage Examples:**
358
359
```java
360
import aQute.bnd.memoize.Memoize;
361
import aQute.bnd.memoize.CloseableMemoize;
362
import java.util.concurrent.TimeUnit;
363
364
// Basic memoization
365
Memoize<String> expensiveResult = Memoize.supplier(() -> {
366
// Expensive computation
367
Thread.sleep(1000);
368
return "Computed result";
369
});
370
371
String result1 = expensiveResult.get(); // Takes 1 second
372
String result2 = expensiveResult.get(); // Instant (cached)
373
374
// Time-based refresh
375
Memoize<Date> currentTime = Memoize.refreshingSupplier(
376
() -> new Date(),
377
5, TimeUnit.MINUTES
378
);
379
380
// Reference-based caching (e.g., weak references)
381
Memoize<LargeObject> weakCached = Memoize.referenceSupplier(
382
() -> new LargeObject(),
383
WeakReference::new
384
);
385
386
// Predicate-based validation
387
Memoize<Configuration> config = Memoize.predicateSupplier(
388
() -> loadConfiguration(),
389
cfg -> cfg.isValid()
390
);
391
392
// Transform cached values
393
Memoize<Integer> length = expensiveResult.map(String::length);
394
395
// Closeable resources
396
try (CloseableMemoize<Connection> connection = CloseableMemoize.closeableSupplier(
397
() -> DriverManager.getConnection(url))) {
398
399
Connection conn = connection.get();
400
// Use connection
401
} // Automatically closes cached connection
402
```
403
404
## Functional Exception Handling
405
406
Extended functional interfaces that properly handle exceptions in functional pipelines.
407
408
```java { .api }
409
/**
410
* Function that can throw exceptions
411
*/
412
@FunctionalInterface
413
public interface FunctionWithException<T, R> {
414
R apply(T t) throws Exception;
415
}
416
417
/**
418
* Consumer that can throw exceptions
419
*/
420
@FunctionalInterface
421
public interface ConsumerWithException<T> {
422
void accept(T t) throws Exception;
423
}
424
425
/**
426
* Supplier that can throw exceptions
427
*/
428
@FunctionalInterface
429
public interface SupplierWithException<T> {
430
T get() throws Exception;
431
}
432
```