Untitled
unknown
scala
a year ago
5.5 kB
6
Indexable
package mipt.monads import cats.MonadThrow import cats.data.OptionT import cats.syntax.flatMap.toFlatMapOps import cats.syntax.functor.toFunctorOps import cats.syntax.traverse.toTraverseOps import cats.syntax.applicativeError import scala.concurrent.Future import scala.util.control.NoStackTrace trait UserRepository[F[_]]: def findAll: F[List[User]] def create(name: UserName, age: Age, friends: Set[UserId] = Set.empty): F[User] def delete(userId: UserId): F[Unit] def update(user: User): F[Unit] object UserRepository: case class UserNotFoundError(id: UserId) extends Throwable type Op[F[_], T] = UserRepository[F] => F[T] given [F[_]: MonadThrow]: MonadThrow[Op[F, *]] with override def pure[A](a: A): Op[F, A] = repo => MonadThrow[F].pure(a) override def handleErrorWith[A](fa: Op[F, A])(f: Throwable => Op[F, A]): Op[F, A] = repo => MonadThrow[F].handleErrorWith(fa(repo))(e => f(e)(repo)) override def raiseError[A](e: Throwable): Op[F, A] = repo => MonadThrow[F].raiseError(e) override def flatMap[A, B](fa: Op[F, A])(f: A => Op[F, B]): Op[F, B] = repo => MonadThrow[F].flatMap(fa(repo))(a => f(a)(repo)) override def tailRecM[A, B](a: A)(f: A => Op[F, Either[A, B]]): Op[F, B] = repo => MonadThrow[F].tailRecM(a)(a => f(a)(repo)) object Operations: def findAll[F[_]]: Op[F, List[User]] = _.findAll def create[F[_]](name: UserName, age: Age, friends: Set[UserId] = Set.empty): Op[F, User] = _.create(name, age, friends) def delete[F[_]](userId: UserId): Op[F, Unit] = _.delete(userId) def update[F[_]](user: User): Op[F, Unit] = _.update(user) /** реализуйте композитные методы, используя базовые выше * * для работы с ошибками можно использовать синтаксис из cats.syntax.applicativeError val err: Op[User] = * UserNotFoundError(UserId(1)).raiseError[Op, User] */ /** Метод опционального поиска пользователя */ def findMaybe[F[_]](userId: UserId)(using me: MonadThrow[F]): Op[F, Option[User]] = repo => OptionT(repo.findAll.map(_.find(_.id == userId))).value /** Метод поиска пользователя. Если пользователь не найден, должна генерироваться ошибка UserNotFound */ def find[F[_]](userId: UserId)(using me: MonadThrow[F]): Op[F, User] = repo => findMaybe[F](userId).map(_.getOrElse(throw UserNotFoundError(userId)))(repo) /** Метод добавления друга к пользователю. */ def addFriend[F[_]](currentUserId: UserId, friendId: UserId)(using me: MonadThrow[F] ): Op[F, User] = repo => for { curUser <- find(currentUserId)(using me)(repo) friend <- find(friendId)(using me)(repo) curUserWithFriend = curUser.copy(friends = curUser.friends + friendId) _ <- update(curUserWithFriend)(repo) } yield curUserWithFriend /** Метод удаления друга у пользователя */ def deleteFriend[F[_]](currentUserId: UserId, friendId: UserId)(using me: MonadThrow[F] ): Op[F, User] = repo => for { curUser <- find(currentUserId)(using me)(repo) curUserWithoutFriend = curUser.copy(friends = curUser.friends - friendId) _ <- update(curUserWithoutFriend)(repo) } yield curUserWithoutFriend /** Метод получения всех друзей пользователя */ def getUserFriends[F[_]](userId: UserId)(using me: MonadThrow[F] ): Op[F, List[User]] = repo => for { user <- find(userId)(using me)(repo) friends <- user.friends.toList.traverse(find(_)(using me)(repo)) } yield friends /** Метод получения пользователей, у которых в друзьях только взрослые пользователи */ def getUsersWithAdultOnlyFriends[F[_]]()(using me: MonadThrow[F] ): Op[F, List[User]] = repo => repo.findAll.map(users => users.filter(u => u.friends.nonEmpty && u.friends.forall(fid => users.find(_.id == fid).get.isAdult)) ) /** Метод удаления всех молодых пользователей */ def deleteAllJuniorUsers[F[_]]()(using me: MonadThrow[F] ): Op[F, Unit] = repo => for { users <- repo.findAll juniorUsers = users.filter(!_.isAdult) _ <- juniorUsers.traverse(user => delete(user.id)(repo)) } yield () /** Метод создания сообщества, где все являются друзьями друг для друга. На вход подается список атрибутов * пользователей из сообщества */ def createCommunity[F[_]](community: List[(UserName, Age)])(using me: MonadThrow[F] ): Op[F, List[User]] = repo => for { communityUsers <- community.traverse { case (name, age) => create(name, age)(repo) } updatedCommunityUsers <- communityUsers.traverse { currentUser => val curUserWithFriend = currentUser.copy(friends = communityUsers.filterNot(_ == currentUser).map(_.id).toSet) update(curUserWithFriend)(repo).map(_ => curUserWithFriend) } } yield updatedCommunityUsers
Editor is loading...