Reputation: 4794
I'm using the magnet pattern to call the right overloaded method (flatMap
) depending on whether some implicit parameters exist.
https://scastie.scala-lang.org/pYQG0Q6TTWuvbjatD7SIxw
import scala.language.implicitConversions
object Tester {
final case class Foo[A](value: A) {
def flatMap[B](cat: Cat[A, B]): Foo[B] = {
println("cat")
cat.f(value)
}
def flatMap[B](monkey: Monkey[A, B]): Foo[B] = {
println("monkey")
monkey.f(value)
}
def map[B](f: A => B): Foo[B] = {
Foo(f(value))
}
}
case class Banana()
class Monkey[A, B](val f: A => Foo[B])
object Monkey {
implicit def funcToMonkey[A, B](f: A => Foo[B])(implicit i: Banana): Monkey[A, B] = new Monkey(f)
}
case class Catnip()
class Cat[A, B](val f: A => Foo[B])
object Cat {
implicit def funcToCat[A, B](f: A => Foo[B])(implicit i: Catnip): Cat[A, B] = new Cat(f)
}
}
object Main {
import Tester._
def main(args: Array[String]) = {
for {
_ <- Foo(1)
_ <- Foo("asdf")
} yield ()
}
}
However I'm getting the error missing parameter type for expanded function
.
If I desugar it to direct flatMap calls I get a similar error:
Foo(1).flatMap(_ => Foo("asdf").map(_ => ()))
missing parameter type
I can solve the problem by explicitly specifying the type of the flatMap parameter (i.e. .flatMap((_: Int) => ...)
) but I want to avoid that. Even if I do use the parameter, Scala still requires me to specify the type
Is there a way to make the for comprehension code compile?
Upvotes: 0
Views: 106
Reputation: 170735
I don't think this can work. To avoid having to provide parameter type for a lambda, the expected type (so flatMap
parameter's type here) has to be a function type or a SAM type, not just some type which has an implicit conversion from a function type like Cat
and Monkey
are.
Before 2.13, you'd also run into a separate problem because the method wasn't allowed to be overloaded, but the fix doesn't cover your situation either:
To avoid breaking existing code, we only provide an expected type (for each argument position) when:
there is at least one FunctionN type expected by one of the overloads: in this case, the expected type is a FunctionN[Ti, ?], where Ti are the argument types (they must all be =:=), and the expected result type is elided using a wildcard. This does not exclude any overloads that expect a SAM, because they conform to a function type through SAM conversion
OR: all overloads expect a SAM type of the same class, but with potentially varying result types (argument types must be =:=)
Neither of these are true for you (Cat
and Monkey
are not SAM types and wouldn't work even if they did). I've tried to make flatMap
take function with implicits directly
import scala.language.implicitConversions
object Tester {
final case class Foo[A](value: A) {
def flatMap[B](f: A => Foo[B])(implicit i: Catnip): Foo[B] = {
val cat: Cat[A, B] = f
println("cat")
cat.f(value)
}
def flatMap[B](f: A => Foo[B])(implicit i: Banana): Foo[B] = {
val monkey: Monkey[A, B] = f
println("monkey")
monkey.f(value)
}
def map[B](f: A => B): Foo[B] = {
Foo(f(value))
}
}
case class Banana()
class Monkey[A, B](val f: A => Foo[B])
object Monkey {
implicit def funcToMonkey[A, B](f: A => Foo[B])(implicit i: Banana): Monkey[A, B] = new Monkey(f)
}
case class Catnip()
class Cat[A, B](val f: A => Foo[B])
object Cat {
implicit def funcToCat[A, B](f: A => Foo[B])(implicit i: Catnip): Cat[A, B] = new Cat(f)
}
}
object Main {
import Tester._
implicit val cn = Catnip()
def main(args: Array[String]) = {
for {
_ <- Foo(1)
_ <- Foo("asdf")
} yield ()
}
}
but that just gives a different error:
ambiguous reference to overloaded definition,
both method flatMap in class Foo of type [B](f: Int => Tester.Foo[B])(implicit i: Tester.Banana)Tester.Foo[B]
and method flatMap in class Foo of type [B](f: Int => Tester.Foo[B])(implicit i: Tester.Catnip)Tester.Foo[B]
match argument types (Int => Tester.Foo[Unit])
Upvotes: 1