【问题标题】:Using multiple Scala TypeClass instances in function parameters在函数参数中使用多个 Scala TypeClass 实例
【发布时间】:2015-07-24 02:14:44
【问题描述】:

我创建了一组案例类,我想使用 Scala Typeclass 实现不同的行为。下面的代码示例按预期工作。

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

到目前为止一切顺利。我可以将toQuery 方法与任何定义的类型一起使用。

我的问题:我想通过组合抽象类 AS 创建一个返回 BuildableQuery 的函数。

我尝试创建一个新的隐式对象,该对象定义了由AS 组合组成的新案例类的行为。在函数中使用它时,它不会编译。

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))

这个问题有解决方案吗?

【问题讨论】:

    标签: scala typeclass


    【解决方案1】:

    更新

    如果您只想为 IP 的特定值定义 AS[I, P] 实例,这也是可能的 - 您可以在问题中定义 ASQueryBuilder,然后在您的方法中使用类型类:

    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))
    

    现在这些只有在适当的实例可用时才会编译。


    原答案

    听起来您想通用地创建实例,而不仅仅是为AS[B, X]。您可以使用通用方法来做到这一点:

    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") }
    }
    

    现在您的示例将编译。

    附带说明,使用相同的名称 (toQuery) 作为类型类方法和类型类语法方法会使事情变得有些困难。您可能想要重命名一个,例如语法方法在类型类实例的toQuery的定义中可用。

    【讨论】:

    • 感谢您的回复!我是我的情况,我想为 AS[B,X] 和 AS[C,Y] 定义不同的行为,而不是 AS[A,S] 的一般行为。例如,对func(B(), X()).s 的调用应返回“BX 查询”,而不是“AS 查询”。
    • @Konstantinos 哦,我误会了。已更新。
    • func2(B(),X()) 似乎有效。但是,func(B(),X()) 存在编译错误。
    • 对——类型类解析是静态的,您已经说过参数静态只是AS。如果您没有 BuildableQuery[AS[A, S]] 的类型类实例,它将无法编译。
    猜你喜欢
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-29
    • 1970-01-01
    • 2019-01-14
    相关资源
    最近更新 更多