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