1

doobie、http4s、および cat を使用して、データベースから User レコードを取得しようとしています。以下のコードに基づいて次のエラーを提供している型システムに悩まされています。

ルーター:

val httpRoutes = HttpRoutes.of[IO] {
    case GET -> Root / "second" / id =>
      val intId : Integer = Integer.parseInt(id)
      //if i make thie ConnectionIO[Option[Unit]] it compiles, but returns a cats Free object
      val userOption: ConnectionIO[Option[User]] = UserModel.findById(intId, transactor.transactor)
      Ok(s"userOption is instance of: ${userOption.getClass} object: ${userOption.toString}")
  }.orNotFound

モデル:

case class User(
                 id: Read[Integer],
                 username: Read[String],
                 email: Read[String],
                 passwordHash: Read[String], //PasswordHash[SCrypt],
                 isActive: Read[Boolean],
                 dob: Read[Date]
               ) {
//  def verifyPassword(password: String) : VerificationStatus = SCrypt.checkpw[cats.Id](password, passwordHash)
}

object UserModel {
  def findById[User: Read](id: Integer, transactor: Transactor[ConnectionIO]): ConnectionIO[Option[User]] = findBy(fr"id = ${id.toString}", transactor)

  private def findBy[User: Read](by: Fragment, transactor: Transactor[ConnectionIO]): ConnectionIO[Option[User]] = {
    (sql"SELECT id, username, email, password_hash, is_active, dob FROM public.user WHERE " ++ by)
      .query[User]
      .option
      .transact(transactor)
  }
}

エラー:

Error:(35, 70) Cannot find or construct a Read instance for type:
  core.model.User
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.
      val userOption: ConnectionIO[Option[User]] = UserModel.findById(intId, transactor.transactor)

行を ConnectionIO[Option[User] から ConnectionIO[Option[Unit]] に変更すると、コンパイルして実行されますが、cats ライブラリから Free(...) オブジェクトが返されます。ケースクラスを返すことができない理由がわかりません!

findBy および findById メソッドの型宣言も参照してください。それらを追加する前に、ユーザーが見つかったというコンパイル エラーがありましたが、Read[User] が必要でした。同じ型宣言をルーターの findById の呼び出しに適用しようとしましたが、上記と同じエラーが発生しました。

事前に助けてくれてありがとう、そして私の無知を我慢してください。私よりもはるかに賢い型システムに出会ったことはありません!

4

1 に答える 1