Last active
November 22, 2017 10:27
-
-
Save fanf/88eb2f97370e20f68c5a0ad810f35c76 to your computer and use it in GitHub Desktop.
Handling and composing errors
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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