3

私は高次の型を持っており、それを使っていくつかの DSL を構築しようとしています。そして、この型を明示的に指定せずに型を受け入れることができる関数を定義する方法を探しています。

自己記述的な例:

class Wrap[T] (val data : T)

class DSL {
  def doSomething[T](x : Wrap[T]) =
    println(x.data)
  def <<=[T,W <: Wrap[T]](arg : W) : W = {
    doSomething(arg)
    arg
  }
  def <<-[T](arg : Wrap[T]) : Wrap[T] = {
    doSomething(arg)
    arg
  }
  def <<+[W <: Wrap[_]](arg : W) = {
    doSomething(arg)
    arg
  }
  def <<~(arg : Wrap[_]) = {
    doSomething(arg)
    arg
  }
}

class ExtendedInt(x : Int) extends Wrap[Int](x) {
  def expose() = println(data)
}

object Test {
  val dsl = new DSL
  val exi = new ExtendedInt(3)

  val x1 = dsl <<= exi
  val x2 = dsl <<- exi
  val x3 = dsl <<+ exi
  val x4 = dsl <<~ exi

  x1.expose()
  x2.expose()
  x3.expose()
  x4.expose()
}

4 つの異なる方法を試しましたが、4 つの異なるエラーが発生しました。

Casting.scala:15: error: no type parameters for method doSomething: (x: Wrap[T])Unit exist so that it can be applied to arguments (W)
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : W
 required: Wrap[?T]

    doSomething(arg)
    ^
Casting.scala:32: error: inferred type arguments [Nothing,ExtendedInt] do not conform to method <<='s type parameter bounds [T,W <: Wrap[T]]
  val x1 = dsl <<= exi
               ^
Casting.scala:38: error: value expose is not a member of Wrap[Int]
  x2.expose()
     ^
Casting.scala:40: error: value expose is not a member of Wrap[_$2]
  x4.expose()
     ^
four errors found

すべてのエラーは非常に説明的です。私は、scala のぎこちない型システムと制限に反対しません。しかし、私はまだ目的から遠く離れており、望ましい機能を実装するための別のハックを探すことに前向きです。

私が見落としている他の方法はありますか?

4

2 に答える 2

2

ここで問題を引き起こしている型推論の制限については、私の回答here (およびそこにリンクされているこの他の回答) を参照してください。あなたの最善の策は、おそらくビュー バウンド アプローチです。

class Wrap[T](val data: T)

class DSL {
  def doSomething[T](x : Wrap[T]) { println(x.data) }

  def <<=[T, W <% Wrap[T]](arg : W): W = {
    doSomething(arg)
    arg
  }
}

<:yourが に置き換えられていることに注意してください<%。これは期待どおりに機能します。

于 2012-11-23T23:47:58.383 に答える
1

@ Travis-Brown は、すべての入力情報を保持する必要があるため、コードに組み込むことができなかった解決策を提案しました。

更新された例には、新しい要件が組み込まれています。

class DSL {
  def doSomething[T](x : Wrap[T]) =
    println(x.data)
  /** Should really receive both types */
  def realWork[T, W <: Wrap[T]]( arg : W ) : W = {
    doSomething(arg)
    arg
  }

  /** syntax sugar for calling realWork*/
  def <<=[T, W <: Wrap[T]](arg : W) : W = realWork[T,W](arg) // This doesn't work
}

<<=メソッドを機能するものに置き換える必要があります。ビュー境界では、realWork を呼び出すのに十分な型情報を提供できませんでした。

引数を 2 倍にするだけで問題なく動作し、必要な型情報をコンパイラに送信することがわかりました。

  def <<=[T, W <: Wrap[T]](arg : W, trash : Wrap[T]) : W = realWork[T,W](arg)

使い方は不便ですが、簡単に改善できます。

implicit def doubleArg[T](arg : T) : (T,T) = (arg,arg)
...
def <<=[T, W <: Wrap[T]]( args : (W, Wrap[T]) ) : W = realWork[T,W](args._1)

暗黙的な変換には欠点が 1 つあります。Tuple2 は非常に一般的な型であり、任意のシングルをタプルにキャストするのは危険です。

そのため、型システム全体を汚染しない、暗黙的な変換専用の独自の型を導入しました。

class SugarPair[+T1 <: T2, +T2] (val s1 : T1) {
  def apply() : T1 = s1
}
implicit def toSugarPair[T](sugar : T) : SugarPair[T,T] = new SugarPair[T,T](sugar)
...
def <<=[T, W <: Wrap[T]](arg : SugarPair[W,Wrap[T]]) : W = realWork[T,W](arg() )
于 2012-11-24T21:35:21.617 に答える