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