Я создал простой trait
и его реализацию:
trait UserRepositoryAlg[F[_]] {
def find(nick: String): F[User]
def update(user: User): F[User]
}
class UserRepositoryInterpreter extends UserRepositoryAlg[Either[Error, *]] {
override def find(nick: String): Either[Error, User] = for {
res <- users.find(user => user.nick == nick).toRight(UserError)
} yield res
override def update(user: User): Either[Error, User] = for {
found <- users.find(u => u.nick == user.nick).toRight(UserError)
updated = found.copy(points = found.points + user.points)
} yield updated
}
Здесь я хотел бы использовать Either
или EitherT
, чтобы "ловить" ошибки, но я также хотел бы использоватьIO
или Future
в качестве главной монады. В моем основном классе я создал вызов этой реализации:
object Main extends App {
class Pointer[F[_] : Monad](repo: UserRepositoryAlg[F]) {
def addPoints(nick: String): EitherT[F, Error, User] = {
for {
user <- EitherT.right(repo.find(nick))
updated <- EitherT.right(repo.update(user))
} yield Right(updated)
}
}
val pointer = new Pointer[IO](new UserRepositoryInterpreter{}).addPoints("nick")
}
Но в строке, где создается pointer
, IntelliJ показывает мне ошибку: Type mismatch - required: UserRepositoryAlg[F], found: UserRepositoryInterpreter
, и я не понимаю, почему. Я создал Pointer
класс с F[_]
как IO
и хочу использовать реализацию UserRepositoryAlg[F]
. Как я мог решить эту проблему или что является хорошей практикой в этом случае? Если я хочу добиться чего-то вроде этого: IO[Either[Error, User]]
или EitherT[IO, Error, User]
.
Я пытался изменить class UserRepositoryInterpreter extends UserRepositoryAlg[Either[Error, *]]
на что-то вроде class UserRepositoryInterpreter[F[_]] extends UserRepositoryAlg[F[Either[Error, *]]]
, но это мне не помогло.
РЕДАКТИРОВАТЬ: Я узнал, как вернуть F[Either[Error,User]]
с помощью Applicative[F]
, которые преобразуют A => F[A]
:
class UserRepositoryInterpreter[F[_] : Applicative] extends UserRepositoryAlg[F[Either[Error, *]]] {
override def find(nick: String): F[Either[Error, User]] = for {
res <- Applicative[F].pure(users.find(user => user.nick == nick).toRight(UserError))
} yield res
override def update(user: User): F[Either[Error, User]] = for {
found <- Applicative[F].pure(users.find(u => u.nick == user.nick).toRight(UserError))
updated = Applicative[F].pure(found.map(u => u.copy(points = u.points + user.points)))
} yield updated
}
Но у меня все еще есть проблема в основной функции, потому что яне может получить Right
значение Either
:
def addPoints(nick: String): EitherT[F, Error, User] = {
for {
user <- EitherT.liftF(repo.find(nick))
updated <- EitherT.rightT(repo.update(user))
} yield Right(updated)
}
Здесь updated <- EitherT.rightT(repo.update(user))
user
равно Either[Error, User]
, но мне нужно передать только User
. Поэтому я попытался сделать что-то вроде: Right(user).map(u=>u)
и передать его, но это также не помогает. Как мне принять это значение?