Format
This commit is contained in:
parent
310b3961ad
commit
c1099c97f9
|
@ -13,35 +13,27 @@ case class Error(error: String)
|
|||
type WriterTF[F[_], L] = [A] =>> WriterT[F, L, A]
|
||||
case class LogEmbed[F[_], A](value: WriterT[WriterTF[WriterTF[F, Vector[Debug]], Vector[Info]], Vector[Error], A])
|
||||
|
||||
/**
|
||||
* I. Продвинутый логгер
|
||||
*
|
||||
* В этом задании вам предстоит реализовать логгер, имеющий 3 уровня записей - Debug, Info и Error.
|
||||
* Структура лога LogEmbed описана выше, от вас же требуется реализовать функции для удобного создания логов
|
||||
/** I. Продвинутый логгер
|
||||
*
|
||||
* В этом задании вам предстоит реализовать логгер, имеющий 3 уровня записей - Debug, Info и Error. Структура лога
|
||||
* LogEmbed описана выше, от вас же требуется реализовать функции для удобного создания логов
|
||||
*/
|
||||
final case class Logger[F[_]: Monad]():
|
||||
/**
|
||||
* I.1) Реализовать функцию debug
|
||||
*
|
||||
* Функция должна принимать сообщение уровня debug и возвращать лог,
|
||||
* содержащий только это сообщение на правильном уровне
|
||||
/** I.1) Реализовать функцию debug
|
||||
*
|
||||
* Функция должна принимать сообщение уровня debug и возвращать лог, содержащий только это сообщение на правильном
|
||||
* уровне
|
||||
*/
|
||||
def debug(debug: String): LogEmbed[F, Unit] = ???
|
||||
|
||||
/**
|
||||
* I.1) Реализовать функцию info
|
||||
/** I.1) Реализовать функцию info
|
||||
*
|
||||
* Функция аналогична предыдущей за исключением того, что уровень лога меняется на Info
|
||||
*
|
||||
*/
|
||||
def info(info: String): LogEmbed[F, Unit] = ???
|
||||
|
||||
/**
|
||||
* I.1) Реализовать функцию error
|
||||
/** I.1) Реализовать функцию error
|
||||
*
|
||||
* Функция-аналог предыдущих двух для уровня Error
|
||||
*
|
||||
*/
|
||||
def error(error: String): LogEmbed[F, Unit] = ???
|
||||
|
|
|
@ -4,14 +4,11 @@ import cats.Monad
|
|||
import cats.data.{Reader, ReaderT}
|
||||
import cats.syntax.applicative.*
|
||||
|
||||
/**
|
||||
* II. Интерфейс Ask
|
||||
*
|
||||
* Теперь от вас требуется реализовать несколько интерфейсов Ask для некоторых монад.
|
||||
* Интерфейс служит для получения контекстной информации, хранящейся в монаде
|
||||
* (подробнее о данной монаде можно прочить на сайте библиотеки cats,
|
||||
* а в одном из следующих заданий будут примеры применения)
|
||||
/** II. Интерфейс Ask
|
||||
*
|
||||
* Теперь от вас требуется реализовать несколько интерфейсов Ask для некоторых монад. Интерфейс служит для получения
|
||||
* контекстной информации, хранящейся в монаде (подробнее о данной монаде можно прочить на сайте библиотеки cats, а в
|
||||
* одном из следующих заданий будут примеры применения)
|
||||
*/
|
||||
trait Ask[F[_], R]:
|
||||
def ask: F[R]
|
||||
|
@ -19,18 +16,14 @@ trait Ask[F[_], R]:
|
|||
object Ask:
|
||||
def apply[F[_], R](using ask: Ask[F, R]): Ask[F, R] = ask
|
||||
|
||||
/**
|
||||
* II.1) Интерфейс Ask для монады Reader
|
||||
/** II.1) Интерфейс Ask для монады Reader
|
||||
*
|
||||
* Реализуйте описанный выше интерфейс, возвращающий контекст, содержащийся в монаде Reader
|
||||
*
|
||||
*/
|
||||
given [R]: Ask[Reader[R, *], R] = ???
|
||||
|
||||
/**
|
||||
* II.2) Интерфейс Ask для трансформера ReaderT
|
||||
/** II.2) Интерфейс Ask для трансформера ReaderT
|
||||
*
|
||||
* Реализуйте описанный выше интерфейс, возвращающий контекст, содержащийся в трансформере ReaderT
|
||||
*
|
||||
*/
|
||||
given [F[_]: Monad, R]: Ask[ReaderT[F, R, *], R] = ???
|
||||
|
|
|
@ -4,12 +4,10 @@ import cats.{Monad, Monoid}
|
|||
import cats.data.{Writer, WriterT}
|
||||
import cats.syntax.applicative.*
|
||||
|
||||
/**
|
||||
* III. Интерфейс Tell
|
||||
*
|
||||
* Данное задание очень похоже на предыдущее, только наш интерфейс будет не возвращать контекст,
|
||||
* а, наоборот, отдавать дополнительную информацию во вне
|
||||
/** III. Интерфейс Tell
|
||||
*
|
||||
* Данное задание очень похоже на предыдущее, только наш интерфейс будет не возвращать контекст, а, наоборот, отдавать
|
||||
* дополнительную информацию во вне
|
||||
*/
|
||||
trait Tell[F[_], W]:
|
||||
def tell(log: W): F[Unit]
|
||||
|
@ -17,18 +15,14 @@ trait Tell[F[_], W]:
|
|||
object Tell:
|
||||
def apply[F[_], W](using tell: Tell[F, W]): Tell[F, W] = tell
|
||||
|
||||
/**
|
||||
* III.1) Интерфейс Tell для Writer
|
||||
/** III.1) Интерфейс Tell для Writer
|
||||
*
|
||||
* Реализуйте интерфейс Tell так, чтобы полученное сообщение добавлялось в лог монады
|
||||
*
|
||||
*/
|
||||
given [W: Monoid]: Tell[Writer[W, *], W] = ???
|
||||
|
||||
/**
|
||||
* III.2) Интерфейс Tell для WriterT
|
||||
/** III.2) Интерфейс Tell для WriterT
|
||||
*
|
||||
* Реализуйте интерфейс Tell так, чтобы полученное сообщение добавлялось в лог трансформера
|
||||
*
|
||||
*/
|
||||
given [F[_]: Monad, W: Monoid]: Tell[WriterT[F, W, *], W] = ???
|
||||
|
|
|
@ -12,7 +12,9 @@ case class Config(chunkSize: Int)
|
|||
|
||||
trait UserRepositoryDao:
|
||||
def findAll(config: Config): List[User]
|
||||
def create(name: UserName, age: Age, friends: Set[UserId] = Set.empty)(config: Config): Either[UserAlreadyExists, User]
|
||||
def create(name: UserName, age: Age, friends: Set[UserId] = Set.empty)(
|
||||
config: Config
|
||||
): Either[UserAlreadyExists, User]
|
||||
def delete(userId: UserId)(config: Config): Either[UserDoesNotExists, Unit]
|
||||
def update(user: User)(config: Config): Either[UserDoesNotExists, Unit]
|
||||
|
||||
|
@ -22,51 +24,38 @@ trait UserRepository[F[_]]:
|
|||
def delete(userId: UserId): F[Unit]
|
||||
def update(user: User): F[Unit]
|
||||
|
||||
/**
|
||||
* IV. Обёртка для работы с базой данных
|
||||
*
|
||||
* В последнем задании рассмотрим более практичный пример:
|
||||
* пусть есть интерфейс UserRepositoryDao, который обеспечивает работу с базой данных, обновляя данные о пользователях
|
||||
* (модель пользователей и операции, проводимые с ними, описываются кодом в данном файле и в User.scala, UserErrors.scala).
|
||||
* От вас требуется сделать обёртку над данным интерфейсом, чтобы его "грязные" функции можно было бы использовать
|
||||
* в коде в функциональном стиле.
|
||||
* При написании используйте интерфейсы MonadThrow и Ask - такой подход часто называется Tagless final
|
||||
/** IV. Обёртка для работы с базой данных
|
||||
*
|
||||
* В последнем задании рассмотрим более практичный пример: пусть есть интерфейс UserRepositoryDao, который обеспечивает
|
||||
* работу с базой данных, обновляя данные о пользователях (модель пользователей и операции, проводимые с ними,
|
||||
* описываются кодом в данном файле и в User.scala, UserErrors.scala). От вас требуется сделать обёртку над данным
|
||||
* интерфейсом, чтобы его "грязные" функции можно было бы использовать в коде в функциональном стиле. При написании
|
||||
* используйте интерфейсы MonadThrow и Ask - такой подход часто называется Tagless final
|
||||
*/
|
||||
object UserRepositoryDao:
|
||||
def apply[F[_]: MonadThrow](dao: UserRepositoryDao)(using Ask[F, Config]): UserRepository[F] = new UserRepository[F]:
|
||||
/**
|
||||
* IV.1) Фукнция findAll
|
||||
*
|
||||
* Для данной функции требуется, используя интерфейс Ask из библиотеки Cats
|
||||
* (полностью аналогичный варианту из предыдущего задания), получить конфиг, хранящийся в монаде F,
|
||||
* и вернуть значение, обёрнутое в монаду
|
||||
/** IV.1) Фукнция findAll
|
||||
*
|
||||
* Для данной функции требуется, используя интерфейс Ask из библиотеки Cats (полностью аналогичный варианту из
|
||||
* предыдущего задания), получить конфиг, хранящийся в монаде F, и вернуть значение, обёрнутое в монаду
|
||||
*/
|
||||
override def findAll: F[List[User]] = ???
|
||||
|
||||
/**
|
||||
* IV.2) Фукнция create
|
||||
*
|
||||
* Для этой функции аналогично нужно получить конфиг,
|
||||
* а так же дополнительно обработать возможные ошибки при помощи интерфейса MonadThrow
|
||||
* (примеры использования можно найти на сайте cats)
|
||||
/** IV.2) Фукнция create
|
||||
*
|
||||
* Для этой функции аналогично нужно получить конфиг, а так же дополнительно обработать возможные ошибки при помощи
|
||||
* интерфейса MonadThrow (примеры использования можно найти на сайте cats)
|
||||
*/
|
||||
override def create(name: UserName, age: Age, friends: Set[UserId]): F[User] = ???
|
||||
|
||||
/**
|
||||
* IV.3) Функция delete
|
||||
/** IV.3) Функция delete
|
||||
*
|
||||
* Данная функция аналогично требует получения конфига и обработки ошибки
|
||||
*
|
||||
*/
|
||||
override def delete(userId: UserId): F[Unit] = ???
|
||||
|
||||
/**
|
||||
* IV.4) Функция update
|
||||
/** IV.4) Функция update
|
||||
*
|
||||
* Для последней функции задание аналогично
|
||||
*
|
||||
*/
|
||||
override def update(user: User): F[Unit] = ???
|
||||
|
|
|
@ -8,7 +8,9 @@ import scala.util.{Success, Try}
|
|||
|
||||
class Tests extends AnyFlatSpec with Matchers:
|
||||
def logEmbed(debug: Vector[String], info: Vector[String], error: Vector[String]): LogEmbed[Try, Unit] =
|
||||
LogEmbed(WriterT(WriterT(WriterT(Success((debug.map(Debug.apply), (info.map(Info.apply), (error.map(Error.apply), ()))))))))
|
||||
LogEmbed(
|
||||
WriterT(WriterT(WriterT(Success((debug.map(Debug.apply), (info.map(Info.apply), (error.map(Error.apply), ())))))))
|
||||
)
|
||||
|
||||
it should "info log" in {
|
||||
val logger = Logger[Try]()
|
||||
|
|
|
@ -25,10 +25,11 @@ object TestsData:
|
|||
lastConfig = Right(config)
|
||||
usersList.toList
|
||||
|
||||
override def create(name: UserName, age: Age, friends: Set[UserId])(config: Config): Either[UserAlreadyExists, User] =
|
||||
override def create(name: UserName, age: Age, friends: Set[UserId])(
|
||||
config: Config
|
||||
): Either[UserAlreadyExists, User] =
|
||||
lastConfig = Right(config)
|
||||
if (usersList.exists(_.name == name))
|
||||
Left(UserAlreadyExists(name))
|
||||
if (usersList.exists(_.name == name)) Left(UserAlreadyExists(name))
|
||||
else
|
||||
val user = User(UserId(usersList.size), name, age, friends)
|
||||
usersList += user
|
||||
|
@ -39,8 +40,7 @@ object TestsData:
|
|||
if (usersList.exists(_.id == userId))
|
||||
usersList -= usersList.find(_.id == userId).get
|
||||
Right(())
|
||||
else
|
||||
Left(UserDoesNotExists(userId))
|
||||
else Left(UserDoesNotExists(userId))
|
||||
|
||||
override def update(user: User)(config: Config): Either[UserDoesNotExists, Unit] =
|
||||
lastConfig = Right(config)
|
||||
|
@ -48,8 +48,7 @@ object TestsData:
|
|||
usersList -= usersList.find(_.id == user.id).get
|
||||
usersList += user
|
||||
Right(())
|
||||
else
|
||||
Left(UserDoesNotExists(user.id))
|
||||
else Left(UserDoesNotExists(user.id))
|
||||
|
||||
type M = [A] =>> Config => Either[Throwable, A]
|
||||
|
||||
|
@ -58,12 +57,14 @@ object TestsData:
|
|||
override def pure[A](x: A): M[A] = _ => Right(x)
|
||||
override def flatMap[A, B](fa: M[A])(f: A => M[B]): M[B] = c => fa(c).flatMap(a => f(a)(c))
|
||||
override def tailRecM[A, B](a: A)(f: A => M[Either[A, B]]): M[B] =
|
||||
c => f(a)(c) match
|
||||
c =>
|
||||
f(a)(c) match
|
||||
case Left(e) => Left(e)
|
||||
case Right(Left(a)) => tailRecM(a)(f)(c)
|
||||
case Right(Right(b)) => Right(b)
|
||||
override def raiseError[A](e: Throwable): M[A] = _ => Left(e)
|
||||
override def handleErrorWith[A](fa: M[A])(f: Throwable => M[A]): M[A] = c => fa(c) match
|
||||
override def handleErrorWith[A](fa: M[A])(f: Throwable => M[A]): M[A] = c =>
|
||||
fa(c) match
|
||||
case Left(e) => f(e)(c)
|
||||
case Right(a) => Right(a)
|
||||
|
||||
|
@ -121,7 +122,7 @@ class Tests extends AnyFlatSpec with Matchers:
|
|||
it should "not create user more than once" in {
|
||||
val repository = UserRepositoryDao[M](dao(sampleUsers))
|
||||
val config = Config(46)
|
||||
repository.create(UserName("Subject #2"), Age(0), Set.empty)(config) shouldBe a [Left[Throwable, User]]
|
||||
repository.create(UserName("Subject #2"), Age(0), Set.empty)(config) shouldBe a[Left[Throwable, User]]
|
||||
}
|
||||
|
||||
it should "delete existing user" in {
|
||||
|
@ -133,7 +134,7 @@ class Tests extends AnyFlatSpec with Matchers:
|
|||
it should "not delete nonexistent user" in {
|
||||
val repository = UserRepositoryDao[M](dao(sampleUsers))
|
||||
val config = Config(46)
|
||||
repository.delete(UserId(42))(config) shouldBe a [Left[Throwable, Unit]]
|
||||
repository.delete(UserId(42))(config) shouldBe a[Left[Throwable, Unit]]
|
||||
}
|
||||
|
||||
it should "update existing user" in {
|
||||
|
@ -145,5 +146,6 @@ class Tests extends AnyFlatSpec with Matchers:
|
|||
it should "not update nonexistent user" in {
|
||||
val repository = UserRepositoryDao[M](dao(sampleUsers))
|
||||
val config = Config(46)
|
||||
repository.update(User(UserId(22), UserName("Subject #22"), Age(42), Set.empty))(config) shouldBe a [Left[Throwable, Unit]]
|
||||
repository
|
||||
.update(User(UserId(22), UserName("Subject #22"), Age(42), Set.empty))(config) shouldBe a[Left[Throwable, Unit]]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue