Skip to content

Instantly share code, notes, and snippets.

@fanf
Last active November 22, 2017 10:27
Show Gist options
  • Save fanf/88eb2f97370e20f68c5a0ad810f35c76 to your computer and use it in GitHub Desktop.
Save fanf/88eb2f97370e20f68c5a0ad810f35c76 to your computer and use it in GitHub Desktop.
Handling and composing errors
object TypicalUseCase {
final case class UUID(value: String)
final case class DomainThingy(stuff: String)
final case class ValidatedDomainThingy(moreStuff: String)
// in some module or part of the system
sealed trait MyParserError { def msg: String }
final case class IOError(val msg: String, ex: Throwable) extends MyParserError
final case class ParsingError(val msg: String, token: String) extends MyParserError
def read(file: File): Either[IOError, String] = ???
def parse(input: String): Either[ParsingError, DomainThingy] = ???
// in some other part of the system
sealed trait SomeBusinessError { def msg: String }
final case class BadLogicError(val msg: String) extends SomeBusinessError
def validate(thing: DomainThingy) : Either[BadLogicError, ValidatedDomainThingy] = ???
//somewhere else again
def doInParallel(lotsOfFiles: Set[(UUID,File)])(doStuff: (UUID,File) => Either[BadLogicError, ValidatedDomainThingy]): Either[Set[SomeBusinessError], Set[ValidatedDomainThingy]] = ???
val lotOfFiles: Set[(UUID, File)] = ???
for {
// doInParallel is most likelly applicative, and I want to handle errors in a monoïdale way (aggregate)
res <- doInParallel(lotOfFiles) { (id, f) =>
// obviously the error types don't align, so I also need some kind of transformer from
// MyParserError => SomeBusinessError
for {
// here I would like to add a more precise message to contextualize where/what happen in case of error.
// lifweb Box has a "?~!" that left map + aggregate msg + stack previous error
// so perhaps it's not a "msg: String", but a "messages: NEL + msg = NEL.head"
r <- read(f) // ?~! s"Something bad happen with Business ID '${id.value}'"
d <- parse(r) // ?~! s"Oh no! Can parse data for ID '${id.value}' coming from file '${f.getName()}'"
v <- validate(d) // ?~! s"Woops, that is coming from file '${f.getName()}'"
} yield {
v
}
}
}
// and then you want to log/process errors, whatever their type but there is common patterns:
// - the messages will be displayed
// - perhaps display a kind of stack-like list of message from the root cause
// - if exception where stored, perhaps special process them on debug mode
// - yes, yes, it awfully looks like re-inventing Exception
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment