1

I have this trait with a single method of type WSResponse => HTTPCallResult[A]

trait Parsable[A] {
  def parse(response: WSResponse): HTTPCallResult[A]
}

I want to define an implicit conversion from a function WSResponse => A

object Parsable {
  implicit class DefaultResponseParser[A](parser: WSResponse => A) extends Parsable[A] {
    override def parse(r: WSResponse): HTTPCallResult[A] = {
     //implementation
    }
  }
}

The desired behaviour is such, that if a function WSResponse => HTTPCallResult[A] is passed the anonymous class of Parsable[A] is automatically created. If a function of any other return type is passed (WSResponse => A) the implicit conversion is triggered.

However, an error occurs when compiling with SBT

[error] /Auth.scala:24:60: type mismatch;
[error]  found   : String
[error]  required: HTTPCall.HTTPCallResult[?]
[error]     (which expands to)  scala.util.Either[String,?]
[error]       .post((r: WSResponse) => (r.json \ "key").as[String])

This function was used in order to test it.

def post[A](parser: Parsable[A])
.post((r: WSResponse) => (r.json \ "key").as[String])

EDIT 1: Even making the implicit class an implicit method and moving it to the very same scope as post didn't help.

EDIT 2: It seems that the problem stems from the use of trait with single undefined method. Therefore when passed a function to the post method, the compiler tries to create an anonymous class of type Parser[A] from that. Due to a type missmatch it throws, without even trying to be looking for any implicits.

Levi Ramsey
  • 18,884
  • 1
  • 16
  • 30
  • You have an implicit function that says, given a function `WSResponse => A` you can call `.parser(WSResponse)`. But, I guess, what you are trying to do is pass `WSResponse => A` instead of `Parsable[A]`. – Johny T Koshy Sep 22 '22 at 10:49
  • @JohnyTKoshy yes, that is exactly what I am trying to achieve (instead of `Parser[A]` be able to pass just the function). If I can't achieve this by employing implicit method or class, how can I then? – David Tomecek Sep 22 '22 at 11:03
  • I am not sure about the types here. If my assumptions are correct, following signature could work: `def post[A](p: WSResponse => A): WSResponse => HTTPCallResult[A] = p.parse(_)` – Johny T Koshy Sep 22 '22 at 11:07
  • 2
    It seems that the problem stems from the single-method nature of the trait. Because given a function of signature `WSResponse => HTTPCallResult[A]` the compiler tries to make an anonymous class from it, and fails when the types don't match. When I added a one more method to the trait, and thus made it unparsable, the implicit conversion worked. Somewhat the anonymous class parsing operation takes a precedence, and when it fails, the responsibility is not passed down to the implicits... – David Tomecek Sep 22 '22 at 11:26
  • 1
    @DavidTomecek You're right, [SAM conversion precedes implicits](https://www.scala-lang.org/news/2.12.0/#sam-conversion-precedes-implicits). See also [Prevent a Scala trait from qualifying as a SAM type](https://stackoverflow.com/questions/48971622). – Dmytro Mitin Sep 22 '22 at 11:55
  • After going through the links @DmytroMitin provided if the parser function is referentially transparent, using `val parser: WSResponse => HTTPCallResult[A]` instead of using function definition would work in this case. – Johny T Koshy Sep 22 '22 at 14:22

0 Answers0