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.