I was coding a for comprehension, and wondered something:
def updateUserStats(user: User): Either[Error,User] = for {
stampleCount <- stampleRepository.getStampleCount(user).right
userUpdated <- Right(copyUserWithStats(user,stampleCount)).right // ?????????
userSaved <- userService.update(userUpdated).right
} yield userSaved
def copyUserWithStats(user: User, stamples: Long): User = {
val newStats = user.userStats.copy(stamples = stamples)
user.copy(userStats = newStats)
}
It seems using copyUserWithStats which does not return an Either can't be used directly in the for comprehension, because it doesn't have the map/flatMap methods.
So I wonder, in this case, it is the appropriate solution to use Right(copyUserWithStats(user,stampleCount)).right
It seems to work at least...
By the way, I also tried with Option but it didn't work, can someone explain why?
def updateUserStats(user: User): Either[Error,User] = for {
stampleCount <- stampleRepository.getStampleCount(user).right
userUpdated <- Some(copyUserWithStats(user,stampleCount))
userSaved <- userService.update(userUpdated).right
} yield userSaved
Thanks
In a for-comprehension all monads have to be of the same kind. That means you can not mix RightProjection
and Option
, because the output has to be an Either
. This is because the for-comprehension gets translated to a nested flatMap/map construct. Your example would look like this:
def updateUserStats(user: User): Either[Error,User] =
stampleRepository.getStampleCount(user).right.flatMap { stampleCount =>
Some(copyUserWithStats(user,stampleCount)).flatMap { userUpdated =>
userService.update(userUpdated).right.map { userSaved =>
userSaved
}
}
}
If we now look at the signature of RightProjection.flatMap
, which is def
flatMap[AA >: A, Y](f: (B) ⇒ Either[AA, Y]): Either[AA, Y]
, we see, that the result has to be an Either
, but flatMap
of Option
has the signature flatMap[B](f: (A) ⇒ Option[B]): Option[B]
. It returns an Option
and there is no sane way to translate an Option
to an Either
.
edit: Following example does not quiet work for Either
, see link by huynhjl for more information
However you can besides extracting values from monads in a for-comprehension also create variables, so your example could be rewritten as:
def updateUserStats(user: User): Either[Error,User] = for {
stampleCount <- stampleRepository.getStampleCount(user).right
userUpdated = copyUserWithStats(user,stampleCount)
userSaved <- userService.update(userUpdated).right
} yield userSaved
which saves us an unnecessary allocation and also makes the code more readable.