If-Then-Else DSL - 定义implicits以区分两种返回类型

时间:2016-08-19 10:08:13

标签: scala typeclass implicit

我正在尝试构建一个小型DSL,允许一些if-then-else分支具有两种类型的组合,一种是泛型(If),另一种是具有附加功能(IfGE)。我的印象是,混合使用低优先级的隐式特征,我可以让Scala为Else操作选择更精确的返回类型,但它失败了。这是结构:

修改:这是一个小例子。下面是对上下文的更全面的介绍。对于回答这个问题,可能只关注这个极小的情况就足够了,而要理解我做的更长的例子可能会更好。

trait GE
trait If[A] {
  def Else[B >: A, Out](cond: => B)(implicit result: Result[B, Out]): Out
}
trait IfGE extends If[GE] with GE

case class SinOsc()     extends GE
case class WhiteNoise() extends GE

trait LowPri {
  implicit def AnyRes[A]: Result[A, If[A]] = ???  // !
}
object Result extends LowPri {
  implicit def GERes: Result[GE, IfGE] = ???
}
sealed trait Result[A, Out]

def IfExample: If[SinOsc]

val res1: GE = IfExample.Else(WhiteNoise())
val res2: GE = IfExample.Else[GE, IfGE](WhiteNoise())

此处,res1的类型推断和隐式解析失败,而明确将类型放入res2使其工作。我需要让res1工作,即不指定类型参数。

更长的例子:

(1)带有一些含义的图元素,可以使用数字作为图元素并应用二元运算符:

object GE {
  implicit def intIsGE   (x: Int   ): GE = ???
  implicit def doubleIsGE(x: Double): GE = ???

  implicit class GEOps(private val ge: GE) extends AnyVal {
    def <= (that: GE): GE = ???
    def >  (that: GE): GE = ???
    def &  (that: GE): GE = ???
    def poll(): Unit = ???
  }
}
trait GE

(2)分支结构:

object If {
  def apply(cond: => GE): IfBuilder = ???
}

trait IfBuilder {
  def Then [A](branch: => A): If[A]
}

trait If[A] {
  def Else: ElseBuilder[A]
  def ElseIf(cond: => GE): ElseIfBuilder[A]
}

trait IfGE extends If[GE] with GE

object ElseBuilder {
  trait LowPri {
    implicit def AnyRes[A]: Result[A, If[A]] = ???
  }
  object Result extends LowPri {
    implicit def GERes: Result[GE, IfGE] = ???
  }
  sealed trait Result[A, Out]
}
trait ElseBuilder[A] {
  def apply[B >: A, Out](b: => B)(implicit res: ElseBuilder.Result[B, Out]): Out
}

trait ElseIfBuilder[A] {
  def Then [B >: A](branch: => B): If[B]
}

(3)一些示例图元素:

case class Out(signal: GE)
case class SinOsc(freq: GE) extends GE
case class Dust(freq: GE) extends GE
case class WhiteNoise() extends GE

(4)测试套件:

trait Tests {
  def freq: GE

  // ---- Unit/Any result ----

  val res0 = If (freq > 600) Then {
    Out(SinOsc(freq))
  }

  val res1 = If (freq > 400 & freq <= 600) Then {
    Out(SinOsc(freq))
  } Else {
    freq.poll()
  }

  // ---- GE result ----

  val res2: GE = If (freq > 100) Then {
    SinOsc(freq)
  } Else {
    WhiteNoise()
  }

  val res3: GE = If (freq > 1000) Then {
    SinOsc(freq)
  } ElseIf (freq > 100) Then {
    Dust(freq)
  } Else {
    WhiteNoise()
  }

  Out(res3)
}

但是最后两个测试(res2res3)不起作用。返回类型显然不是IfGE,而只是If[GE]如何解决此问题,以便最后两个示例找到GERes而不是AnyRes

1 个答案:

答案 0 :(得分:3)

关于你的简短例子:

定义implicit def GERes: Result[GE, IfGE]完全对应于GE类型,但WhiteNoise()的类型为WhiteNoise <: GE,并且不适合隐式。

您可以更改隐式的定义,使其适用于GE的子类型:

object Result extends LowPri {
  implicit def GERes[T <: GE]: Result[T, IfGE] = ???
}

或者在其第一个类型参数中将Result定义为逆变。这将使隐含也适用于子类型:

sealed trait Result[-A, Out]
object Result extends LowPri {
  implicit def GERes: Result[GE, IfGE] = ???
}