我无法通过参数化类型获取代码来传递scala编译器。我的目标是能够表达(Predicate, Action)
对象中显示的MyProg
对。
trait ProgBase {
type Predicate[T] = T => Boolean
type Action[T] = T => Unit
private var prog = List[(Predicate[Any], Action[Any])]()
final def on[T <: Any](pred: Predicate[T])(action: Action[T]) = {
prog = (pred, action) :: prog // gives type mismatch
}
// remainder of trait elided
}
object MyProg extends ProgBase {
on[String](s => !s.isEmpty) { s =>
println(s + " is not empty")
}
on[Int](i => i.isValidByte) { i =>
println(i + " can fit in a byte")
}
}
通过指定T
的上限为Any
,我希望这会安抚编译器,但很明显我错过了一些东西:
[error] ......ProgBase.scala:8 type mismatch;
[error] found : (T => Boolean, T => Unit)
[error] required: (Any => Boolean, Any => Unit)
[error] prog = (pred, action) :: prog
[error] ^
答案 0 :(得分:1)
首先,如果你写的话,请回答你的问题:
private var prog = List[(Predicate[_ <: Any], Action[_ <: Any])]()
这一切都可以编译好。我们应该使用通配符,因为元素的类型是未知的。
其次,也许你输入错误,你不能使用你的on
功能,使用它像:
on[String](s => !s.isEmpty)(s => !s.isEmpty)
由类型不匹配导致:type Action[T] = T => Unit
但println
的类型为Unit
,
所以作为一个变种你可以简单地写:type Action = Unit
。显然,你可以完全避免使用这种类型的别名。
第三,也许你已经知道了,我并没有告诉你,事实上,你丢失了有关谓词类型的所有信息 - 让我们使用Scala反射来检查它:
import scala.reflect.runtime.{universe => ru}
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
val s: String = "123"
val i: Int = 123
on[String](s => !s.isEmpty)(s => !s.isEmpty)
on[Int](i => i.isValidByte)(i => i.isValidByte)
getTypeTag((MyProg.prog.head._1)).tpe =:= ru.typeOf[(String) => Boolean] //>false!
所以你看到了问题。
要处理它,您可以使用异构列表。您可以找到无形的列表和其他各种强大的结构:https://github.com/milessabin/shapeless