Something like Either with triple types to store recoverable errors.

436 views
Skip to first unread message

scala solist

unread,
Feb 18, 2016, 8:06:22 AM2/18/16
to scala-user
I'm using Either to represent a calculation that could fail. But there are two general ways for a calculation to fail: soft and hard. In the first case the caller could recover computing with reasonable default. In the second something horrible occurred  that defeat the purpose of all calculation.

e.g. we need to map such computation over a collection. If it is successful then we add computed element to output collection, if we get soft failure then we recover just skipping the erroneous element, and if the computation returns hard error then we pass the error instead of mapped collection.

I'm in doubt how to code that cases properly. (1) I could use nested Either: Either[S, [Either[F,E]]. That brings all needed functionality from standard library code with cost of extra verbosity. (2) Or I could use single Either[S,F] to store success or recoverable failure and use exceptions to pass irrecoverable errors. It seems concise but I personally dislike exceptions. (3) Or I could define suitable type manually:

sealed trait Computation[+S,+F,+E]
final case class Success[+S,+F,+E](succ : S) extends Computation[S,F,E]
final case class Failure[+S,+F,+E](fail : F) extends Computation[S,F,E]
final case class Error[+S,+F,+E](err : E) extends Computation[S,F,E]

But that would require to write bunch of supporting code, projections, monadic behaviour with map and flatmap an so on.

Is there any ready to use library that implemented such behaviour? It seems like an obvious idea, so someone should already done it. And it would be shame to multiply trivial implementations.

Raul Raja Martinez

unread,
Feb 18, 2016, 8:29:27 AM2/18/16
to scala solist, scala-user
Something like cats.Ior or Validated may work for your use case. http://eed3si9n.com/herding-cats/Ior.html

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Peter Corlett

unread,
Feb 18, 2016, 8:50:38 AM2/18/16
to scala-user
On Thu, Feb 18, 2016 at 05:06:22AM -0800, scala solist wrote:
> I'm using Either to represent a calculation that could fail. But there are
> two general ways for a calculation to fail: soft and hard. In the first case
> the caller could recover computing with reasonable default. In the second
> something horrible occurred that defeat the purpose of all calculation.

This sounds like it could get messy very quickly. Your design also sets policy
in that a soft-failed computation cannot return a value, whereas it can still
be reasonable to return a best-efforts result along with a diagnostic. It's
also unclear what you intend to do with the soft-fail results. If you are
skipping over them, you don't need a Either[Soft, Success] and can just use an
Option[Success].

If you are determined to write this in pure functional form, your proposed
`Computation` trait looks less likely to get messy in use than nested
`Either`s. If nothing else, it is a clean slate to fill with helper methods,
whereas you're stuck with `Either`'s existing API. Try to aim for mechanism
rather than policy when designing your API.

When I've had similar requirements, I've taken a more pragmatic approach and
not bothered with monads at all. Hard failures are signalled by throwing an
exception. Soft failures are recorded by an instance of a custom `Warning`
trait, which is passed into code that may soft fail using implicit parameters.
Computation results are returned unwrapped. We're on the JVM, it's designed for
this.

My `Warning` is typically a type alias for or subclass of `String => Unit`, the
threaded variable called `warn`, and so code that signals soft failure is
written `warn("something's wrong")`. Concrete implementations of `Warning` turn
soft-fails into an exception during development, and log them in production.

This is all nice and simple, for which any maintenance programmer which comes
afterwards will be thankful.

Kevin Wright

unread,
Feb 18, 2016, 8:57:52 AM2/18/16
to scala solist, scala-user
`recover` takes a partial function, so you can simply create a subclass of `Exception` for your soft-failures and only match that type in recover.

No need to over-engineer this!

--

Rex Kerr

unread,
Feb 18, 2016, 9:04:40 AM2/18/16
to scala solist, scala-user
I did this, almost (the error type was just a wrapped Throwable), and I found it too awkward to use.

I reverted to a type equivalent to Either[Either[A, B], C].  (I have my own Either-like sum type that is a little less clunky to use--pretty similar to Or and Xor, if you know those.)

The "just use an exception" strategy sounds like not a bad one given how bad you think the hard errors are.  You could also use Try[Either[F, S]].  (It is the custom to put the "correct" value on the right side, because of the other meaning of right in English.)

  --Rex


--
Reply all
Reply to author
Forward
0 new messages