Reputation: 2414
I want to call a generic function f[X](...)
, and in my case X
happens to be Option[Y]
. I try to pass both Some[...]
and None
where the function expects X
, but Scala insists that X
be of type Some[Y]
.
def flattenOptionMap[A, B](input : Map[A, Option[B]]) : Option[Map[A, B]] = {
input.foldLeft[Option[Map[A,B]]] (Some(Map.empty)) {
case (_, (_, None)) => None
case (None, (_, _)) => None
case (Some(acc), (key, Some(value))) => Some(acc + (key -> value))
}
}
In this example, I had to explicitly specify that Option[Map[A,B]]
should be used as generic type for foldLeft
. All the necessary type information is already contained in the context, and typing cumbersome types like Option[Map[A,B]]
more often than necessary in my opinion drastically reduces readability of my code.
Is there some way to get Scala to infer the type after all, or otherwise avoid copy-pasting the whole type?
Upvotes: 2
Views: 291
Reputation: 1821
If your goal is to improve readability, and to get help from the compiler to infer types, try:
def flattenOptionMap[A, B](input: Map[A, Option[B]]) =
input.foldLeft(Option(Map.empty[A, B])) {
case (_, (_, None)) => None
case (None, (_, _)) => None
case (Some(acc), (key, Some(value))) => Some(acc + (key -> value))
}
or even better:
def flattenOptionMap[A, B](input: Map[A, Option[B]]) =
input.foldLeft(Option(Map.empty[A, B])) {
case (Some(acc), (key, Some(value))) => Some(acc + (key -> value))
case _ => None
}
Or even better (in my opinion): Peter Neyens's answer. Use sequence
from your Traverse
instance.
Upvotes: 4
Reputation: 9820
When you use Option(Map.empty[A, B])
as the start value of your foldLeft
, Scala will infer the correct type as I wrote in the comments (and beefyhalo in his answer).
I would like to add, that if you are open to using Scalaz, you can just use the sequence
function.
import scalaz._
import Scalaz._
val mapSome = Map(1 -> Some("a"), 2 -> Some("b"))
val mapNone = Map(1 -> Some("a"), 2 -> Some("b"), 3 -> None)
mapSome.sequence
flattenOptionMap(mapSome)
// Option[Map[Int,String]] = Some(Map(1 -> a, 2 -> b))
mapNone.sequence
flattenOptionMap(mapNone)
// Option[Map[Int,String]] = None
Upvotes: 5