在Scala中,如何定义returntype基于参数类型和类型参数类型的方法?

在Scala中,如何定义returntype基于参数类型和类型参数类型的方法?,scala,types,Scala,Types,我想定义一个方法,它的returntype依赖于传递给该方法的参数的类型和封闭特性的类型参数。很难用语言来描述我的意思,所以下面是一些片段来解释 我有一些积木 // 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

我想定义一个方法,它的returntype依赖于传递给该方法的参数的类型和封闭特性的类型参数。很难用语言来描述我的意思,所以下面是一些片段来解释

我有一些积木

// 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
    }
}
绑定有一个源和一个目标,存在两种类型:

  • 完成:源和目标类型参数都相同
  • 不完整:源和目标类型参数不同
  • 我可以构造以下实例

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


    尽管我很想找到一个解决方案,但我也想知道自己将来如何找到这样的解决方案。如果您有上述问题的解决方案,您能给我指出一些我可以用来自学的资料吗?

    TypeClass可以帮助您解决这个问题。首先,让我们定义简单特征:

    trait Composeable[A, B, R] {
        def compose(a: A, b: B): R
    }
    
    它只需要
    a
    b
    并将它们组合到另一个
    R
    类型的容器中

    现在,让我们在companion对象中为
    CompleteBinding
    impletebinding
    定义它的两个具体实现,并将它们隐式化:

    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)
        }
    }
    
    如您所见,实现非常简单。它只需要将
    observeValue
    BindableValue
    合并到
    Binding
    中即可。我以不同的特征将它们分开,因为如果您观察并绑定某个类型的相同值
    T
    (例如
    CompleteBinding
    ),通常它们都可以使用。通过在单独的trait中提取
    comincomplete
    ,我告诉编译器,如果找不到其他合适的隐式表达式,它应该使用它。换句话说,您告诉编译器要始终尝试应用
    CompleteBinding
    ,如果无法实现,则应应用
    impletebinding

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

    def bindTo[S, R](o: ObservableValue[S])(implicit ev: Composeable[ObservableValue[S], BindableValue[T], R]): R = 
        ev.compose(o, this)
    
    这里我要说的是,第一个参数是
    o
    ,它是一些
    ObservableValue
    ,它包含
    S
    类型的值,但我还想找到一个证据,证明存在一些隐式的,证明我可以用
    BindableValue[T]
    组合
    ObservableValue[S]
    。当我有这样的证据时,我就用这个(可绑定的)组成可观察的。正如您所看到的,类型参数
    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
    

    非常感谢,这正是我需要知道的。我听说过类型课,甚至看了一段关于它们的视频。我的大脑只是不知道如何在这种情况下使用它们。我会去更多地了解这些。再次感谢!
    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