<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p><font size="4">For clarity, this line of thought originally arose
        from questions around outcome aggregation and failure
        representation in the context of Loom’s StructuredTaskScope,
        particularly when reasoning about multiple concurrent subtasks
        and their combined results. The sketch above is not intended to
        suggest changes to Loom or to structured concurrency APIs, but
        simply reflects how thinking through that specific context led
        me to explore more general value-oriented outcome modeling as a
        way to reason about the trade-offs discussed.<br>
        <br>
        I’m not proposing this as a solution, nor suggesting that such
        an abstraction should exist; this was simply a way for me to
        think more concretely about the design space and the trade-offs
        discussed in the thread. If nothing else, I found the exercise
        useful for clarifying where value-oriented outcomes align well
        with Java’s existing idioms, and where they introduce tension.
        I’ll defer to the architects’ judgment on whether any of this is
        worth revisiting now or in the future, and appreciate the
        perspectives shared in helping me better understand the
        constraints involved.</font></p>
    <p><font size="4">Note: I am content with these </font><font
        size="5"><b><i>not</i></b></font><font size="4"> being
        extensible by mere mortals, but maintained and extended through
        the exclusive diligence of the Java Language Architects.</font></p>
    <p><font size="5">Even when using value-oriented outcomes such as
        Result, callers must still be prepared for exceptions to be
        thrown, whether due to programming errors, contract violations,
        cancellation, or JVM-level conditions. This sketch is not
        intended to eliminate or replace Java’s exception model, but to
        complement it where expected failure can be modeled explicitly
        as data.</font></p>
    <blockquote>
      <p><font size="4">package java.util;<br>
          <br>
          import java.util.function.Function;<br>
          <br>
          public sealed interface Result<V, F><br>
                  permits Result.Success, Result.Failure {<br>
          <br>
              // --- Variants ---<br>
          <br>
              record Success<V, F>(V value) implements
          Result<V, F> {}<br>
              record Failure<V, F>(F failure) implements
          Result<V, F> {}<br>
          <br>
              // --- Constructors ---<br>
          <br>
              static <V, F> Result<V, F> success(V value) {<br>
                  return new Success<>(value);<br>
              }<br>
          <br>
              static <V, F> Result<V, F> failure(F failure)
          {<br>
                  return new Failure<>(failure);<br>
              }<br>
          <br>
              // --- Introspection ---<br>
          <br>
              default boolean isSuccess() {<br>
                  return this instanceof Success<?, ?>;<br>
              }<br>
          <br>
              default boolean isFailure() {<br>
                  return this instanceof Failure<?, ?>;<br>
              }<br>
          <br>
              // --- Core transforms ---<br>
          <br>
              default <U> Result<U, F> map(Function<?
          super V, ? extends U> mapper) {<br>
                  return switch (this) {<br>
                      case Success<V, F>(var value) -><br>
                              Result.success(mapper.apply(value));<br>
                      case Failure<V, F>(var failure) -><br>
                              Result.failure(failure);<br>
                  };<br>
              }<br>
          <br>
              default <G> Result<V, G>
          mapFailure(Function<? super F, ? extends G> mapper) {<br>
                  return switch (this) {<br>
                      case Success<V, F>(var value) -><br>
                              Result.success(value);<br>
                      case Failure<V, F>(var failure) -><br>
                              Result.failure(mapper.apply(failure));<br>
                  };<br>
              }<br>
          <br>
              default <U> Result<U, F> flatMap(<br>
                      Function<? super V, ? extends Result<U,
          F>> mapper) {<br>
                  return switch (this) {<br>
                      case Success<V, F>(var value) -><br>
                              mapper.apply(value);<br>
                      case Failure<V, F>(var failure) -><br>
                              Result.failure(failure);<br>
                  };<br>
              }<br>
          <br>
              // --- Extraction ---<br>
          <br>
              default V orElse(V fallback) {<br>
                  return switch (this) {<br>
                      case Success<V, F>(var value) -> value;<br>
                      case Failure<V, F>(var failure) ->
          fallback;<br>
                  };<br>
              }<br>
          <br>
              default V orElseGet(Function<? super F, ? extends V>
          fallback) {<br>
                  return switch (this) {<br>
                      case Success<V, F>(var value) -> value;<br>
                      case Failure<V, F>(var failure) ->
          fallback.apply(failure);<br>
                  };<br>
              }<br>
          <br>
              default <X extends Throwable> V orElseThrow(<br>
                      Function<? super F, ? extends X>
          toException) throws X {<br>
                  return switch (this) {<br>
                      case Success<V, F>(var value) -> value;<br>
                      case Failure<V, F>(var failure) -><br>
                              throw toException.apply(failure);<br>
                  };<br>
              }<br>
          <br>
              // --- Optional interop ---<br>
          <br>
              static <T, F> Result<T, F>
          fromOptional(Optional<T> optional, F failureIfEmpty) {<br>
                  return optional<br>
                          .<Result<T,
          F>>map(Result::success)<br>
                          .orElseGet(() ->
          Result.failure(failureIfEmpty));<br>
              }<br>
          <br>
              static <T, F> Result<T, F> requirePresent(<br>
                      Result<Optional<T>, F> result,<br>
                      F failureIfEmpty) {<br>
          <br>
                  return result.flatMap(opt -><br>
                          opt.<Result<T,
          F>>map(Result::success)<br>
                             .orElseGet(() ->
          Result.failure(failureIfEmpty)));<br>
              }<br>
          <br>
              // --- Exception bridge ---<br>
          <br>
              @FunctionalInterface<br>
              interface ThrowingSupplier<T> {<br>
                  T get() throws Throwable;<br>
              }<br>
          <br>
              static <T> Result<T, Throwable>
          catching(ThrowingSupplier<T> supplier) {<br>
                  try {<br>
                      return Result.success(supplier.get());<br>
                  } catch (Throwable t) {<br>
                      return Result.failure(t);<br>
                  }<br>
              }<br>
          <br>
              // --- Lossy projections ---<br>
          <br>
              default Optional<V> success() {<br>
                  return switch (this) {<br>
                      case Success<V, F>(var value) ->
          Optional.ofNullable(value);<br>
                      case Failure<V, F>(var failure) ->
          Optional.empty();<br>
                  };<br>
              }<br>
          <br>
              default Optional<F> failure() {<br>
                  return switch (this) {<br>
                      case Success<V, F>(var value) ->
          Optional.empty();<br>
                      case Failure<V, F>(var failure) ->
          Optional.ofNullable(failure);<br>
                  };<br>
              }<br>
          }</font></p>
    </blockquote>
    <p><font size="4">example</font></p>
    <blockquote>
      <p><font size="4">switch (result) {<br>
              case Result.Success(var value)   -> use(value);<br>
              case Result.Failure(var failure) -> handle(failure);<br>
          }</font></p>
    </blockquote>
    <p><font size="4">Notes:</font></p>
    <ul>
      <li><font size="4">The name java.util.Result may collide with the
          existing java.xml.transform.Result (formerly
          javax.xml.transform.Result). This is not a hard conflict, but
          it does introduce potential import ambiguity and would need to
          be weighed.</font></li>
      <li><font size="4">The example switch uses record patterns and
          pattern matching for switch, which assumes availability of the
          relevant language features (currently preview /
          release-dependent).</font></li>
      <li><font size="4">Null handling is unspecified. As written, both
          Success(null) and Failure(null) are possible, and the lossy
          projections (success() / failure()) would treat null as
          absence. A real API would likely want to either forbid nulls
          or explicitly document this behavior.</font></li>
      <li><font size="4">The instance methods success() and failure()
          (lossy projections to Optional) share names with the static
          factory methods success(V) / failure(E). This is legal but may
          be a source of confusion and could merit different naming.</font></li>
      <li><font size="4">The catching helper currently captures all
          Throwable, including Error. This is intentional for
          discussion, but in a real API it would likely raise questions
          about whether to catch Exception only, or to offer separate
          variants.</font></li>
      <li><font size="4">The ThrowingSupplier interface is included as a
          convenience placeholder. The JDK may or may not want to
          introduce an additional throwing functional interface in
          java.util.</font></li>
      <li><font size="4">Providing mapFailure naturally raises questions
          about whether complementary recovery helpers (e.g., recover,
          recoverWith) should exist. They are intentionally omitted here
          to keep the surface area minimal.</font></li>
      <li><font size="4">The Optional interop helpers (fromOptional,
          requirePresent) are intended to clarify the distinction
          between “operation failed” and “operation succeeded but value
          is absent,” rather than to suggest a particular modeling
          style.</font></li>
    </ul>
    <p><font size="4">Cheers, Eric</font></p>
    <br>
  </body>
</html>