Skip to content

Instantly share code, notes, and snippets.

@vkorenev
Created March 19, 2019 07:39
Show Gist options
  • Save vkorenev/21bdd7d57e81a0752972f4bb3f45398a to your computer and use it in GitHub Desktop.
Save vkorenev/21bdd7d57e81a0752972f4bb3f45398a to your computer and use it in GitHub Desktop.
Working with monad transformer stacks using cats-mtl
import cats._
import cats.data._
import cats.instances.list._
import cats.instances.parallel._
import cats.mtl._
import cats.mtl.implicits._
import cats.syntax.all._
import scala.language.higherKinds
case class User(name: String, age: Int, address: String)
object ValidationMTL {
type Errors = NonEmptyChain[String]
private val Errors = NonEmptyChain
type Params = Map[String, String]
def getStr[M[_]](key: String)(
implicit
E: MonadError[M, Errors],
A: ApplicativeAsk[M, Params],
W: FunctorTell[M, List[String]]
): M[String] =
for {
params <- A.ask
value <- E.fromOption(params.get(key), Errors(s"$key is not provided"))
_ <- W.tell(List(s"""Read "$value" for $key"""))
} yield value
def getNonEmptyStr[M[_]](key: String)(
implicit
E: MonadError[M, Errors],
A: ApplicativeAsk[M, Params],
W: FunctorTell[M, List[String]]
): M[String] = getStr(key).ensure(Errors(s"$key is empty"))(_.nonEmpty)
def parseInt[F[_]](s: String)(implicit F: ApplicativeError[F, Errors]): F[Int] =
F.fromEither(
Either.catchOnly[NumberFormatException](s.toInt).leftMap(Function.const(Errors(s"$s is not a number")))
)
def getInt[M[_]](key: String)(
implicit
E: MonadError[M, Errors],
A: ApplicativeAsk[M, Params],
W: FunctorTell[M, List[String]]
): M[Int] = getNonEmptyStr(key).flatMap(parseInt[M])
def getUser[M[_], F[_]](
implicit
E: MonadError[M, Errors],
R: ApplicativeAsk[M, Params],
W: FunctorTell[M, List[String]],
P: Parallel[M, F]
): M[User] = (getNonEmptyStr("name"), getInt("age"), getNonEmptyStr("address")).parMapN(User)
def main(args: Array[String]): Unit = {
type Stack[A] = EitherT[WriterT[Reader[Params, ?], List[String], ?], Errors, A]
val params = Map("name" -> "", "age" -> "aa")
val (debug, result) =
getUser[Stack, Nested[WriterT[Reader[Params, ?], List[String], ?], Validated[Errors, ?], ?]].value.run
.run(params)
println(s"debug = $debug")
println(s"result = $result")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment