
.some and the corresponding constructor method none for Option ;.asRight , .asLeft for Either ;.valid , .invalid , .validNel , .invalidNel for Validated import cats.implicits._ Some("a") //Some[String] "a".some //Option[String] Either (see chapter 4.4.2 of the book Scala with Cats )..asRight and .asLeft still have one type parameter. For example, "1".asRight[Int] is Either[Int, String] . If you do not provide this parameter, the compiler will try to output it and get Nothing . Still, it is more convenient than either to provide both parameters each time or not to provide either one, as in the case of constructors.Apply method (that is, in Applicative , Monad , etc.), simply means “process the original calculation and replace the result with what is specified in the second argument”. In terms of code (in the case of Monad ): fa.flatMap(_ => fb) Either : import cats.implicits._ val success1 = "a".asRight[Int] val success2 = "b".asRight[Int] val failure = 400.asLeft[String] success1 *> success2 //Right(b) success2 *> success1 //Right(a) success1 *> failure //Left(400) failure *> success1 //Left(400) Monix , IO and other similar Monix . success1 <* success2 //Right(a) productR alias, and <is the productL alias.productR ), there is also >> from FlatMapSyntax . >> is defined in the same way as fa.flatMap(_ => fb) , but with two nuances:productR , and therefore, if for any reason the contract of this method changes (theoretically, it can be changed without violating monadic laws, but I am not sure about MonadError ), you will not suffer;fb: => F[B] . The difference in semantics becomes fundamental if you perform calculations that can lead to a stack explosion.lift . But when you succeed, you will find that it is everywhere.lift comes from category theory . I will try to explain: take an operation, change the signature of its type so that it becomes directly related to the abstract type F. def lift[A, B](f: A => B): F[A] => F[B] = map(_)(f) EitherT.liftF is essentially EitherT.right. Scaladoc example : import cats.data.EitherT import cats.implicits._ EitherT.liftF("a".some) //EitherT(Some(Right(a))) EitherT.liftF(none[String]) //EitherT(None) lift present in the standard Scala library everywhere. The most popular (and, perhaps, the most useful in daily work) example is PartialFunction : val intMatcher: PartialFunction[Int, String] = { case 1 => "jak się masz!" } val liftedIntMatcher: Int => Option[String] = intMatcher.lift liftedIntMatcher(1) //Some(jak się masz!) liftedIntMatcher(0) //None intMatcher(1) //jak się masz! intMatcher(0) //Exception in thread "main" scala.MatchError: 0 mapN is a useful helper function for working with tuples. Again, this is not a novelty, but a replacement for the good old operator |@| , he's “Scream”. // where t2: Tuple2[F[A0], F[A1]] def mapN[Z](f: (A0, A1) => Z)(implicit functor: Functor[F], semigroupal: Semigroupal[F]): F[Z] = Semigroupal.map2(t2._1, t2._2)(f) import cats.implicits._ ("a".some, "b".some).mapN(_ ++ _) //Some(ab) (List(1, 2), List(3, 4), List(0, 2).mapN(_ * _ * _)) //List(0, 6, 0, 8, 0, 12, 0, 16) leftmap for tuples: ("a".some, List("b","c").mapN(_ ++ _)) //won't compile, because outer type is not the same ("a".some, List("b", "c")).leftMap(_.toList).mapN(_ ++ _) //List(ab, ac) .mapN is instantiating case classes: case class Mead(name: String, honeyRatio: Double, agingYears: Double) ("półtorak".some, 0.5.some, 3d.some).mapN(Mead) //Some(Mead(półtorak,0.5,3.0)) import cats.effect.IO import cats.implicits._ //interchangable with eg Monix's Task type Query[T] = IO[Option[T]] def defineMead(qName: Query[String], qHoneyRatio: Query[Double], qAgingYears: Query[Double]): Query[Mead] = (for { name <- OptionT(qName) honeyRatio <- OptionT(qHoneyRatio) agingYears <- OptionT(qAgingYears) } yield Mead(name, honeyRatio, agingYears)).value def defineMead2(qName: Query[String], qHoneyRatio: Query[Double], qAgingYears: Query[Double]): Query[Mead] = for { name <- qName honeyRatio <- qHoneyRatio agingYears <- qAgingYears } yield (name, honeyRatio, agingYears).mapN(Mead) Nested - in fact, a generalizing twin monadny transformers. As the name suggests, it allows you to perform attachments under certain conditions. Here is an example for .map(_.map( : import cats.implicits._ import cats.data.Nested val someValue: Option[Either[Int, String]] = "a".asRight.some Nested(someValue).map(_ * 3).value //Some(Right(aaa)) Functor , Nested summarizes the operations Applicative , ApplicativeError and Traverse . Additional information and examples are here .ApplicativeError and MonadError have several useful methods, and it may be useful for you to learn the subtle differences between the main four. So, with ApplicativeError F[A]:handleError converts all errors at the dial peer to A according to the specified function.recover works in a similar way, but accepts partial functions, and therefore can convert to A errors you have selected.handleErrorWith is similar to handleError , but its result should look like F[A] , which means it helps you convert errors.recoverWith acts as recover, but also requires F[A] as a result.handleErrorWith and recoverWith , which cover all possible functions. However, each method has its advantages and is convenient in its own way.cats.effect.IO , monix.Task , etc.Either/EitherT , Validated and Ior - .valueOr . In fact, it works like .getOrElse for Option , but it is generalized for classes that contain something “left”. import cats.implicits._ val failure = 400.asLeft[String] failure.valueOr(code => s"Got error code $code") //"Got error code 400" Try most popular in this project, because Try , as you know, does not satisfy all monadic laws in terms of fatal errors. Now he is truly represented in the Cats.cats.x for basic (kernel) types;cats.data for data types like Validated, monad transformers, etc .;cats.instances.x. _ to directly import the implementation of various timeclasses to the implicit scope for individual concrete types, so that when calling, for example, sth.pure, the error "implicit not found" does not occur.cats.implicits._ import, which imports all the syntax and all instances of the type class into the implicit scope. import cats._ import cats.data._ import cats.implicits._ cats.syntax.x provides the extension syntax related to x;cats.instances.x provides the cats.instances.x instances..asRight , which is an .asRight method for Either , do the following: import cats.syntax.either._ "a".asRight[Int] //Right[Int, String](a) Option.pure you must import cats.syntax.monad And cats.instances.option : import cats.syntax.applicative._ import cats.instances.option._ "a".pure[Option] //Some(a) // , `pure`, // import cats.implicits._ import cats.instances.option._ "a".pure[Option] //could not find implicit value for parameter F: cats.Applicative[Option] cats.implicits and cats.instances.option are extensions of cats.instances.OptionInstances . In fact, we import its implicit scope twice, which confuses the compiler.cats.implicits and explore the type hierarchy.cats and scalaz actively updated. Take cats as an example. Here are just the latest changes:Source: https://habr.com/ru/post/448128/
All Articles