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