0
# Control Types
1
2
Monadic control structures for error handling, optional values, and validation that eliminate null pointer exceptions and provide functional composition patterns.
3
4
## Capabilities
5
6
### Option - Optional Values
7
8
Container for values that may or may not exist, eliminating null pointer exceptions with functional composition.
9
10
```java { .api }
11
/**
12
* Container for optional values - either Some(value) or None
13
*/
14
interface Option<T> extends Value<T> {
15
// Factory methods
16
static <T> Option<T> of(T value); // Create Some(value) or None if value is null
17
static <T> Option<T> some(T value); // Create Some(value), throws if null
18
static <T> Option<T> none(); // Create None instance
19
static <T> Option<T> when(boolean condition, T value); // Conditional creation
20
static <T> Option<T> when(boolean condition, Supplier<? extends T> supplier);
21
22
// State checking
23
boolean isDefined(); // true if Some, false if None
24
boolean isEmpty(); // true if None, false if Some
25
26
// Value access
27
T get(); // Get value, throws if None
28
T getOrElse(T other); // Get value or return other if None
29
T getOrElse(Supplier<? extends T> supplier); // Get value or compute other if None
30
<X extends Throwable> T getOrElseThrow(Supplier<X> exceptionSupplier) throws X;
31
Option<T> orElse(Option<? extends T> other); // Return this if Some, other if None
32
Option<T> orElse(Supplier<? extends Option<? extends T>> supplier);
33
34
// Transformation operations
35
<U> Option<U> map(Function<? super T, ? extends U> mapper);
36
<U> Option<U> flatMap(Function<? super T, ? extends Option<? extends U>> mapper);
37
Option<T> filter(Predicate<? super T> predicate);
38
Option<T> filterNot(Predicate<? super T> predicate);
39
40
// Side effects
41
Option<T> peek(Consumer<? super T> action); // Perform action if Some
42
43
// Conversion operations
44
Either<Throwable, T> toEither();
45
Try<T> toTry();
46
List<T> toList(); // Empty list if None, single-element if Some
47
java.util.Optional<T> toJavaOptional();
48
49
// Combining operations
50
<U> Option<Tuple2<T, U>> zip(Option<? extends U> that);
51
<U, R> Option<R> zipWith(Option<? extends U> that, BiFunction<? super T, ? super U, ? extends R> mapper);
52
}
53
54
/**
55
* Some variant containing a value
56
*/
57
class Some<T> implements Option<T> {
58
T get(); // Returns the contained value
59
}
60
61
/**
62
* None variant representing absence of value
63
*/
64
class None<T> implements Option<T> {
65
static None<Object> instance(); // Singleton None instance
66
}
67
```
68
69
**Usage Examples:**
70
71
```java
72
import io.vavr.control.Option;
73
74
// Creating Options
75
Option<String> some = Option.of("Hello");
76
Option<String> none = Option.of(null); // Creates None
77
Option<Integer> conditional = Option.when(5 > 3, 42); // Creates Some(42)
78
79
// Safe operations
80
String result = some
81
.map(String::toUpperCase)
82
.filter(s -> s.length() > 3)
83
.getOrElse("DEFAULT");
84
85
// Chaining operations
86
Option<Integer> length = some.map(String::length);
87
Option<String> doubled = some.flatMap(s ->
88
s.isEmpty() ? Option.none() : Option.some(s + s));
89
90
// Pattern matching style
91
String describe = some.fold(
92
() -> "No value",
93
value -> "Value: " + value
94
);
95
96
// Converting to other types
97
java.util.Optional<String> javaOpt = some.toJavaOptional();
98
List<String> list = some.toList(); // [Hello] or empty list
99
100
// Combining Options
101
Option<String> firstName = Option.of("John");
102
Option<String> lastName = Option.of("Doe");
103
Option<String> fullName = firstName.zipWith(lastName, (f, l) -> f + " " + l);
104
```
105
106
### Either - Disjoint Union
107
108
Represents a value that can be one of two types - typically used for error handling where Left represents error and Right represents success.
109
110
```java { .api }
111
/**
112
* Disjoint union type - either Left(error) or Right(value)
113
*/
114
interface Either<L, R> extends Value<R> {
115
// Factory methods
116
static <L, R> Either<L, R> left(L left); // Create Left instance
117
static <L, R> Either<L, R> right(R right); // Create Right instance
118
static <L, R> Either<L, R> cond(boolean test, R right, L left); // Conditional creation
119
120
// State checking
121
boolean isLeft(); // true if Left, false if Right
122
boolean isRight(); // true if Right, false if Left
123
124
// Value access
125
R get(); // Get right value, throws if Left
126
L getLeft(); // Get left value, throws if Right
127
R getOrElseGet(Function<? super L, ? extends R> other); // Get right or compute from left
128
L getLeftOrElseGet(Function<? super R, ? extends L> other); // Get left or compute from right
129
<X extends Throwable> R getOrElseThrow(Function<? super L, X> exceptionFunction) throws X;
130
void orElseRun(Consumer<? super L> action); // Run action if Left
131
132
// Alternative operations
133
Either<L, R> orElse(Either<? extends L, ? extends R> other); // Use other if Left
134
Either<L, R> orElse(Supplier<? extends Either<? extends L, ? extends R>> supplier);
135
136
// Transformation operations
137
<U> Either<L, U> map(Function<? super R, ? extends U> mapper); // Transform right value
138
<U> Either<U, R> mapLeft(Function<? super L, ? extends U> leftMapper); // Transform left value
139
<L2, R2> Either<L2, R2> bimap(Function<? super L, ? extends L2> leftMapper,
140
Function<? super R, ? extends R2> rightMapper);
141
142
<U> Either<L, U> flatMap(Function<? super R, ? extends Either<L, ? extends U>> mapper);
143
144
// Filtering
145
Option<Either<L, R>> filter(Predicate<? super R> predicate);
146
Option<Either<L, R>> filterOrElse(Predicate<? super R> predicate, L zero);
147
148
// Side effects
149
Either<L, R> peek(Consumer<? super R> action); // Perform action if Right
150
Either<L, R> peekLeft(Consumer<? super L> action); // Perform action if Left
151
152
// Swapping and projections
153
Either<R, L> swap(); // Swap Left and Right
154
LeftProjection<L, R> left(); // Project to Left operations
155
RightProjection<L, R> right(); // Project to Right operations
156
157
// Folding operations
158
<U> U fold(Function<? super L, ? extends U> leftMapper,
159
Function<? super R, ? extends U> rightMapper);
160
161
// Conversion operations
162
Option<R> toOption(); // Right becomes Some, Left becomes None
163
Try<R> toTry(); // Assumes Left contains Throwable
164
Validation<L, R> toValidation();
165
}
166
167
/**
168
* Left variant containing error/left value
169
*/
170
class Left<L, R> implements Either<L, R> {
171
L getLeft(); // Returns the left value
172
}
173
174
/**
175
* Right variant containing success/right value
176
*/
177
class Right<L, R> implements Either<L, R> {
178
R get(); // Returns the right value
179
}
180
181
/**
182
* Left projection for operating on left values
183
*/
184
class LeftProjection<L, R> {
185
<U> Either<U, R> map(Function<? super L, ? extends U> mapper);
186
<U> Either<U, R> flatMap(Function<? super L, ? extends Either<? extends U, R>> mapper);
187
Option<L> filter(Predicate<? super L> predicate);
188
L getOrElseGet(Function<? super R, ? extends L> other);
189
}
190
191
/**
192
* Right projection for operating on right values
193
*/
194
class RightProjection<L, R> {
195
<U> Either<L, U> map(Function<? super R, ? extends U> mapper);
196
<U> Either<L, U> flatMap(Function<? super R, ? extends Either<L, ? extends U>> mapper);
197
Option<R> filter(Predicate<? super R> predicate);
198
R getOrElseGet(Function<? super L, ? extends R> other);
199
}
200
```
201
202
**Usage Examples:**
203
204
```java
205
import io.vavr.control.Either;
206
207
// Creating Either instances
208
Either<String, Integer> success = Either.right(42);
209
Either<String, Integer> failure = Either.left("Error occurred");
210
211
// Error handling pattern
212
Either<String, String> result = parseInteger("123")
213
.map(i -> "Parsed: " + i)
214
.mapLeft(error -> "Failed: " + error);
215
216
// Chaining operations (right-biased)
217
Either<String, Integer> doubled = success
218
.flatMap(value -> value > 0 ? Either.right(value * 2) : Either.left("Negative number"));
219
220
// Folding both sides
221
String message = result.fold(
222
error -> "Error: " + error,
223
value -> "Success: " + value
224
);
225
226
// Working with projections
227
String leftResult = failure.left().map(String::toUpperCase).getOrElseGet(i -> "No error");
228
Integer rightResult = success.right().map(i -> i + 1).getOrElseGet(err -> 0);
229
230
// Converting between types
231
Option<Integer> opt = success.toOption(); // Some(42) if Right, None if Left
232
Try<Integer> attempt = success.toTry(); // Success(42) if Right, Failure if Left
233
234
// Helper method example
235
static Either<String, Integer> parseInteger(String str) {
236
try {
237
return Either.right(Integer.parseInt(str));
238
} catch (NumberFormatException e) {
239
return Either.left("Invalid number: " + str);
240
}
241
}
242
```
243
244
### Try - Exception Handling
245
246
Represents a computation that may succeed with a value or fail with an exception, providing functional exception handling.
247
248
```java { .api }
249
/**
250
* Computation that may succeed or fail - either Success(value) or Failure(exception)
251
*/
252
interface Try<T> extends Value<T> {
253
// Factory methods
254
static <T> Try<T> of(CheckedFunction0<? extends T> supplier); // Try computation
255
static <T> Try<T> success(T value); // Create Success
256
static <T> Try<T> failure(Throwable exception); // Create Failure
257
static <T> Try<T> withResources(CheckedFunction0<? extends T> supplier,
258
AutoCloseable... closeables); // Try-with-resources
259
260
// State checking
261
boolean isSuccess(); // true if Success, false if Failure
262
boolean isFailure(); // true if Failure, false if Success
263
264
// Value access
265
T get(); // Get value, throws if Failure
266
T getOrElse(T other); // Get value or return other if Failure
267
T getOrElse(Supplier<? extends T> supplier);
268
<X extends Throwable> T getOrElseThrow(Function<? super Throwable, X> f) throws X;
269
270
// Exception access (for Failure)
271
Throwable getCause(); // Get exception, throws if Success
272
273
// Transformation operations
274
<U> Try<U> map(Function<? super T, ? extends U> mapper);
275
<U> Try<U> mapTry(CheckedFunction1<? super T, ? extends U> mapper);
276
<U> Try<U> flatMap(Function<? super T, ? extends Try<? extends U>> mapper);
277
<U> Try<U> flatMapTry(CheckedFunction1<? super T, ? extends Try<? extends U>> mapper);
278
279
// Error handling
280
Try<T> recover(Function<? super Throwable, ? extends T> recovery);
281
Try<T> recoverWith(Function<? super Throwable, ? extends Try<? extends T>> recovery);
282
Try<T> mapFailure(Function<? super Throwable, ? extends Throwable> f);
283
284
// Filtering
285
Try<T> filter(Predicate<? super T> predicate);
286
Try<T> filterTry(CheckedPredicate<? super T> predicate);
287
288
// Side effects
289
Try<T> peek(Consumer<? super T> action); // Perform action if Success
290
Try<T> onFailure(Consumer<? super Throwable> action); // Perform action if Failure
291
292
// Conversion operations
293
Either<Throwable, T> toEither(); // Failure -> Left, Success -> Right
294
Option<T> toOption(); // Failure -> None, Success -> Some
295
Validation<Throwable, T> toValidation();
296
297
// Folding operations
298
<U> U fold(Function<? super Throwable, ? extends U> failureMapper,
299
Function<? super T, ? extends U> successMapper);
300
301
// Combining operations
302
<U> Try<Tuple2<T, U>> zip(Try<? extends U> that);
303
<U, R> Try<R> zipWith(Try<? extends U> that, BiFunction<? super T, ? super U, ? extends R> mapper);
304
}
305
306
/**
307
* Success variant containing the computed value
308
*/
309
class Success<T> implements Try<T> {
310
T get(); // Returns the successful value
311
}
312
313
/**
314
* Failure variant containing the exception
315
*/
316
class Failure<T> implements Try<T> {
317
Throwable getCause(); // Returns the exception
318
}
319
```
320
321
**Usage Examples:**
322
323
```java
324
import io.vavr.control.Try;
325
import io.vavr.CheckedFunction1;
326
327
// Basic Try operations
328
Try<Integer> result = Try.of(() -> Integer.parseInt("123"));
329
Try<Integer> failure = Try.of(() -> Integer.parseInt("abc"));
330
331
// Chaining operations
332
Try<String> processed = result
333
.map(i -> i * 2)
334
.map(i -> "Result: " + i)
335
.recover(ex -> "Error: " + ex.getMessage());
336
337
// Exception handling
338
Try<String> safe = Try.of(() -> riskyOperation())
339
.recover(throwable -> "Default value")
340
.map(String::toUpperCase);
341
342
// Filtering with exceptions
343
Try<Integer> positive = result.filter(i -> i > 0); // Becomes Failure if <= 0
344
345
// Working with multiple Try instances
346
Try<String> name = Try.of(() -> getName());
347
Try<Integer> age = Try.of(() -> getAge());
348
Try<String> person = name.zipWith(age, (n, a) -> n + " is " + a + " years old");
349
350
// Try-with-resources pattern
351
Try<String> content = Try.withResources(
352
() -> Files.lines(Paths.get("file.txt")).collect(Collectors.joining("\n")),
353
Files.newBufferedReader(Paths.get("file.txt"))
354
);
355
356
// Converting to other types
357
Option<Integer> opt = result.toOption(); // Some(123) or None
358
Either<Throwable, Integer> either = result.toEither(); // Right(123) or Left(exception)
359
360
// Pattern matching style processing
361
String outcome = result.fold(
362
exception -> "Failed with: " + exception.getMessage(),
363
value -> "Succeeded with: " + value
364
);
365
366
// Helper methods with checked exceptions
367
static String riskyOperation() throws IOException {
368
// Some operation that might throw
369
return "success";
370
}
371
372
static CheckedFunction1<String, Integer> parseToInt = Integer::parseInt;
373
```
374
375
### Validation - Accumulating Validation
376
377
Represents validation results that can accumulate multiple errors, useful for form validation and data processing.
378
379
```java { .api }
380
/**
381
* Validation that accumulates errors - either Valid(value) or Invalid(errors)
382
*/
383
interface Validation<E, T> extends Value<T> {
384
// Factory methods
385
static <E, T> Validation<E, T> valid(T value); // Create Valid instance
386
static <E, T> Validation<E, T> invalid(E error); // Create Invalid with single error
387
static <E, T> Validation<E, T> invalid(E... errors); // Create Invalid with multiple errors
388
static <E, T> Validation<E, T> invalid(Iterable<? extends E> errors);
389
390
// Conditional creation
391
static <E, T> Validation<E, T> fromTry(Try<? extends T> tryValue, Function<Throwable, E> errorMapper);
392
static <E, T> Validation<E, T> fromEither(Either<E, ? extends T> either);
393
static <E, T> Validation<E, T> fromOption(Option<? extends T> option, E ifNone);
394
395
// State checking
396
boolean isValid(); // true if Valid, false if Invalid
397
boolean isInvalid(); // true if Invalid, false if Valid
398
399
// Value access
400
T get(); // Get value, throws if Invalid
401
T getOrElse(T other); // Get value or return other if Invalid
402
T getOrElse(Supplier<? extends T> supplier);
403
404
// Error access
405
Seq<E> getError(); // Get errors, throws if Valid
406
407
// Transformation operations
408
<U> Validation<E, U> map(Function<? super T, ? extends U> mapper);
409
<U> Validation<U, T> mapError(Function<E, U> errorMapper);
410
<F, U> Validation<F, U> bimap(Function<E, F> errorMapper, Function<? super T, ? extends U> valueMapper);
411
<U> Validation<E, U> flatMap(Function<? super T, ? extends Validation<E, ? extends U>> mapper);
412
413
// Filtering
414
Validation<E, T> filter(Predicate<? super T> predicate, E ifFalse);
415
Validation<E, T> filter(Predicate<? super T> predicate, Supplier<? extends E> errorSupplier);
416
417
// Combining validations (accumulates errors)
418
<U> Validation<E, Tuple2<T, U>> zip(Validation<E, ? extends U> that);
419
<U, R> Validation<E, R> zipWith(Validation<E, ? extends U> that,
420
BiFunction<? super T, ? super U, ? extends R> mapper);
421
422
// Applicative operations for accumulating multiple validations
423
static <E, T1, T2, R> Validation<E, R> combine(
424
Validation<E, T1> v1, Validation<E, T2> v2,
425
BiFunction<T1, T2, R> function);
426
427
static <E, T1, T2, T3, R> Validation<E, R> combine(
428
Validation<E, T1> v1, Validation<E, T2> v2, Validation<E, T3> v3,
429
Function3<T1, T2, T3, R> function);
430
// ... up to combine8
431
432
// Folding operations
433
<U> U fold(Function<? super Seq<E>, ? extends U> invalidMapper,
434
Function<? super T, ? extends U> validMapper);
435
436
// Conversion operations
437
Either<Seq<E>, T> toEither(); // Invalid -> Left(errors), Valid -> Right(value)
438
Option<T> toOption(); // Invalid -> None, Valid -> Some(value)
439
Try<T> toTry(); // Invalid -> Failure, Valid -> Success
440
}
441
442
/**
443
* Valid variant containing the validated value
444
*/
445
class Valid<E, T> implements Validation<E, T> {
446
T get(); // Returns the valid value
447
}
448
449
/**
450
* Invalid variant containing accumulated errors
451
*/
452
class Invalid<E, T> implements Validation<E, T> {
453
Seq<E> getError(); // Returns the accumulated errors
454
}
455
```
456
457
**Usage Examples:**
458
459
```java
460
import io.vavr.control.Validation;
461
import io.vavr.collection.List;
462
import static io.vavr.control.Validation.*;
463
464
// Form validation example
465
public class Person {
466
private final String name;
467
private final String email;
468
private final int age;
469
470
public Person(String name, String email, int age) {
471
this.name = name;
472
this.email = email;
473
this.age = age;
474
}
475
}
476
477
// Individual field validations
478
static Validation<String, String> validateName(String name) {
479
return name != null && name.trim().length() >= 2
480
? valid(name.trim())
481
: invalid("Name must be at least 2 characters");
482
}
483
484
static Validation<String, String> validateEmail(String email) {
485
return email != null && email.contains("@")
486
? valid(email)
487
: invalid("Email must contain @");
488
}
489
490
static Validation<String, Integer> validateAge(Integer age) {
491
return age != null && age >= 0 && age <= 150
492
? valid(age)
493
: invalid("Age must be between 0 and 150");
494
}
495
496
// Combining validations (accumulates all errors)
497
static Validation<List<String>, Person> validatePerson(String name, String email, Integer age) {
498
return Validation.combine(
499
validateName(name).mapError(List::of),
500
validateEmail(email).mapError(List::of),
501
validateAge(age).mapError(List::of)
502
).ap((n, e, a) -> new Person(n, e, a));
503
}
504
505
// Usage
506
Validation<List<String>, Person> result = validatePerson("", "invalid-email", -5);
507
// result.isInvalid() == true
508
// result.getError() == ["Name must be at least 2 characters", "Email must contain @", "Age must be between 0 and 150"]
509
510
// Successful validation
511
Validation<List<String>, Person> success = validatePerson("John", "john@example.com", 30);
512
// success.isValid() == true
513
// success.get() == Person("John", "john@example.com", 30)
514
515
// Working with validation results
516
String message = result.fold(
517
errors -> "Validation failed: " + errors.mkString(", "),
518
person -> "Valid person: " + person
519
);
520
521
// Converting to other types
522
Option<Person> maybePerson = success.toOption();
523
Either<List<String>, Person> eitherPerson = result.toEither();
524
```