Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doobie cannot find or construct a Read instance for type T

I'm using doobie to query some data and everything works fine, like this:

case class Usuario(var documento: String, var nombre: String, var contrasena: String)

  def getUsuario(doc: String) =
     sql"""SELECT documento, nombre, contrasena FROM "Usuario" WHERE "documento" = $doc"""
      .query[Usuario]
      .option
      .transact(xa)
      .unsafeRunSync()

But if I declare a function with type restriction like this:

 def getOption[T](f: Fragment): Option[T] = {
    f.query[T]
     .option
     .transact(xa)
     .unsafeRunSync()

}

I got these errors:

Error:(42, 12) Cannot find or construct a Read instance for type:
  T
This can happen for a few reasons, but the most common case is that a data
member somewhere within this type doesn't have a Get instance in scope. Here are
some debugging hints:
- For Option types, ensure that a Read instance is in scope for the non-Option
  version.
- For types you expect to map to a single column ensure that a Get instance is
  in scope.
- For case classes, HLists, and shapeless records ensure that each element
  has a Read instance in scope.
- Lather, rinse, repeat, recursively until you find the problematic bit.
You can check that an instance exists for Read in the REPL or in your code:
  scala> Read[Foo]
and similarly with Get:
  scala> Get[Foo]
And find the missing instance and construct it as needed. Refer to Chapter 12
of the book of doobie for more information.
    f.query[T].option.transact(xa).unsafeRunSync()

Error:(42, 12) not enough arguments for method query: (implicit evidence$1: doobie.util.Read[T], implicit h: doobie.LogHandler)doobie.Query0[T].
Unspecified value parameter evidence$1.
    f.query[T].option.transact(xa).unsafeRunSync()

Does anyone know how to make what I want? I think it's something with implicits but I don't know how to fix it.

like image 828
santiromf Avatar asked Oct 23 '19 21:10

santiromf


1 Answers

In order for doobie to be able to transform the result of SQL query to your case class, it needs an instance of Read typeclass in scope.

For example for Usuario it needs instance of Read[Usuario]. Fortunately, doobie is able to derive typeclasses for types from typeclasses it already knows, like String, so in most cases, we don't need to create these explicitly.

In your case, you want to create method getOption which has type parameter T, which means, that compiler doesn't know for which typeclass of which type to look for.

You can fix it very easily, by just adding context-bound for Read to your type (like T: Read or by adding implicit parameter). It means that your method will pass "request" to resolve typeclass later in compile-time when the concrete type of T would be already known.

So your fixed method will be:

def getOption[T: Read](f: Fragment): Option[T] = {
    f.query[T]
     .option
     .transact(xa)
     .unsafeRunSync()

or with implicit parameter:

def getOption[T](f: Fragment)(implicit read: Read[T]): Option[T] = {
    f.query[T]
     .option
     .transact(xa)
     .unsafeRunSync()
like image 108
Krzysztof Atłasik Avatar answered Nov 11 '22 23:11

Krzysztof Atłasik