13

I am trying to abstract inserting objects of different types into sql tables of similar structure. Here's what I'm trying to do:

class TableAccess[A : Meta](table: String) {
  def insert(key: String, a: A): ConnectionIO[Unit] = {
    (fr"insert into " ++ Fragment.const(table) ++ fr" values ($key, $a);").update.run.map(_ => ())
  }
}

But I get this compile error:

[error] diverging implicit expansion for type doobie.util.param.Param[A]
[error] starting with method fromMeta in object Param
[error]     (fr"insert into " ++ Fragment.const(table) ++ fr" values ($key, $a);").update.run.map(_ => ())

All I can find in the documentation is:

doobie allows you to interpolate values of any type (and options thereof) with an Meta instance, which includes...

But it seems that is not enough in this case; what's the right typeclass/imports/conversions I need?

Daenyth
  • 35,856
  • 13
  • 85
  • 124
Joe K
  • 18,204
  • 2
  • 36
  • 58
  • There might be something wrong with how Doobie derives Param from Meta. You can see more at https://github.com/tpolecat/doobie/issues/780 – Mark Canlas Oct 09 '18 at 21:03

3 Answers3

3

I'll go ahead an answer my own question, almost a year later. I never fully understood what was happening, and I have since updated to a newer version of doobie, so I am not sure how relevant this is. But now the documentation contains this clue:

Note: it is important to understand that Meta exists only to introduce Get/Put pairs into implicit scope. You should never demand Meta as evidence in user code: instead demand Get, Put, or both.

def foo[A: Meta](...)     // don't do this
def foo[A: Get: Put](...) // ok

And indeed, between that change and the new version, this now compiles just fine for me:

class TableAccess[A: Get: Put](table: String) {
Joe K
  • 18,204
  • 2
  • 36
  • 58
0

When the compiler is resolving implicit its searches for one of a specific type in the current scope. Here it seems like his finding more than one in his tree search.

It's not a matter of a missing typeclass or imports, it's more like you have too many of them and the compiler cant figure the right one. Try removing some implicit and see how that works or pass them explicitly.

Merlin
  • 224
  • 1
  • 11
0

One way I resolved this was to localize the type parameters (and their evidence) onto the method (on a static/companion object), and then it compiled.

Something like

object MinimalGood {
  def good[A: Meta, B: Meta](a: A, b: B): Update0 =
  sql"""$a $b""".update
}
Mark Canlas
  • 9,385
  • 5
  • 41
  • 63