Error handling with Task and Process

185 views
Skip to first unread message

Laurence Rouesnel

unread,
Jan 7, 2015, 10:06:35 PM1/7/15
to sca...@googlegroups.com
Just wanted to canvas the community's thoughts on how to return errors when working with Task[T] and Process[Task, T].

Being explicit about errors/exceptions via types seems to be a good idea so normally you might do something like MyDomainError \/ DomainValue where MyDomainError might inherit or contain an exception.

However, Task already wraps an Exception (Task[T] ==> Throwable \/ Future[T]) so there's double handling there.

My current thinking is to use Task[MyDomainError \/ DomainValue] and have a combinator to flatten it out to Task[DomainValue] if the caller doesn't care about the custom error type.

There are similar design issues with Process where the errors are just Throwable rather than any kind of specific error type.

Any thoughts on the above approach - has any found a nice way to deal with errors in these cases?

Peter Salanki

unread,
Jan 7, 2015, 11:46:54 PM1/7/15
to sca...@googlegroups.com
+1

I would also like to hear some best practices around this. Having to deal with both domain errors and throwables is a bit of pain.

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

Tony Morris

unread,
Jan 7, 2015, 11:57:50 PM1/7/15
to sca...@googlegroups.com
The use of domain-specific errors is an approach that has been successfully executed in the argonaut project.

If you consider a cursor that moves around a JSON data type object in an effort to decode to a user-specific type (e.g. case class Person(name: String)), then in doing so, the cursor may fail. For example, you might say, move the cursor down, then look for the 'name' key and the string value that is found at that point is the name of the Person. Possible failures are:

* moving the cursor down, but there is nothing in the JSON data type further down
* the name key does not exist
* The name key exists, but the corresponding value is not a string (that can be used for the Person data type)

Of course, there are other failure modes with respect to "moving a cursor around a JSON data type."

Here is the data type that represents all of the failure types:
https://github.com/argonaut-io/argonaut/blob/master/src/main/scala/argonaut/CursorOpElement.scala

Here is the data type that wraps up all of the decoding failures (simply, a List):
https://github.com/argonaut-io/argonaut/blob/master/src/main/scala/argonaut/CursorHistory.scala

Here is the data type that encapsulates the idea of, "either a succeeding value, or a list of errors":
https://github.com/argonaut-io/argonaut/blob/master/src/main/scala/argonaut/DecodeResult.scala

Notice also a String that is associated with the failing value. This is similar in your case to Throwable, because if you are against a legacy API that uses Throwable, you might want to also carry that information to provide the user, even though it is nowhere near as useful as a more precise error data type.

Here is the data type that actually decodes a JSON value to a user-specific data type, with that potential for error:
https://github.com/argonaut-io/argonaut/blob/master/src/main/scala/argonaut/DecodeJson.scala

Hope that helps.

Brian McKenna

unread,
Jan 8, 2015, 9:13:42 AM1/8/15
to sca...@googlegroups.com
Take a look at scalaz.stream.Writer - it's exactly a Process[F, W \/
O] but gives some very useful functions for working with each side:

https://github.com/scalaz/scalaz-stream/blob/master/src/main/scala/scalaz/stream/Process.scala#L1423
Reply all
Reply to author
Forward
0 new messages