Forget Optional<T>, adopt Try<T>?

Zhong Yu zhong.j.yu at gmail.com
Tue Jun 4 16:13:36 PDT 2013


Ideally each method can design its own return type that represents all
possible outcomes in the best way, but that requires too much effort.
A lot of methods just want to return T|Exception, so let's provide a
T|Exception type for them. Why Exception though? I argue that
Exception is a familiar type to programmers; it can be lightweight
without stacktrace; it is typed and it can contain structured info; we
may want to capture a thrown Exception and wrap it as a failure, or we
may want to throw a failure as an Exception.

Future/Promise use Throwable to represent action failure, if that
works fine in real world, the same thing should work for immediate
Result too.

I would limit it to Exception though, not the more general Throwable.

Option is more like T|null, and I say we can use T|NoResultException
to replace it.

Zhong Yu


On Tue, Jun 4, 2013 at 5:31 PM, Jed Wesley-Smith <jed at wesleysmith.io> wrote:
> An Exception (or Throwable) on the left is really quite limiting. For
> instance with the case where you want an obvious failure, and you don't need
> to or want to produce a failure value, you'd simply want an Either<Void, A>
> (or just Option<A> but I digress). For many other cases, you want to provide
> richer data-types such as collections of errors if a validation failed like
> Either<Iterable<ValidationFailure<String>>, A>.
>
> The Throwable type is only to be part of the throw/throws machinery, there
> is really no need to limit generally useful kit to it.
>
> cheers,
> jed.
>
>
> On 5 June 2013 02:16, Zhong Yu <zhong.j.yu at gmail.com> wrote:
>>
>> On Tue, Jun 4, 2013 at 2:52 AM, Jed Wesley-Smith <jed at wesleysmith.io>
>> wrote:
>> > Actually Try is a (sort of*) monad on exception handling. It is really
>> > an
>> > Either (or disjunction) class that is specialised to catch exceptions
>> > during
>> > map/flatMap.
>> >
>> > As you point out, Option is isomorphic to an Either with no useful value
>> > on
>> > the left hand side (Unit or Void for instance), and a true Either can
>> > return
>> > something meaningful as to the reason why there was nothing.
>> >
>> > Making something that was returning @Nullable T an Optional<T> simply
>> > says
>> > to the client, "there may not be a return value here". Making it return
>> > an
>> > Either<X, T> says to the client "there may not be a return value here
>> > and X
>> > is why". This is actually not necessary in many cases. Imagine a
>> > Map.getOptional, there is no real reason to supply
>> > BecauseThisMapDidntContainValue on the left hand side if the key isn't
>> > found– it is somewhat obvious.
>>
>> Even though the failure is obvious, what's the harm of returning it
>> anyway? It'll require extra work for API authors, but it doesn't
>> affect API users at all. The failure object might become useful later
>> where we need to take different actions based on different types of
>> failures occurred earlier. Returning a vacuous state to imply the
>> failure only works in the immediate vicinity of the callsite.
>>
>> (By failure I don't mean "error". Failure should be a checked
>> exception, it is a "normal" result of the action)
>>
>> Though we can have both Option and Try, I don't see the reason to keep
>> Option while Try covers all use cases of Option.
>>
>> And finally I propose to rename "Try" to "Result". A Result<T> is
>> either a success with a value of type T, or a failure with an
>> Exception.
>>
>> Zhong Yu
>>
>> >
>> > So, while Either and Option to serve similar purposes, there are
>> > important
>> > differences. Rather than choose one, you really want to have both in the
>> > kit.
>> >
>> > cheers,
>> > jed.
>> >
>> > * it is sort of a monad as a monad should really obey the contravariant
>> > functor laws, and it doesn't due to the fact that composition is
>> > violated:
>> > https://issues.scala-lang.org/browse/SI-6284
>> >
>> >
>> > On 4 June 2013 14:21, Zhong Yu <zhong.j.yu at gmail.com> wrote:
>> >>
>> >> I'm thinking that if we have something like scala's Try<T>, it'll
>> >> express APIs' intentions better than Optional<T>.
>> >>
>> >> Some people view Optional as a collection of either 0 or 1 element.
>> >> That doesn't seem to be what an API author wants to express by
>> >> returning an Optional. The actual intention is that, either the action
>> >> succeeds with a value (not a collection of!), or it fails (with an
>> >> unspecified or implied reason). Try<T> describes that intention
>> >> precisely (but it probably needs a better name)
>> >>
>> >> Suppose we have a good old method that returns a nullable
>> >>
>> >>     /** return null if there's no element */
>> >>     T getFirst();
>> >>
>> >> Why do we hate it? Probably because we need to insert null handling in
>> >> the code path
>> >>
>> >>     T first = getFirst();
>> >>     if(first==null)
>> >>         blah blah
>> >>     first.doSomething()...
>> >>
>> >> we would rather defer null handling to some later time, leaving the
>> >> main code path cleaner. Java already has a way to do that - exception
>> >> propagation. For example the method can be designed to throw
>> >>
>> >>     T getFirst() throw NotFoundExcpetion;
>> >>     // for efficiency, throw a cached immutable exception object
>> >>
>> >>     T first = getFirst();  // may throw - exception will be handled
>> >> somewhere else
>> >>     // no null handling here
>> >>     first.doSomething()
>> >>
>> >> But lots of try-catch blocks may make code look ugly. Try<T> may be
>> >> nicer
>> >>
>> >>     /** fail with NotFoundException if  there's no element */
>> >>     Try<T> getFirst();
>> >>
>> >>     Try<T> first = getFirst();
>> >>     first.map().filter().recover()....
>> >>
>> >> So we don't need Optional. Try<T> does the same thing, and expresses
>> >> the intention better. And Try<T> has broader use cases. It'll also
>> >> appease those who prefer returning errors over throwing errors.
>> >>
>> >> Sounds good?
>> >>
>> >> Zhong Yu
>> >>
>> >
>
>


More information about the lambda-dev mailing list