001/* 002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package gololang.error; 012 013import java.util.NoSuchElementException; 014import java.util.Objects; 015import java.util.Optional; 016import java.util.List; 017import java.util.Iterator; 018import java.util.Collections; 019import java.util.function.Function; 020import java.util.function.Predicate; 021import gololang.Tuple; 022import gololang.FunctionReference; 023 024/** 025 * A container object which represent the result of a maybe failing operation. 026 * 027 * <p>This object is used when chaining computations (e.g. using map-filter operations) that can 028 * produce an error. Instead of raising an exception, the operation can use this object to 029 * encapsulate the result. This is similar to {@code Optional}, but also encapsulate the type of 030 * error in the form of a {@code Throwable} instance that can be raised later. 031 * 032 * <p>This is similar to the {@code Either} or {@code Result} type in other functional languages (e.g. 033 * <a href="https://hackage.haskell.org/package/base/docs/Data-Either.html">Haskell</a>, 034 * <a href="https://doc.rust-lang.org/std/result/">Rust</a> or 035 * <a href="http://www.scala-lang.org/api/2.9.3/scala/Either.html">Scala</a>) 036 * 037 * <p>Typical usage:<ul> 038 * <li>return {@link #empty()} instead of returning {@code null}, 039 * <li>use {@link #error(java.lang.Throwable)} or {@link #fail(java.lang.String)} instead of 040 * throwing an exception, 041 * <li>use {@link #ok(java.lang.Object)} to return a normal value. 042 * </ul> 043 */ 044public final class Result<T, E extends Throwable> implements Iterable<T> { 045 046 private static final Result<?, ?> EMPTY = new Result<>(); 047 private final T value; 048 private final E error; 049 050 private Result() { 051 this.value = null; 052 this.error = null; 053 } 054 055 private Result(T value, E throwable) { 056 this.value = value; 057 this.error = throwable; 058 } 059 060 /** 061 * Dynamic polymorphic constructor. 062 * 063 * <p>Dynamically dispatch on {@link #empty()}, {@link #error(java.lang.Throwable)}, 064 * {@link #option(java.util.Optional)} or {@link #ok(java.lang.Object)} depending on the 065 * {@code value} type. This is mainly useful in Golo code. 066 * 067 * @param value the value to encapsulate 068 * @return a {@code Result} representing the value 069 */ 070 public static Result<Object, Throwable> of(Object value) { 071 if (value == null) { 072 return empty(); 073 } 074 if (value instanceof Throwable) { 075 return error((Throwable) value); 076 } 077 if (value instanceof Optional) { 078 @SuppressWarnings("unchecked") 079 Optional<Object> opt = (Optional<Object>) value; 080 return option(opt); 081 } 082 return ok(value); 083 } 084 085 /** 086 * Returns an empty {@code Result}. 087 * 088 * <p>Represents a successful computation that returns nothing. 089 * 090 * @return an empty {@code Result} 091 */ 092 public static <T, E extends Throwable> Result<T, E> empty() { 093 @SuppressWarnings("unchecked") 094 Result<T, E> r = (Result<T, E>) EMPTY; 095 return r; 096 } 097 098 /** 099 * Returns a valid {@code Result}. 100 * 101 * <p>Represents a successful computation's result. 102 * 103 * @param <T> the class of the value 104 * @param value the possibly-null value to describe 105 * @return a {@code Result} containing the value if the specified value is non-null, 106 * otherwise an empty {@code Result} 107 */ 108 public static <T, E extends Throwable> Result<T, E> ok(T value) { 109 return value == null ? empty() : new Result<>(value, null); 110 } 111 112 /** 113 * Returns a failed {@code Result}. 114 * 115 * <p>Represent a computation that failed. 116 * 117 * @param <E> the class of the throwable 118 * @param throwable the error that occurred 119 * @return a {@code Result} containing the throwable 120 */ 121 public static <T, E extends Throwable> Result<T, E> error(E throwable) { 122 return throwable == null ? empty() : new Result<>(null, throwable); 123 } 124 125 /** 126 * Construct a {@code Result} from a {@code Optional}. 127 * 128 * @param <T> the class of the value 129 * @param opt the {@code Optional} representing the possibly present value 130 * @return a {@code Result} containing the value if {@code isPresent()} is {@code true}, 131 * otherwise an empty {@code Result} 132 */ 133 public static <T, E extends Throwable> Result<T, E> option(Optional<T> opt) { 134 return opt == null || !opt.isPresent() ? empty() : new Result<>(opt.get(), null); 135 } 136 137 /** 138 * Construct a {@code Result} from a {@code Optional}. 139 * 140 * @param <T> the class of the value 141 * @param opt the {@code Optional} representing the possibly present value 142 * @param message a message used to create an error if the {@code Optional} is empty 143 * @return a {@code Result} containing the value if {@code isPresent()} is {@code true}, 144 * otherwise an error containing {@code NoSuchElementException} 145 */ 146 public static <T> Result<T, NoSuchElementException> option(Optional<T> opt, String message) { 147 return (opt == null || !opt.isPresent()) 148 ? new Result<>(null, new NoSuchElementException(message)) 149 : new Result<>(opt.get(), null); 150 } 151 152 /** 153 * Returns a failed {@code Result}. 154 * 155 * <p>Represent a computation that failed. This is similar to {@link #error(java.lang.Throwable)} 156 * but only the message is provided, and a {@code RuntimeException} is automatically created. 157 * 158 * @param message the message representing the error 159 * @return a {@code Result} containing a {@code RuntimeException} 160 */ 161 public static <T> Result<T, RuntimeException> fail(String message) { 162 return error(new RuntimeException(message)); 163 } 164 165 /** 166 * If a value is present, returns the value, if empty throws {@code NoSuchElementException}, 167 * otherwise throws the contained error. 168 * 169 * @return the non-null value contained in this {@code Result} 170 * @throws NoSuchElementException if the {@code Result} is empty 171 * @throws E if the {@code Result} is an error 172 */ 173 public T get() throws E, NoSuchElementException { 174 if (value != null) { 175 return value; 176 } 177 if (error != null) { 178 throw error; 179 } 180 throw new NoSuchElementException("Empty result"); 181 } 182 183 /** 184 * Convert this {@code Result} into a {@code Optional} describing its value. 185 * 186 * @return an {@code Optional} containing the value of this {@code Result}, 187 * or an empty {@code Optional} if {@code isValue()} is {@code false} 188 */ 189 public Optional<T> toOptional() { 190 if (value != null) { 191 return Optional.of(value); 192 } 193 return Optional.empty(); 194 } 195 196 /** 197 * Convert this {@code Result} into a {@link java.util.List} of values. 198 * 199 * @return an singleton list containing the value if present, otherwise an empty list 200 */ 201 public List<T> toList() { 202 if (value != null) { 203 return Collections.singletonList(value); 204 } 205 return Collections.emptyList(); 206 } 207 208 /** 209 * Convert this {@code Result} into a {@link java.util.List} of error. 210 * 211 * @return an singleton list containing the error if present, otherwise an empty list 212 */ 213 public List<E> toErrorList() { 214 if (error != null) { 215 return Collections.singletonList(error); 216 } 217 return Collections.emptyList(); 218 } 219 220 221 @Override 222 public Iterator<T> iterator() { 223 return toList().iterator(); 224 } 225 226 /** 227 * Convert this {@code Result} into a {@code Optional} describing its error. 228 * 229 * @return an {@code Optional} containing the error of this {@code Result}, 230 * or an empty {@code Optional} if {@code isError()} is {@code false} 231 */ 232 public Optional<E> toOptionalError() { 233 if (error != null) { 234 return Optional.of(error); 235 } 236 return Optional.empty(); 237 } 238 239 /** 240 * Return the value of present, otherwise return {@code other}. 241 * 242 * @param other the value to return if is empty 243 * @return the value if present, otherwise {@code other} 244 */ 245 public T orElse(T other) { 246 if (value != null) { 247 return value; 248 } 249 return other; 250 } 251 252 /** 253 * Return the value of present, otherwise return the result of the invocation of {@code fun}. 254 * 255 * @param fun the function to invoke if is empty (may return a default value or throw an 256 * exception) 257 * @return the value if present, otherwise the invocation of {@code fun} 258 */ 259 public Object orElseGet(FunctionReference fun) throws Throwable { 260 if (value != null) { 261 return value; 262 } 263 return fun.invoke(); 264 } 265 266 267 /** 268 * @return {@code true} if there is neither a value nor an error, otherwise {@code false} 269 */ 270 public boolean isEmpty() { 271 return value == null && error == null; 272 } 273 274 /** 275 * @return {@code true} if there is an error (and no value), otherwise {@code false} 276 */ 277 public boolean isError() { 278 return value == null && error != null; 279 } 280 281 /** 282 * @param type the class to test the error for 283 * @return {@code true} if the present error is an instance of {@code type} 284 */ 285 public boolean isError(Class<?> type) { 286 return error != null && type.isInstance(error); 287 } 288 289 /** 290 * @return {@code true} if there is a value (and no error), otherwise {@code false} 291 */ 292 public boolean isValue() { 293 return value != null && error == null; 294 } 295 296 /** 297 * @param val the value to test for presence 298 * @return {@code true} if the present value is equal to {@code val} 299 */ 300 public boolean isValue(Object val) { 301 return Objects.equals(value, val); 302 } 303 304 /** 305 * If a value is present, apply the provided mapping function to it, otherwise return the 306 * {@code Result} itself. If the application succeed with a value, return a {@code Result} 307 * containing it, if the result is null, return an empty {@code Result}, otherwise return a 308 * {@code Result} capturing the {@code Throwable} that was thrown. 309 * 310 * @param <U> The type of the result of the mapping function 311 * @param mapper a mapping function to apply to the value, if present 312 * @return a {@code Result} describing the result of applying the mapping function to the value of 313 * this {@code Result} 314 * @throws NullPointerException if the mapping function is null 315 */ 316 public <U, X extends Throwable> Result<U, X> map(Function<? super T, ? extends U> mapper) { 317 Objects.requireNonNull(mapper); 318 if (value == null) { 319 @SuppressWarnings("unchecked") 320 Result<U, X> r = (Result<U, X>) this; 321 return r; 322 } 323 try { 324 return ok(mapper.apply(value)); 325 } catch (Throwable e) { 326 @SuppressWarnings("unchecked") 327 Result<U, X> r = (Result<U, X>) error(e); 328 return r; 329 } 330 } 331 332 /** 333 * If this result is an error, apply the provided mapping function to the contained error, 334 * otherwise return the {@code Result} itself. 335 * If the application succeed with a value, return a {@code Result} 336 * containing it, if the result is null, return an empty {@code Result}, otherwise return a 337 * {@code Result} capturing the {@code Throwable} that was thrown. 338 * 339 * @param <X> The type of the result of the mapping function 340 * @param mapper a mapping function to apply to the error, if present 341 * @return a {@code Result} describing the result of applying the mapping function to the error of 342 * this {@code Result} 343 * @throws NullPointerException if the mapping function is null 344 */ 345 public <X extends Throwable> Result<T, X> mapError(Function<? super E, ? extends X> mapper) { 346 Objects.requireNonNull(mapper); 347 if (error == null) { 348 @SuppressWarnings("unchecked") 349 Result<T, X> r = (Result<T, X>) this; 350 return r; 351 } 352 try { 353 return error(mapper.apply(error)); 354 } catch (Throwable e) { 355 @SuppressWarnings("unchecked") 356 Result<T, X> r = (Result<T, X>) error(e); 357 return r; 358 } 359 } 360 361 362 363 /** 364 * If a value is present, apply the provided {@code Result}-bearing mapping function to it, 365 * otherwise return the {@code Result} itself. 366 * If the application succeed, return its result, otherwise return a 367 * {@code Result} capturing the {@code Throwable} that was thrown. 368 * 369 * @param <U> The type of the value of the {@code Result} returned by the mapping function 370 * @param mapper a mapping function to apply to the value, if present 371 * @return the result of applying the mapping function to the value of this {@code Result} 372 * @throws NullPointerException if the mapping function is {@code null} or if it returns {@code null} 373 */ 374 public <U, X extends Throwable> Result<U, X> flatMap(Function<? super T, Result<U, X>> mapper) { 375 Objects.requireNonNull(mapper); 376 if (isEmpty() || isError()) { 377 @SuppressWarnings("unchecked") 378 Result<U, X> r = (Result<U, X>) this; 379 return r; 380 } 381 Result<U, X> result; 382 try { 383 result = mapper.apply(value); 384 } catch (Throwable e) { 385 @SuppressWarnings("unchecked") 386 Result<U, X> err = (Result<U, X>) error(e); 387 return err; 388 } 389 return Objects.requireNonNull(result); 390 } 391 392 /** 393 * Golo compatible version of {@code flatMap}. 394 * 395 * See <a href="https://github.com/eclipse/golo-lang/issues/277">issue 277</a> 396 */ 397 public Result<Object, Throwable> flatMap(FunctionReference mapper) { 398 @SuppressWarnings("unchecked") 399 Result<Object, Throwable> result = (Result<Object, Throwable>) flatMap((Function) mapper.to(Function.class)); 400 return result; 401 } 402 403 /** 404 * Remove one level of result. 405 * <p> 406 * This is actually equivalent to {@code flatMap(identity)} 407 * (or {@code r.flatMap(f)} is equivalent to {@code r.map(f).flattened()}) 408 * <p> 409 * For instance: 410 * <pre class="listing"><code class="lang-java" data-lang="java"> 411 * ok(ok(42)).flattened() == ok(42) 412 * fail("error").flattened() == fail("error") 413 * empty().flattened() == empty() 414 * </code></pre> 415 * 416 * @return the value contained in this result if it's a result 417 * @throws ClassCastException when the result does not contain a result. 418 */ 419 public Result<?, ?> flattened() { 420 if (value == null) { 421 return this; 422 } 423 return (Result) value; 424 // } 425 // throw new ClassCastException(String.format("%s cannot be cast to %s", 426 // value.getClass(), Result.class)); 427 } 428 429 /** 430 * Same as {@code map} or {@code flatMap} depending on the type returned by {@code mapper}. 431 * <p> 432 * This is a generic version for {@code map} and {@code flatMap}: 433 * if {@code mapper} returns a {@code Result}, it's equivalent to {@code flatMap}, 434 * otherwise, it's equivalent to {@code map}. 435 * <p> 436 * This allows code such as: 437 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 438 * Ok(21): andThen(|x| -> x + 1): andThen(|x| -> Ok(2 * x)) == Ok(42) 439 * </code></pre> 440 */ 441 public Result<?, ? extends Throwable> andThen(FunctionReference mapper) { 442 Objects.requireNonNull(mapper); 443 if (isEmpty() || isError()) { 444 return this; 445 } 446 Object result; 447 try { 448 result = mapper.invoke(value); 449 } catch (Throwable e) { 450 return error(e); 451 } 452 if (result instanceof Result) { 453 return (Result<?, ?>) result; 454 } 455 else { 456 return ok(result); 457 } 458 } 459 460 /** 461 * Case analysis for the result. 462 * <p> 463 * If the result is a value, apply the first function to it; 464 * if it is an error, apply the second function to it. 465 * <p> 466 * Note that if the result is empty, i.e. the value is {@code null}, 467 * the {@code mapping} function is applied to {@code null}. 468 * 469 * @param mapping the function to apply to the contained value 470 * @param recover the function to apply to the contained error 471 * @return the result of applying the corresponding function 472 */ 473 public Object either(FunctionReference mapping, FunctionReference recover) throws Throwable { 474 if (isError()) { 475 return recover.invoke(error); 476 } 477 return mapping.invoke(value); 478 } 479 480 /** 481 * Three way case analysis for the result. 482 * <p> 483 * If the result is a value, apply the first function to it; 484 * if it is an error, apply the second function to it; 485 * if it is empty, invoke the third function. 486 * 487 * @param mapping the function to apply to the contained value 488 * @param recover the function to apply to the contained error 489 * @param def the function to invoke if the result is empty (takes no arguments) 490 * @return the result of applying the corresponding function 491 */ 492 public Object either(FunctionReference mapping, FunctionReference recover, FunctionReference def) throws Throwable { 493 if (isEmpty()) { 494 return def.invoke(); 495 } 496 return this.either(mapping, recover); 497 } 498 499 /** 500 * Golo compatible version of {@code map}. 501 * 502 * See <a href="https://github.com/eclipse/golo-lang/issues/277">issue 277</a> 503 */ 504 public Result<Object, Throwable> map(FunctionReference mapper) { 505 @SuppressWarnings("unchecked") 506 Result<Object, Throwable> result = (Result<Object, Throwable>) map((Function) mapper.to(Function.class)); 507 return result; 508 } 509 510 /** 511 * If a value is present and matches the given predicate, return a {@code Result} describing the 512 * value, otherwise return an empty {@code Result}. If the {@code Result} is empty or is an error, 513 * return the {@code Result} itself. 514 * 515 * @param predicate a predicate to apply to the value, if present 516 * @return a {@code Result} describing the value of this {@code Result} if it maches the predicate 517 * @throws NullPointerException if the predicate is null 518 */ 519 public Result<T, E> filter(Predicate<? super T> predicate) { 520 Objects.requireNonNull(predicate); 521 if (isEmpty() || isError()) { 522 return this; 523 } 524 return predicate.test(value) ? this : empty(); 525 } 526 527 /** 528 * Reduce {@code this} using {@code func} with {@code init} as initial value. 529 * <p> 530 * For instance: 531 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 532 * Result.ok("b"): reduce("a", |x, y| -> x + y) == "ab" 533 * Result.empty(): reduce(42, |x, y| -> x + y) == 42 534 * Result.fail("error"): reduce(42, |x, y| -> x + y) == 42 535 * </code></pre> 536 * @param init the initial value 537 * @param func the aggregation function 538 * @return the initial value if this is not a value, the aggregated result otherwise 539 */ 540 public Object reduce(Object init, FunctionReference func) throws Throwable { 541 if (value == null) { 542 return init; 543 } 544 return func.invoke(init, value); 545 } 546 547 /** 548 * Apply the function contained is this result to the given result. 549 * <p> 550 * If the function has several parameters, a result containing a partialized version 551 * is returned, that can be `apply`ed to subsequent results. 552 * This makes `Result` an “applicative functor”. 553 */ 554 public Result<?, ?> apply(Result<?, ?> other) throws Throwable { 555 if (!this.isValue()) { 556 return this; 557 } 558 if (!(value instanceof FunctionReference)) { 559 throw new RuntimeException("The result must contain a function to be applied"); 560 } 561 FunctionReference f = (FunctionReference) value; 562 if (f.arity() > 1) { 563 return ok(f.bindTo(other.get())); 564 } 565 return other.map((FunctionReference) value); 566 } 567 568 /** 569 * Conjunctive chaining. 570 * 571 * @param other the other result 572 * @return {@code other} if this result is a value, otherwise {@code this} 573 */ 574 public Result<?, ?> and(Result<?, ?> other) { 575 if (!this.isError()) { 576 return other; 577 } 578 return this; 579 } 580 581 /** 582 * Disjunctive chaining. 583 * 584 * @param other the other result 585 * @return {@code other} if this result is an error, otherwise {@code this} 586 */ 587 public Result<?, ?> or(Result<?, ?> other) { 588 if (this.isError()) { 589 return other; 590 } 591 return this; 592 } 593 594 595 /** 596 * Indicate whether some other object is equal to this {@code Result}. 597 * The other object is considered equal if: 598 * <ul> 599 * <li>it is also a {@code Result} and; 600 * <li>both instances are empty or; 601 * <li>both instances have values that are equal via {@code equals()} or; 602 * <li>both instances are errors of the same type with the same message. 603 * </ul> 604 * 605 * @param o an object to be tested for equality 606 * @return {@code true} if the other object is equal to this object, otherwise {@code false} 607 */ 608 @Override 609 public boolean equals(Object o) { 610 if (o == null) { 611 return false; 612 } 613 if (this == o) { 614 return true; 615 } 616 if (this.getClass() != o.getClass()) { 617 return false; 618 } 619 Result<?, ?> that = (Result<?, ?>) o; 620 return Objects.equals(this.value, that.value) 621 && (Objects.equals(this.error, that.error) 622 || (this.error.getClass() == that.error.getClass() 623 && this.error.getMessage().equals(that.error.getMessage()))); 624 } 625 626 @Override 627 public int hashCode() { 628 if (error == null) { 629 return Objects.hash(value); 630 } 631 return Objects.hash(error.getClass(), error.getMessage()); 632 } 633 634 @Override 635 public String toString() { 636 if (isEmpty()) { 637 return "Result.empty"; 638 } 639 if (isError()) { 640 return String.format("Result.error[%s]", error); 641 } 642 return String.format("Result.value[%s]", value); 643 } 644 645 /** 646 * Return a {@link gololang.Tuple} representing this {@code Result}. 647 * 648 * <p>Return a 2-tuple containing the error and the value contained by this {@code Result}, so 649 * that it can be used in a destructuring golo assignment. The first value is the error, and the 650 * second is the correct value (mnemonic: “right” also means “correct”). For instance: 651 * <pre class="listing"><code class="lang-golo" data-lang="golo"> 652 * let e, v = Result.ok(42) # e is null and v is 42 653 * let e, v = Result.empty() # e is null and v is null 654 * let e, v = Result.fail("error") # e is RuntimeException("error") and v is null 655 * </code></pre> 656 * This allows to deal with error in the same way as Go does for instance. 657 * 658 * @return a 2-tuple containing the error and the value contained by this {@code Result} 659 */ 660 public Tuple destruct() { 661 return new Tuple(error, value); 662 } 663 664} 665