1

I created an group of case classes which I want to implement different behaviors using Scala Typeclass. The code sample below works as expected.

case class QueryBuilder(s: String)

abstract class A()
case class B() extends A
case class C() extends A

abstract class S()
case class X() extends S
case class Y() extends S

trait BuildableQuery[T] {
  def toQuery(p: T): QueryBuilder
}
implicit object BQueryBuilder extends BuildableQuery[B] {
  def toQuery(p: B): QueryBuilder = { QueryBuilder("Query of B") }
}
implicit object CQueryBuilder extends BuildableQuery[C] {
  def toQuery(p: C): QueryBuilder = { QueryBuilder("Query of C") }
}
implicit object XQueryBuilder extends BuildableQuery[X] {
  def toQuery(p: X): QueryBuilder = { QueryBuilder("Query of X") }
}
implicit object YQueryBuilder extends BuildableQuery[Y] {
  def toQuery(p: Y): QueryBuilder = { QueryBuilder("Query of Y") }
}

def toQuery[A: BuildableQuery](value: A): QueryBuilder =
  (implicitly[BuildableQuery[A]]).toQuery(value)

println(toQuery(B()).s) // Query of B
println(toQuery(C()).s) // Query of C
println(toQuery(X()).s) // Query of X
println(toQuery(Y()).s) // Query of Y

So far so good. I can use the method toQuery with any of the defined types.

My problem: I want to create a function that returns a BuildableQuery by combining abstract classes A and S.

I tried creating a new implicit object which defines a behavior for a new case class consisting of a combination of A and S. When using it in a function, it does not compile.

case class AS[I <: A, P <: S](a: I, s: P)
implicit object ASQueryBuilder extends BuildableQuery[AS[B, X]] {
  def toQuery(p: AS[B, X]): QueryBuilder = { QueryBuilder("Query of BX") }
}

// Does not compile...
// Error: could not find implicit value for evidence parameter of type BuildableQuery[AS[A,S]]
def func(a: A, s: S): QueryBuilder = toQuery(AS(a,s))

// Does not compile...
// Error: could not find implicit value for evidence parameter of type BuildableQuery[AS[I,P]]
def func2[I <: A: BuildableQuery, P <: S: BuildableQuery](a: I, s: P): QueryBuilder = 
  toQuery(AS(a,s))

Does a solution exist for this problem?

1 Answer 1

1

Update

If you only want to define AS[I, P] instances for particular values of I and P, that's also possible—you can define ASQueryBuilder as in your question, and then require an instance in your methods that use the type class:

def func(a: A, s: S)(implicit bq: BuildableQuery[AS[A, S]]): QueryBuilder = toQuery(AS(a,s))

def func2[I <: A: BuildableQuery, P <: S: BuildableQuery](a: I, s: P)(implicit
  bq: BuildableQuery[AS[I, P]]
): QueryBuilder = toQuery(AS(a, s))

Now these will only compile if the appropriate instances are available.


Original answer

It sounds like you want to create instances generically, not just for AS[B, X]. You can do this with a generic method:

implicit def ASQueryBuilder[I <: A, P <: S]: BuildableQuery[AS[I, P]] =
  new BuildableQuery[AS[I, P]] { 
    def toQuery(p: AS[I, P]): QueryBuilder = { QueryBuilder("Query of AS") }
}

Now your examples will compile.

As a side note, using the same name (toQuery) as both a type class method and type class syntax method can make things a little difficult. You may want to rename one so that e.g. the syntax method is available in the definition of a type class instance's toQuery.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the reply! I'm my case, I would like to define different behavior for AS[B,X] and AS[C,Y], not a general behavior for AS[A, S]. E.g., a call to func(B(), X()).s should return "Query of BX", not "Query of AS".
@Konstantinos Oh, I misunderstood. Updated.
func2(B(),X()) seems to work. However, there's a compilation error for func(B(),X()).
Right—type class resolution is static, and you've said that the arguments are statically just an A and an S. If you don't have a type class instance for BuildableQuery[AS[A, S]] it won't compile.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.