I'd like to implement something like a functor for Option
which accumulates successful results.
It should behave like this (scala-like code below):
Some(1).map(_ + 1) # => Some[(Int, Int)] = Some((1,2))
Some(1).map(_ + 1).map(_ * 3) # => Some[(Int, Int, Int)] = Some((1,2,6))
Some(1).map(_ => None) # Option[None.type] = Some(None)
I do understand this is not a proper functor because it does not preserve identity:
Some(42).map(identity) # => Some[(Int, Int]) = Some((42, 42))
To add some context, I thought it would be useful when there is a chain of maps:
val result = Some(42).map(compute_second_value).map(compute_third_value)
and a pattern matching on result which depends not only on result
final value:
result match {
case Some(value) => do_something_with_all_results(result._1, result._2, result._3)
case None => ...
}
So I'm looking for a well-known solution / approach / theoretical term that is related to desired behaviour
You could implement it in a "general" way with a foldLeft
in Scala and working on a sequence rather than a tuple:
val ops: Seq[Int => Int] = Seq(computeSecondValue, computeThirdValue)
ops.foldLeft(Seq(1)) { case (acc, op) =>
acc :+ op(acc.last)
}
// Seq[Int]
// Seq(1, computeSecondValue(1), computeThirdValue(computeSecondValue(1)))
Or in a less general approach and keeping tuples:
for {
first <- Some(1)
second = computeSecondValue(first)
third = computeThirdValue(second)
} yield (first, second, third)
// Option[Tuple3[Int, Int, Int]]
In both cases, I didn't manage the flatMap
case if operations return an Option
but this can be implemented relatively easily by the reader.