0

I have two similar functions which have different return types:

override def getUsers(uri: Uri): F[Either[CodecException, List[User]]] =
    for {
      response <- retrieveDataFromClient(uri)
      result <- Sync[F].delay(response)
    } yield result

  override def getAnimals(uri: Uri): F[Either[CodecException, List[Animal]]] =
    for {
      response <- retrieveDataFromClient(uri)
      result <- Sync[F].delay(response)
    } yield result

And I have implicit circe decoders for them:

 implicit def decodeUser(response: HttpResponse): Either[CodecException, List[User]] = Decoder[List[User]].decode(response.entity)

 implicit def decodeAnimal(response: HttpResponse): Either[CodecException, List[Animal]] = Decoder[List[Animal]].decode(response.entity)

Now I have refactored first two methods into generic one:

 override def getData[A](uri: Uri): F[Either[CodecException, List[A]]] =
    for {
      response <- retrieveDataFromClient(uri)
      result <- Sync[F].delay(response)
    } yield result

And created generic circe decoder:

 implicit def decodeGeneric[A](response: HttpResponse): Either[CodecException, List[A]] = Decoder[List[A]].decode(response.entity)

But I got compilation error:

Could not find an instance of Decoder for List[A]
implicit def decodeGeneric[A](response: HttpResponse): Either[CodecException, List[A]] = Decoder[List[A]].decode(response.entity)


not enough arguments for method apply: (implicit instance: hammock.Decoder[List[A]])hammock.Decoder[List[A]] in object Decoder.
Unspecified value parameter instance.
implicit def decodeGeneric[A](response: HttpResponse): Either[CodecException, List[A]] = Decoder[List[A]].decode(response.entity)

Method retrieveDataFromClient return F[HttpResponse] from http client Hammock and response is implicit parsed into type User and Animal (via above decoders and works fine), but now I want to have it generic to remove boilerplate. Is it possible to refactor this code in this way and create generic decoder?

Developus
  • 1,400
  • 2
  • 14
  • 50
  • Is there a reason you aren't using auto derivation? – soote Nov 13 '19 at 01:26
  • No, is it possible to resolve it with derivation? – Developus Nov 13 '19 at 10:13
  • Remove all of your decoders, just import `import io.circe.generic.auto._` at the place where you need the object to be decoded. You shouldn't need to write any code for this. – soote Nov 13 '19 at 18:43
  • Ok, but it did not solve a problem. Still get: `type mismatch; found : hammock.HttpResponse required: Either[hammock.CodecException,List[A]] } yield result` – Developus Nov 13 '19 at 18:46
  • Read the docs for hammock circe: http://pepegar.com/hammock/marshalling.html – soote Nov 13 '19 at 19:30
  • I read them. I also use Hammock decoder in my code, as I showed it above. I need to create generic code instead of concrete as it is right now – Developus Nov 13 '19 at 19:33

2 Answers2

1

You can't make a generic decoder for every A, constrain your A in your decodeGeneric method to those that can be decoded. Circe auto should give you a decoder for your User and Animal classes.

import io.circe.generic.auto._

implicit def decodeGeneric[A](response: HttpResponse)(implicit decoder: Decoder[A]): Either[CodecException, List[A]] = 
    Decoder[List[A]].decode(response.entity)
soote
  • 3,240
  • 1
  • 23
  • 34
  • Ok, understand. So how I shuold refactor this two similar methids `getUsers` and `getAnimals` to remove boilderplate? – Developus Nov 13 '19 at 19:52
  • Your getData method looks ok, does it not work if you place this `decodeGeneric` and circe auto in scope? – soote Nov 13 '19 at 20:02
  • No, it doesnt. I go another error: `ambiguous implicit values: both value decodeUser in object parsers of type => io.circe.Decoder[domain.User] and value decodeAnimal in object parsers of type => io.circe.Decoder[domain.Animal] match expected type io.circe.Decoder[A] result <- Sync[F].delay(response)` – Developus Nov 13 '19 at 20:07
  • Yes, I removed them now and back to old error from above post: `type mismatch; found : hammock.HttpResponse required: Either[hammock.CodecException,List[A]] } yield result` – Developus Nov 13 '19 at 20:10
  • Do you have the hammock implicit in scope? `import hammock.circe.implicits._` – soote Nov 13 '19 at 20:13
  • Oh, also you need to pass the type to your getData method, like so: `getData[Animal]`. This will remove the ambiguous reference – soote Nov 13 '19 at 20:17
  • yes, I call this by `getData[User]` or `getData[Animal]` and same error – Developus Nov 13 '19 at 20:20
  • Replacing the hammock `HttpResponse` type with `String`, I get: `decodeGeneric[Animal]("""[{"kind": "Tiger"}]""")` returns `Right(List(Animal(Tiger)))`. I'm not sure how hammock marshaling works, but normal circe works here. – soote Nov 13 '19 at 20:33
0

Change the method signature by introducing a context bound

 implicit def decodeGeneric[A: Decoder](response: HttpResponse): Either[CodecException, List[A]] = Decoder[List[A]].decode(response.entity)
Ivan Stanislavciuc
  • 7,140
  • 15
  • 18