在函数上键入边界

时间:2013-11-20 18:52:01

标签: scala types

我无法通过参数化类型获取代码来传递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]                           ^

1 个答案:

答案 0 :(得分:1)

首先,如果你写的话,请回答你的问题:

private var prog = List[(Predicate[_ <: Any], Action[_ <: Any])]()

这一切都可以编译好。我们应该使用通配符,因为元素的类型是未知的。

其次,也许你输入错误,你不能使用你的on功能,使用它像:

on[String](s => !s.isEmpty)(s => !s.isEmpty)

由类型不匹配导致:type Action[T] = T => Unitprintln的类型为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