如何定义它的返回类型是基于类型斯卡拉参数和类型参数的方法?(How to define a meth

2019-08-16 17:41发布

我想定义其具有返回类型是依赖于传递到方法和包围性状的类型参数的参数的类型的方法。 这是相当困难的话我的意思来形容,所以下面是一些片段来解释。

我有几个积木

// 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
    }
}

A结合具有源和目标和存在两种:

  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的类型系统知识。

Allthough我很想找到一个解决方案,我也想了解如何获得这样的解决方案自己的未来。 如果您对上述问题的解决方案,可能你还点我的一些消息,我可以用它来教育自己?

Answer 1:

类型类可以帮助你解决问题。 首先,让我们定义简单特点:

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

它只是需要ab并合成它们类型的一些其它容器R

现在,让我们来定义它的2个具体实现为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应适用。

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

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

在这里,我说,第一个参数是o这是一些ObservableValue包含类型的值S ,但我也想编译找到一个证据,即存在一些隐了,事实证明,我可以组成ObservableValue[S]BindableValue[T] 。 当我有这样的证据,我只是撰写观察到这个(绑定)。 正如你所看到的,类型参数R是像自由变量-编译器可以计算出来,但本身,所以你总是有从返回concreate类型信息bindTo方法。

现在,所有你测试用例应该编译就好了:

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


文章来源: How to define a method for which the returntype is based on types of argument and type parameter in Scala?
标签: scala types