Created
March 19, 2019 07:39
-
-
Save vkorenev/21bdd7d57e81a0752972f4bb3f45398a to your computer and use it in GitHub Desktop.
Working with monad transformer stacks using cats-mtl
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
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