1

そのメソッドに渡される引数の型とそれを囲むトレイトの型パラメーターに依存する 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
    }
}

バインディングにはソースとターゲットがあり、次の 2 種類があります。

  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 型システムに関する知識が欠けているだけです。

私は解決策を見つけたいと思っていますが、将来そのような解決策に到達する方法も理解したいと思っています。上記の問題の解決策がある場合は、自分自身を教育するために使用できる情報源をいくつか教えていただけますか?

4

1 に答える 1

3

型クラスは、問題の解決に役立ちます。まず、単純なトレイトを定義しましょう:

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

type の他のコンテナに取り込んaで構成するだけです。bR

CompleteBinding次に、コンパニオン オブジェクトの 2 つの具体的な実装を定義しIncompleteBinding、それらを暗黙的にします。

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)

ここで、最初の引数はtype の値を含むoものであると言いますが、コンパイルして、 で構成できる暗黙の証明が存在するという証拠も見つけたいと思います。私がそのような証拠を持っているとき、私はこれでオブザーバブルを作成します(バインド可能)。ご覧のとおり、型パラメーターは自由変数のようなものです。コンパイラーはそれ自体を理解できるため、常にメソッドから返される concreate 型情報を取得できます。ObservableValueSObservableValue[S]BindableValue[T]RbindTo

これで、すべてのテスト ケースが正常にコンパイルされるはずです。

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
于 2013-02-03T16:29:25.567 に答える