我有一个更高阶的类型,并努力用它构建一些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
所有错误都非常具有描述性。我不反对斯卡拉笨拙的类型系统和局限性。但我仍然远离我的目标,我很乐意寻找另一种黑客来实现所需的功能。
还有其他方法我忽略了吗?
答案 0 :(得分:2)
请参阅我的回答here(以及与之关联的this other answer),以了解导致您遇到问题的类型推断限制。你最好的选择可能是视图绑定方法:
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
}
}
请注意,您的<:
已被<%
取代。这将按预期工作。
答案 1 :(得分: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。
我发现加倍的参数工作正常,并向编译器发送所需的类型信息。
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)
隐式转换有一个缺点: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() )