Reputation: 13
I have the following code,
def obsKPI[T](kpi: Option[T], f: T => Unit) = {
kpi match {
case Some(obsValue) => f(obsValue)
case _ => Unit
}
}
def func1(str:String):Unit = println(str)
def func2(num: Int):Unit = println(num)
//option1: val inputArgs = List((Some("first"),(func1 _)),(Some("third"), (func1 _)))
//option2: val inputArgs = List((Some(456), (func2 _)),(None,(func2 _)))
// option3:
val inputArgs = List((Some("first"),(func1 _)),(Some(456), (func2 _)),(Some("third"), (func1 _)),(None,(func2 _)))
inputArgs.map(x => obsKPI(x._1, x._2))
when running either option 1 or 2 (the inputArgs list contains function of only String=>Unit or Int=>Unit), the code works, but when running option 3, I get an error:
:68: error: type mismatch;
found : Int with String => Unit
required: Any => Unit
inputArgs.map(x => obsKPI(x._1, x._2))
^
Thanks for letting me know what went wrong in here.
Upvotes: 1
Views: 69
Reputation: 40500
Functions aren't covariant in their parameter types (they are in fact contravariant).
This means, that if Foo
is a subclass of Bar
, Foo => Unit
is not a subclass of Bar => Unit
(the opposite is true).
In your case, you are trying to coerce func1
and func2
to Any => Unit
, but that cannot work, because their types are incompatible - one is String => Unit
, and the other one is Int => Unit
.
One way to get around this problem, is to use a case class rather than a tuple:
case class KPI[T](t: Option[T], f: T => Unit)
def obsKPI(kpi: KPI[_]) = kpi match {
case KPI(Some(k), f) => f(k)
case _ => () // `()` is the value of type Unit. Unit, as you had it is the value of type Unit.type - not what you want at all
}
// You can also write the above more concise like this: def obsKPI[T](kpi: KPI[T]) = kpi.t.foreach(kpi.f)
def func1(str:String) = println(str)
def func2(num: Int) = println(num)
val inputArgs = List(KPI(Some("first"),func1 _), KPI(Some(456), func2 _), KPI(Some("third"), func1 _), KPI[Int](None,func2 _))
inputArgs.foreach(obsKPI) // Could do .map here too, but ending up with a list of ()s is unlikely what you you want.
You can make it look a bit more elegant, if you make your obsKPI
into a member of the case class:
case class KPI[T](t: Option[T], f: T => Unit) {
def obs = t.foreach(f)
}
val inputArgs = List(KPI(Some("first"),func1 _), KPI(Some(456), func2 _), KPI(Some("third"), func1 _), KPI[Int](None,func2 _))
inputArgs.foreach(_.obs)
Upvotes: 2