I love Scala's for comprehensions! I would love them even more if I could find a way to map over the right-hand side result before assigning it to a value. Say I have the following code:
case class Person(name: String)
def getPersonById(id: Long): Future[Option[Person]] = ???
case class Dog(color: String)
def getDogById(id: Long): Future[Dog] = ???
def addStringToFile(name: Seq[String]): Future[Unit] = ???
val id: Long = ???
for {
person <- getPersonById(id)
name = person.map(_.name)
dog <- getDogById(id)
color = dog.color
_ <- addNamesToFile(Seq(name, color).flatten)
} yield name
I don't actually need person
or dog
as an intermediate value; the are only valuable to the extent that I can extract a property from them on the very next line. Ideally what I'd like to do would look something vaguely like this:
for {
name <- getPersonById(id))>>.map(_.name)
color <- getDogById(id))>>.color
_ <- addNamesToFile(Seq(name, color).flatten)
} yield name
But obviously this isn't valid Scala. Outside of a for-comprenshion I would do:
val name = getPersonById(id).map(_.map(_.name))
val color = getDogById(id).map(_.color)
But the neat thing about the for-comprehension is that I don't need to explicitly perform the outer .map
, and the "Future" part is hidden away.
Is it possible to map over the result of a right-hand side expression in a for-comprehension before assigning it to a value?
Ideally what I'd like to do would look something vaguely like this:
for { name <- getPersonById(id))>>.map(_.name) color <- getDogById(id))>>.color _ <- addNamesToFile(Seq(name, color).flatten) } yield
Note that you can actually write exactly that by simply replacing your cryptic )>>.
with map
:)
for {
name <- getPersonBy(id).map(_.map(_.name))
color <- getDogById(id).map(_.color)
_ <- addNamesToFile(name.toSeq :+ color)
} yield name
The only difference between this and what you wrote is .map
vs. >>
If you like your syntax more for some reason, make an implicit:
object FunnySyntax {
implicit class FS[T](val f: Future[T]) extends AnyVal {
def >>[P] (m: T => P): Future[P] = f.map(m)
}
}
Now, you can do
name <- getPersonById(id) >> { _.map(_.name) }
color <- getDogById(id) >> { _.color }
This is almost exactly what you had - just a couple of braces and an underscore added ... 🤷