-
Cats2 Exercisescala/cats2 2023. 1. 22. 20:35
https://www.geekabyte.io/2018/09/easing-into-cats-and-case-for-category.html 에 나오는 문제의 답안이 scalafiddle 사이트로 연결되지 않아 볼 수가 없다. cats 로 내가 생각한 대로 구현해 본다.
import cats._ import cats.data._ import cats.syntax.all._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration.Duration import scala.concurrent.{Await, ExecutionContext, Future} import scala.util.{Failure, Success, Try} object ScalaApp { // Task 1: Merging two maps def task1(): Unit = { val m1 = Map("k1" -> List("One"), "k2" -> List("Zero")) val m2 = Map("k1" -> List("Two")) val m = m1 |+| m2 println(m) // Map(k1 -> List(One, Two), k2 -> List(Zero)) } // Task 2: Generalise merging of maps def task2(): Unit = { def mergeMaps(maps: Map[String, List[String]]*): Map[String, List[String]] = maps.combineAll val m1 = Map("k1" -> List("One"), "k2" -> List("Zero")) val m2 = Map("k1" -> List("Two")) val m = mergeMaps(m1, m2) println(m) // Map(k1 -> List(One, Two), k2 -> List(Zero)) } // Task 3: Mapping over nested structures def task3(): Unit = { val nested: List[Try[Int]] = List( Success(1), Success(2), Failure(new Throwable("Not valid")), Success(3) ) def mapNested(nested: List[Try[Int]]): Seq[Try[Int]] = Nested(nested).map(_ + 1).value val m: Seq[Try[Int]] = mapNested(nested) println(m) // List(Success(2), Success(3), Failure(java.lang.Throwable: Not valid), Success(4)) } // Task 4: Validate list of values (fail fast scenario) def task4(): Unit = { val listOfInts: List[Int] = (1 to 10).toList val listOfAllEven: List[Int] = List(2, 4, 6, 8) // validation function def isEven(value: Int): Either[String, Int] = if (value % 2 == 0) { Right(value) } else { Left(s"$value is not even") } def validateEven(input: List[Int])(f: Int => Either[String, Int]): Either[String, List[Int]] = input.traverse(f) val r1 = validateEven(listOfInts)(isEven) println(r1) // Left(1 is not even) val r2 = validateEven(listOfAllEven)(isEven) println(r2) // Right(List(2, 4, 6, 8)) } // Task 5: Validation of domain properties (fail slow scenario) def task5(): Unit = { case class Person(name: String, age: Int) def validateAndCreatePerson(name: String, age: Int): Either[Vector[String], Person] = { val nameCheck: Validated[Vector[String], String] = if (name.exists(_.isDigit)) Vector("Name cannot contain numbers").invalid[String] else name.valid[Vector[String]] val ageCheck: Validated[Vector[String], Int] = if (age <= 0) Vector("Age cannot be zero or less than zero").invalid[Int] else age.valid[Vector[String]] (nameCheck, ageCheck).mapN(Person).toEither } validateAndCreatePerson("John", 20) // Right(Person(John,20)) validateAndCreatePerson("John1", 20) // Left(Vector(Name cannot contain numbers)) validateAndCreatePerson("John", -1) // Left(Vector(Age cannot be zero or less than zero)) validateAndCreatePerson( "John1", -1 ) // Left(Vector(Name cannot contain numbers, Age cannot be zero or less than zero)) } // Task 6: File processing def task6(): Unit = { val lines: List[String] = List( "A bird in the hand is worth two in the bush", "Left hand doesn't know what the right hand is doing", "Like a moth to a flame" ) def process(lines: List[String]): (Int, Map[String, Int]) = { val stats: Seq[(Int, Map[String, Int])] = lines.map { line => val cols = line.split(" ") (cols.size, cols.map(key => key -> 1).toMap) } stats.combineAll } process( lines ) // (27,HashMap(Like -> 1, in -> 1, is -> 2, what -> 1, hand -> 2, bird -> 1, doing -> 1, worth -> 1, Left -> 1, flame -> 1, doesn't -> 1, the -> 2, two -> 1, A -> 1, a -> 1, moth -> 1, to -> 1, know -> 1, bush -> 1, right -> 1)) } // Task 7: Chaining asynchronous calls that can return or not return values def task7(): Unit = { // domain objects representing users and membership information case class User(id: Int) case class MembershipInfo(mtype: String) // stub implementation for getUser that only returns a user when called with id of 1 def getUser(id: Int): Option[User] = if (id == 1) { Some(User(id)) } else { None } // stub implementation of getMembershipIfo that only returns membership info when called with a user // with id of 1 def getMembershipInfo(user: User): Future[Option[MembershipInfo]] = if (user.id == 1) { Future.successful(Some(MembershipInfo("Standard"))) } else { Future.successful(None) } // makes use of both getUser and getMembershipInfo to get the membership info given a user id def fetchMembershipInfo(id: Int): Future[Option[MembershipInfo]] = getUser(id).flatTraverse(getMembershipInfo) // def fetchMembershipInfo(id: Int): Future[Option[MembershipInfo]] = // (for { // user <- OptionT.fromOption[Future](getUser(id)) // membership <- OptionT(getMembershipInfo(user)) // } yield membership).value Await.result(fetchMembershipInfo(1), Duration.Inf) // Some(MembershipInfo(Standard)) Await.result(fetchMembershipInfo(2), Duration.Inf) // None } // Extra task: Generalise map def extraTask(): Unit = { def genericMap[F[_]: Monad, A](fa: F[A])(f: A => A): F[A] = fa.map(f) } def main(args: Array[String]): Unit = { // task1() // task2() // task3() // task4() // task5() // task6() // task7() extraTask() } }