Created
March 17, 2019 09:41
-
-
Save fanf/dee9696af98a22c7fae659e12892989f to your computer and use it in GitHub Desktop.
This file contains 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
/* | |
* My application is splitted in several modules, possibly independently built and | |
* distributed. | |
* I have a Base error trait for the whole application. | |
* | |
* Each module has its own business error defined by a module base error trait which | |
* extends BaseError. | |
* | |
* I want to have a common framework to manage bridging errors between modules so | |
* that a module can use the other ones and subsume error type to its own, especially | |
* in the canonical exemple: | |
* | |
* ``` | |
* for { | |
* a <- module1 | |
* b <- module2 | |
* } yield { | |
* ... | |
* } : error of kind module 3 | |
* ``` | |
* | |
* The bridging could be done with a "chain" error kind that encapsulated one error module | |
* in an other: `ErrorModule2.Chain("some explanation", errorModule1)` | |
* | |
* Finally, I want minimum boilerplate for that. How is it usually done? | |
*/ | |
object errors { | |
trait BaseError { | |
def msg: String | |
} | |
trait BaseChainError[E <: BaseError] extends BaseError { | |
def cause: E | |
def hint: String | |
def msg = s"${hint}; cause was: ${cause.getClass.getSimpleName}: ${cause.msg}" | |
} | |
} | |
object TestImplicits { | |
import scalaz.zio._ | |
import scalaz.zio.syntax._ | |
object module1 { | |
import com.normation.errors._ | |
sealed trait M_1_Error extends BaseError | |
object M_1_Error { | |
final case class BusinessError1(msg: String) extends M_1_Error | |
final case class Chained[E <: BaseError](hint: String, cause: E) extends M_1_Error with BaseChainError[E] | |
} | |
object M_1_Result { | |
// implicits ? | |
} | |
object service1 { | |
def doStuff(param: String): IO[M_1_Error, String] = param.succeed | |
} | |
} | |
object module2 { | |
import com.normation.errors._ | |
sealed trait M_2_Error extends BaseError | |
object M_2_Error { | |
final case class MissingImportantStuff(msg: String) extends M_2_Error | |
final case class Chained[E <: BaseError](hint: String, cause: E) extends M_2_Error with BaseChainError[E] | |
} | |
object M_2_Result { | |
// implicits | |
} | |
object service2 { | |
def doStuff(param: Int): IO[M_2_Error, Int] = param.succeed | |
} | |
} | |
object testModule { | |
import module1._ | |
import module2._ | |
import com.normation.errors._ | |
sealed trait M_3_Error extends BaseError | |
object M_3_Error { | |
final case class Oups(msg: String) extends M_3_Error | |
final case class Chained[E <: BaseError](hint: String, cause: E) extends M_3_Error with BaseChainError[E] | |
} | |
object M_3_Result { | |
// implicits ? | |
} | |
import module1.M_1_Result._ | |
import module2.M_2_Result._ | |
import M_3_Result._ | |
/* | |
* I would like all of that to be possible, with the minimum boilerplate, | |
* and the maximum homogeneity between modules | |
*/ | |
object service { | |
// need error adpatation 1 toward 2 | |
def test0(a: String): IO[M_2_Error, Int] = service1.doStuff(a) | |
// need error adpatation 1 toward 3 | |
def test1(a: String): IO[M_3_Error, Int] = service1.doStuff(a) | |
// need error adpatation 1 and 2 toward 3 | |
def test2(a: String, b: Int): IO[M_3_Error, (String, Int)] = { | |
for { | |
x <- service1.doStuff(a) | |
y <- service2.doStuff(b) | |
} yield { | |
(x, y) | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment