Search code examples
scalascala-cats

Cats leftMap seems to be unable to correctly infer type


I have a function that makes an HTTP request using the Softwaremill sttp client. I am also using the Cats library for the leftMap functionality. Here is the function:

def tags(repositoryName: String): Either[String, List[Tag]] = {
  val uri = uri"$gitLabBaseUrl/api/v4/projects/$repositoryName/repository/tags"

  val request = sttp.get(uri).header("PRIVATE-TOKEN", privateToken).response(asJson[List[Tag]])

  request
    .send()
    .body
    .flatMap(
      _.leftMap(
        e => Left(e.message)
      )
    )
    .leftMap(_.toString)
}

So as you can see I want the signature of the function to be Either[String, List[Tag]]. However, to achieve this I need to do two leftMap statements, where in my reasoning just the first leftMap should be sufficient. I broke the statement down to get the type signatures:

val foo: Id[Response[Either[DeserializationError[circe.Error], List[Tag]]]] = request.send()
val foo2: Either[String, Either[DeserializationError[circe.Error], List[Tag]]] = request.send().body
val foo3: Either[io.Serializable, List[Tag]] = request.send().body.flatMap(_.leftMap(e => Left(e.message)))

And as you can see the Either that is returned by the flatMap is of type [io.Serializable, List[Tag]]. However, the signature from request.send().body is:

Either[String, Either[DeserializationError[circe.Error], List[Tag]]]

so therefore e.message results in a String and as such I was expecting the

_.leftMap(e => Left(e.message))

statement to already result in a

Either[String, List[Tag]]

But instead it has as signature

Either[io.Serializable, List[Tag]]

So I need to do the second leftMap to get to the correct signature of

Either[String, List[Tag]]

Could someone please help me out how I can avoid having to do the second leftMap? It seems like it shouldn't be necessary but I have not been able to figure out how to solve it.

Thanks!


Solution

  • Try

    request
      .send()
      .body
      .flatMap { _.left.map(_.message) }