or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

classfile-builder.mdclassfile.mdindex.mdmemoize.mdpreview.mdresult.mdsignatures.mdstream.mdunmodifiable.md

result.mddocs/

0

# Result Type for Error Handling

1

2

Monadic error handling similar to Rust's Result type, providing safe and composable error management without exceptions. This approach enables functional error handling patterns that make error states explicit and composable.

3

4

## Capabilities

5

6

### Result Creation

7

8

Create Result instances representing successful values or error states.

9

10

```java { .api }

11

/**

12

* Monadic error handling for success/failure operations

13

* @param <V> The value type for successful results

14

*/

15

public interface Result<V> {

16

/**

17

* Create a successful Result containing the given value

18

* @param value The success value (may be null)

19

* @return Result representing success

20

*/

21

static <T> Result<T> ok(T value);

22

23

/**

24

* Create an error Result with the given error message

25

* @param error Error message describing the failure

26

* @return Result representing failure

27

*/

28

static <T> Result<T> err(CharSequence error);

29

30

/**

31

* Create an error Result with formatted error message

32

* @param format Format string for the error message

33

* @param args Arguments for the format string

34

* @return Result representing failure

35

*/

36

static <T> Result<T> err(String format, Object... args);

37

38

/**

39

* Create a Result with value and optional error

40

* If error is null or empty, creates success; otherwise creates error

41

* @param value The value (used only if no error)

42

* @param error Optional error message

43

* @return Result representing success or failure based on error presence

44

*/

45

static <T> Result<T> of(T value, CharSequence error);

46

}

47

```

48

49

**Usage Example:**

50

51

```java

52

import aQute.bnd.result.Result;

53

54

// Create successful results

55

Result<String> success = Result.ok("Hello, World!");

56

Result<Integer> number = Result.ok(42);

57

Result<String> nullValue = Result.ok(null); // null is allowed

58

59

// Create error results

60

Result<String> error = Result.err("File not found");

61

Result<Integer> formattedError = Result.err("Invalid number: %d", -1);

62

63

// Conditional result creation

64

Result<String> conditional = Result.of("value", someCondition ? "error occurred" : null);

65

```

66

67

### Result Inspection

68

69

Check the state of Result instances and extract values or errors.

70

71

```java { .api }

72

/**

73

* Check if this Result represents a successful value

74

* @return true if this Result contains a value, false if it contains an error

75

*/

76

boolean isOk();

77

78

/**

79

* Check if this Result represents an error

80

* @return true if this Result contains an error, false if it contains a value

81

*/

82

boolean isErr();

83

84

/**

85

* Get the success value as an Optional

86

* @return Optional containing the success value, or empty if this is an error

87

*/

88

Optional<V> value();

89

90

/**

91

* Get the error message as an Optional

92

* @return Optional containing the error message, or empty if this is a success

93

*/

94

Optional<String> error();

95

96

/**

97

* Get the success value, throwing an exception if this is an error

98

* @return The success value

99

* @throws ResultException if this Result represents an error

100

*/

101

V unwrap();

102

103

/**

104

* Get the success value, throwing an exception with custom message if error

105

* @param message Custom message for the exception

106

* @return The success value

107

* @throws ResultException if this Result represents an error

108

*/

109

V unwrap(CharSequence message);

110

```

111

112

**Usage Example:**

113

114

```java

115

Result<String> result = processFile("data.txt");

116

117

// Check result state

118

if (result.isOk()) {

119

result.value().ifPresent(content ->

120

System.out.println("File content: " + content));

121

} else {

122

result.error().ifPresent(errorMsg ->

123

System.err.println("Error: " + errorMsg));

124

}

125

126

// Unwrap with error handling

127

try {

128

String content = result.unwrap();

129

processContent(content);

130

} catch (ResultException e) {

131

handleError(e.getMessage());

132

}

133

134

// Unwrap with custom error message

135

try {

136

String content = result.unwrap("Failed to read configuration file");

137

useConfiguration(content);

138

} catch (ResultException e) {

139

System.err.println(e.getMessage());

140

}

141

```

142

143

### Fallback Operations

144

145

Provide default values or alternative computations for error cases.

146

147

```java { .api }

148

/**

149

* Return the value if Ok, otherwise return the given default value

150

* @param defaultValue Value to return for error cases

151

* @return The success value or the default value

152

*/

153

V orElse(V defaultValue);

154

155

/**

156

* Return the value if Ok, otherwise compute a value using the given supplier

157

* @param supplier Supplier to compute fallback value

158

* @return The success value or computed fallback value

159

*/

160

V orElseGet(SupplierWithException<? extends V> supplier);

161

162

/**

163

* Return the value if Ok, otherwise throw an exception created by the given function

164

* @param exceptionMapper Function to create exception from error message

165

* @return The success value

166

* @throws R if this Result represents an error

167

*/

168

<R extends Throwable> V orElseThrow(FunctionWithException<? super String, ? extends R> exceptionMapper) throws R;

169

```

170

171

**Usage Example:**

172

173

```java

174

Result<String> result = loadConfiguration();

175

176

// Provide default value

177

String config = result.orElse("default-config");

178

179

// Compute fallback value

180

String config2 = result.orElseGet(() -> {

181

System.out.println("Loading default configuration...");

182

return loadDefaultConfiguration();

183

});

184

185

// Throw custom exception

186

try {

187

String config3 = result.orElseThrow(msg -> new ConfigurationException("Config error: " + msg));

188

} catch (ConfigurationException e) {

189

handleConfigError(e);

190

}

191

```

192

193

### Transformation Operations

194

195

Transform successful values while preserving error states.

196

197

```java { .api }

198

/**

199

* Transform the value if Ok, preserving error state

200

* @param mapper Function to transform the success value

201

* @return Result with transformed value or original error

202

*/

203

<U> Result<U> map(FunctionWithException<? super V, ? extends U> mapper);

204

205

/**

206

* Transform the error message if Err, preserving success state

207

* @param mapper Function to transform the error message

208

* @return Result with original value or transformed error

209

*/

210

Result<V> mapErr(FunctionWithException<? super String, ? extends CharSequence> mapper);

211

212

/**

213

* FlatMap operation for chaining Result computations

214

* @param mapper Function that returns another Result

215

* @return Flattened Result from the mapper or original error

216

*/

217

<U> Result<U> flatMap(FunctionWithException<? super V, ? extends Result<? extends U>> mapper);

218

```

219

220

**Usage Example:**

221

222

```java

223

Result<String> fileContent = readFile("data.txt");

224

225

// Transform successful values

226

Result<Integer> lineCount = fileContent.map(content -> content.split("\n").length);

227

Result<String> upperCase = fileContent.map(String::toUpperCase);

228

229

// Transform error messages

230

Result<String> friendlyError = fileContent.mapErr(err -> "Unable to process file: " + err);

231

232

// Chain operations with flatMap

233

Result<ProcessedData> processed = fileContent

234

.flatMap(content -> parseData(content))

235

.flatMap(data -> validateData(data))

236

.flatMap(data -> transformData(data));

237

238

// Complex pipeline

239

Result<String> result = Result.ok("hello")

240

.map(String::toUpperCase) // Result.ok("HELLO")

241

.map(s -> s + " WORLD") // Result.ok("HELLO WORLD")

242

.flatMap(s -> s.length() > 5 ?

243

Result.ok(s) :

244

Result.err("String too short")); // Result.ok("HELLO WORLD")

245

```

246

247

### Recovery Operations

248

249

Recover from error states by providing alternative computations or transformations.

250

251

```java { .api }

252

/**

253

* Recover from error by transforming error message to success value

254

* @param recovery Function to transform error message to success value

255

* @return Result with recovered value or original success value

256

*/

257

Result<V> recover(FunctionWithException<? super String, ? extends V> recovery);

258

259

/**

260

* Recover from error by providing alternative Result computation

261

* @param recovery Function to transform error message to alternative Result

262

* @return Alternative Result or original success Result

263

*/

264

Result<V> recoverWith(FunctionWithException<? super String, ? extends Result<? extends V>> recovery);

265

```

266

267

**Usage Example:**

268

269

```java

270

Result<String> primary = loadPrimaryConfig();

271

272

// Recover with default value

273

Result<String> recovered = primary.recover(errorMsg -> {

274

System.out.println("Primary config failed: " + errorMsg);

275

return "default-configuration";

276

});

277

278

// Recover with alternative computation

279

Result<String> withFallback = primary.recoverWith(errorMsg -> {

280

System.out.println("Trying fallback config due to: " + errorMsg);

281

return loadFallbackConfig();

282

});

283

284

// Multiple recovery attempts

285

Result<DatabaseConnection> connection = connectToPrimary()

286

.recoverWith(err -> connectToSecondary())

287

.recoverWith(err -> connectToLocal())

288

.recover(err -> createMockConnection());

289

```

290

291

### Consumer Operations

292

293

Execute side effects based on Result state without transforming the Result.

294

295

```java { .api }

296

/**

297

* Execute consumers based on Result state

298

* @param onSuccess Consumer to execute if Result is Ok

299

* @param onError Consumer to execute if Result is Err

300

*/

301

void accept(ConsumerWithException<? super V> onSuccess, ConsumerWithException<? super String> onError);

302

303

/**

304

* Convert to error Result regardless of current state (for testing/debugging)

305

* @return Error Result with current error message or "Expected error but was Ok"

306

*/

307

Result<V> asError();

308

```

309

310

**Usage Example:**

311

312

```java

313

Result<String> result = processRequest();

314

315

// Handle both success and error cases

316

result.accept(

317

value -> System.out.println("Success: " + value),

318

error -> System.err.println("Error: " + error)

319

);

320

321

// Logging example

322

result.accept(

323

value -> logger.info("Processed successfully: {}", value),

324

error -> logger.error("Processing failed: {}", error)

325

);

326

```

327

328

### Exception Handling

329

330

The Result type uses specific exception types for error propagation.

331

332

```java { .api }

333

/**

334

* Exception thrown by unwrap operations when Result contains an error

335

*/

336

public class ResultException extends RuntimeException {

337

/**

338

* Create ResultException with error message

339

* @param message Error message from the Result

340

*/

341

public ResultException(String message);

342

343

/**

344

* Create ResultException with error message and cause

345

* @param message Error message from the Result

346

* @param cause Underlying cause

347

*/

348

public ResultException(String message, Throwable cause);

349

}

350

```

351

352

## Functional Interfaces with Exception Support

353

354

The Result API uses specialized functional interfaces that support checked exceptions:

355

356

```java { .api }

357

@FunctionalInterface

358

public interface SupplierWithException<T> {

359

T get() throws Exception;

360

}

361

362

@FunctionalInterface

363

public interface FunctionWithException<T, R> {

364

R apply(T value) throws Exception;

365

}

366

367

@FunctionalInterface

368

public interface ConsumerWithException<T> {

369

void accept(T value) throws Exception;

370

}

371

```

372

373

## Pattern Examples

374

375

### Configuration Loading with Fallbacks

376

377

```java

378

public Result<Configuration> loadConfiguration() {

379

return loadFromFile("app.config")

380

.recoverWith(err -> loadFromClasspath("default.config"))

381

.recoverWith(err -> Result.ok(Configuration.createDefault()))

382

.map(this::validateConfiguration)

383

.flatMap(config -> config.isValid() ?

384

Result.ok(config) :

385

Result.err("Configuration validation failed"));

386

}

387

```

388

389

### Safe Resource Processing

390

391

```java

392

public Result<ProcessedData> processFile(String filename) {

393

return readFile(filename)

394

.flatMap(this::parseContent)

395

.flatMap(this::validateContent)

396

.map(this::processContent)

397

.mapErr(err -> "Failed to process " + filename + ": " + err);

398

}

399

400

public void handleFileProcessing(String filename) {

401

processFile(filename).accept(

402

data -> saveProcessedData(data),

403

error -> logError(error)

404

);

405

}

406

```

407

408

### API Call Chain

409

410

```java

411

public Result<UserProfile> getUserProfile(String userId) {

412

return validateUserId(userId)

413

.flatMap(id -> fetchUser(id))

414

.flatMap(user -> fetchUserPermissions(user.getId())

415

.map(permissions -> new UserProfile(user, permissions)))

416

.recover(error -> createGuestProfile());

417

}

418

```

419

420

## Comparison with Optional and Exceptions

421

422

**vs Optional:**

423

- Result handles both success values and error information

424

- Optional only represents presence/absence, not why something is absent

425

- Result provides error context for debugging and user feedback

426

427

**vs Exceptions:**

428

- Result makes error states explicit in method signatures

429

- Exceptions require try-catch blocks and can be forgotten

430

- Result enables functional composition of error-prone operations

431

- Result provides better performance for expected error conditions

432

433

## Best Practices

434

435

1. **Use for Expected Errors**: Use Result for operations that commonly fail (file I/O, network calls, parsing)

436

2. **Chain Operations**: Use flatMap to chain multiple fallible operations

437

3. **Provide Context**: Include descriptive error messages

438

4. **Handle Both Cases**: Always handle both success and error cases explicitly

439

5. **Recovery Strategies**: Use recover/recoverWith for graceful degradation

440

6. **Avoid Unwrap**: Prefer orElse/orElseGet over unwrap to avoid exceptions