Skip to content

Instantly share code, notes, and snippets.

@Tvaroh
Last active October 18, 2018 08:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tvaroh/3e40d8866c3e7f6dacb9c34f7f393c9d to your computer and use it in GitHub Desktop.
Save Tvaroh/3e40d8866c3e7f6dacb9c34f7f393c9d to your computer and use it in GitHub Desktop.
import java.util.UUID
import cats.data.EitherT
import cats.effect.IO
import cats.implicits._
import cats.{Applicative, ApplicativeError}
import io.circe.syntax._
import io.circe.{Encoder, Json}
object example extends App {
// typeclasses
trait Raise[F[_], E] {
def raise[A](err: E): F[A]
}
object Raise {
implicit def raiseApplicativeError[F[_], E, E1](implicit appErr: ApplicativeError[F, E],
adapt: Adapt[E1, E]): Raise[F, E1] =
new Raise[F, E1] {
override def raise[A](err: E1): F[A] = appErr.raiseError(adapt.adapt(err))
}
}
trait Adapt[E, E1] {
def adapt(e: E): E1
}
object Adapt {
implicit def adaptSubtyping[E, E1](implicit sub: E1 <:< E): Adapt[E1, E] =
(e: E1) => sub(e)
}
// global state
var analyses: Set[UUID] = Set.empty
// interfaces
trait RunAnalysis[F[_]] {
def runAnalysis(value: Int): F[UUID]
}
trait CancelAnalysis[F[_]] {
def cancelAnalysis(id: UUID): F[Unit]
}
// implementations
sealed trait RunAnalysisError {
def errorCode: String
}
object RunAnalysisError {
object NegativeValue extends RunAnalysisError {
override def errorCode: String = "NEGATIVE_VALUE"
}
implicit val encoder: Encoder[RunAnalysisError] =
(err: RunAnalysisError) => Json.obj("errorCode" -> Json.fromString(err.errorCode))
}
class RunAnalysisImpl[F[_]: Applicative](implicit raise: Raise[F, RunAnalysisError])
extends RunAnalysis[F] {
override def runAnalysis(value: Int): F[UUID] =
if (value >= 0) {
val analysisUuid = UUID.randomUUID()
analyses = analyses + analysisUuid
analysisUuid.pure[F]
} else {
raise.raise(RunAnalysisError.NegativeValue: RunAnalysisError)
}
}
sealed trait CancelAnalysisError {
def errorCode: String
}
object CancelAnalysisError {
object AnalysisNotFound extends CancelAnalysisError {
override def errorCode: String = "ANALYSIS_NOT_FOUND"
}
implicit val encoder: Encoder[CancelAnalysisError] =
(err: CancelAnalysisError) => Json.obj("errorCode" -> Json.fromString(err.errorCode))
}
class CancelAnalysisImpl[F[_]: Applicative](implicit raise: Raise[F, CancelAnalysisError])
extends CancelAnalysis[F] {
override def cancelAnalysis(id: UUID): F[Unit] =
if (analyses(id)) {
analyses = analyses - analysisUuid
().pure[F]
}
else raise.raise(CancelAnalysisError.AnalysisNotFound: CancelAnalysisError)
}
// wiring
object wiring {
type F[T, E] = EitherT[IO, E, T]
implicit def raiseAdapt[E: Encoder]: Adapt[E, Json] = (e: E) => e.asJson
val runAnalysis: RunAnalysis[F[?, Json]] =
new RunAnalysisImpl
val cancelAnalysis: CancelAnalysis[F[?, Json]] =
new CancelAnalysisImpl
}
// apps
import wiring._
println(runAnalysis.runAnalysis(-1).value.unsafeRunSync())
// Left({
// "errorCode" : "NEGATIVE_VALUE"
//})
println(cancelAnalysis.cancelAnalysis(UUID.randomUUID()).value.unsafeRunSync())
// Left({
// "errorCode" : "ANALYSIS_NOT_FOUND"
//})
println {
(for {
id <- runAnalysis.runAnalysis(1)
_ <- cancelAnalysis.cancelAnalysis(id)
} yield id).value.unsafeRunSync()
}
// Right(uuid)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment