繁体   English   中英

如何定义一个返回类型基于Scala中参数类型和类型参数的方法?

[英]How to define a method for which the returntype is based on types of argument and type parameter in Scala?

我想定义一个方法,它具有一个返回类型,它依赖于传递给该方法的参数的类型和封闭特征的类型参数。 用文字描述我的意思是非常困难的,所以下面是一些要解释的片段。

我有几个构建块

// This is the target of a binding
trait BindableValue[T] {
  def set(value: T): Unit

  // this is the method I am strugling with
  def bindTo[S](o: S) = ???
}

// This will dispatch events of type T
trait Observable[T]

// This represents a value of type T that will 
// dispatch events if the value changes
trait ObservableValue[T] extends Observable[T] {
  def value: T
}

// An Observable can be converted to an optional ObservableValue
object ObservableValue {
  implicit def wrap[T](o:Observable[T]):ObservableValue[Option[T]] = 
    new ObservableValue[Option[T]] {
      val value = None
    }
}

绑定具有源和目标,并且存在两种类型:

  1. 完成:源类型和目标类型参数相同
  2. 不完整:源和目标类型参数不同

...

trait Binding[S, T] {
  def source: ObservableValue[S]
  def target: BindableValue[T]
}

class CompleteBinding[T](
  val source: ObservableValue[T], 
  val target: BindableValue[T]) extends Binding[T, T]

class IncompleteBinding[S, T](
  val source: ObservableValue[S], 
  val target: BindableValue[T]) extends Binding[S, T]

我可以构造以下实例。

val bindable1 = new BindableValue[Int] { def set(value:Int) = {} }
val property1 = new ObservableValue[Int] { def value = 0 }
val property2 = new ObservableValue[String] { def value = "" }

val bindable2 = new BindableValue[Option[Int]] { def set(value:Option[Int]) = {} }
val event1 = new Observable[Int] {}
val event2 = new Observable[String] {}

现在我想使用这样的实例:

// 'a' should be of type CompleteBinding
val a = bindable1 bindTo property1

// 'b' should be of type IncompleteBinding
val b = bindable1 bindTo property2

// 'c' should be of type CompleteBinding
val c = bindable2 bindTo event1

// 'd' should be of type IncompleteBinding
val d = bindable2 bindTo event2

我无法弄清楚如何定义方法bindTo以便它将编译上述4行并为所有值具有正确的具体类型。 我简直错过了有关Scala类型系统的知识。

虽然我很想找到一个解决方案,但我也希望了解如何在未来找到这样的解决方案。 如果您有针对上述问题的解决方案,您是否也可以指出一些我可以用来教育自己的资源?

类型可以帮助您解决问题。 首先让我们定义简单的特征:

trait Composeable[A, B, R] {
    def compose(a: A, b: B): R
}

它只需要ab并将它们组合在R型的其他容器中。

现在让我们为伴随对象中的CompleteBindingIncompleteBinding定义它的两个具体实现,并使它们隐式:

object Composeable extends LowPriorityComposables {
    implicit def  comCommplete[T] = new Composeable[ObservableValue[T], BindableValue[T], CompleteBinding[T]] {
        def compose(a: ObservableValue[T], b: BindableValue[T]) = new CompleteBinding(a, b)
    }
}

trait LowPriorityComposables {
    implicit def  comIncommplete[S, T] = new Composeable[ObservableValue[S], BindableValue[T], IncompleteBinding[S, T]] {
        def compose(a: ObservableValue[S], b: BindableValue[T]) = new IncompleteBinding(a, b)
    }
}

如您所见,实现非常简单。 它只需要ObservableValueBindableValue然后在Binding 我将它们分成不同的特征,因为一般来说它们都可以使用,如果你观察并绑定某种类型T的相同值( CompleteBinding的情况)。 通过在单独的特征中提取comIncommplete ,我告诉编译器,如果没有找到其他合适的隐式,它应该使用它。 换句话说,您告诉编译器总是尝试应用CompleteBinding ,如果无法完成,则应该应用IncompleteBinding

唯一剩下的就是定义使用Composeable类型类的bindTo方法:

def bindTo[S, R](o: ObservableValue[S])(implicit ev: Composeable[ObservableValue[S], BindableValue[T], R]): R = 
    ev.compose(o, this)

在这里我说,第一个参数是o ,它是一些包含S类型值的ObservableValue ,但我也希望编译找到一个证据,有一些隐含的,证明我可以用BindableValue[T]组成ObservableValue[S] BindableValue[T] 当我有这样的证据时,我只是用这个(可绑定的)构成可观察的。 正如您所看到的,类型参数R类似于自由变量 - 编译器可以解决它本身,因此您始终具有从bindTo方法返回的concreate类型信息。

现在所有测试用例都应该编译得很好:

val a: CompleteBinding[Int] = bindable1 bindTo property1
val b: IncompleteBinding[String, Int] = bindable1 bindTo property2
val c: CompleteBinding[Option[Int]] = bindable2 bindTo event1
val d: IncompleteBinding[Option[String], Option[Int]] = bindable2 bindTo event2

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM