Scala 类型类的反射
我正在为一些应用程序编写一个小的注册模块,并使用TypeClass来完成这项工作。主要逻辑基于喷洒路线:Scala 类型类的反射,scala,generics,reflection,typeclass,Scala,Generics,Reflection,Typeclass,我正在为一些应用程序编写一个小的注册模块,并使用TypeClass来完成这项工作。主要逻辑基于喷洒路线: val api = post { path("register" / Rest) { rest => rest match { case "user" => register[User] // ... other cases } } } } 为了简化,我为用户提供了以下数据类型: case class
val api = post {
path("register" / Rest) { rest =>
rest match {
case "user" => register[User]
// ... other cases
}
}
}
}
为了简化,我为用户提供了以下数据类型:
case class User(id: String, email: String)
为了从POST请求中提取实体,我使用typeclass:
trait EntityExtractor[T] {
def extractFromRequest: Directive[T :: String :: HNil]
}
用户的实现:
object User {
implicit val extractor = new EntityExtractor[User] {
def extractFromRequest: Directive[User :: String :: HNil] =
formFields('email, 'pass).hmap {
case email :: pass :: HNil =>
val id = UID.genId
User(id, email) :: pass :: HNil
}
}
}
问题出现在使用typeclass指令的register方法中:
def register[T] =
extractEntity[T].apply { entity => // fails here
validateEntity(entity) {
completeRegistration(entity)
}
}
}
def extractEntity[T: EntityExtractor]: Directive[Entity[T] :: HNil] = {
implicitly[EntityExtractor[T]].extractFromRequest.hmap {
case entity :: pass :: HNil => Entity(entity, pass) :: HNil
}
}
它失败了,出现了一个异常:找不到EntityExtractor[T]类型的证据参数的隐式值
有没有办法用反射类型标签或清单来解决这个问题,而不需要模式匹配?希望下面这个有点做作的例子能稍微澄清一下整个问题 考虑与当前代码对应的以下代码:
object Main {
def main(args: Array[String]) {
implicit val intIntable = new Intable[Int] {
def toInt(obj: Int) = obj
}
println(squareAndDouble(10: Int))
}
// Corresponds to your register[T] function
def squareAndDouble[T](obj: T) = {
val d = double[T](obj)
d*d
}
// Corresponds to your extractEntity[T] function
def double[T: Intable](obj: T) = {
implicitly[Intable[T]].toInt(obj)*2
}
}
trait Intable[T] {
def toInt(obj: T): Int
}
此代码未编译,但出现以下错误:
test.scala:11: error: could not find implicit value for evidence parameter of type Intable[T]
val d = double[T](obj)
现在让我们使用隐式参数重写它,这是编译器在看到上下文绑定时为我们所做的:
object Main {
def main(args: Array[String]) {
implicit val intIntable = new Intable[Int] {
def toInt(obj: Int) = obj
}
println(squareAndDouble(10: Int))
}
def squareAndDouble[T](obj: T) = {
val d = double[T](obj)
d*d
}
def double[T](obj: T)(implicit intable: Intable[T]) = {
intable.toInt(obj)*2
}
}
trait Intable[T] {
def toInt(obj: T): Int
}
此程序也不编译:
test.scala:11: error: could not find implicit value for parameter intable: Intable[T]
val d = double[T](obj)
现在应该更清楚为什么它不编译了。它失败的原因很简单,因为squareAndDouble函数中没有squareAndDouble函数所需的T类型参数的隐式值“证据”。由于double函数的类型参数需要一个证据,它可以是任何东西,所以这个证据出现在squareAndDouble范围内的唯一方法是再次从一个隐式参数中出现,但现在是在squareAndDouble函数中
基本上,这意味着您必须“传递”泛型参数的证据才能使用typeclass
让我们修复它:
object Main {
def main(args: Array[String]) {
implicit val intIntable = new Intable[Int] {
def toInt(obj: Int) = obj
}
println(squareAndDouble(10: Int))
}
// Added context bound here
def squareAndDouble[T: Intable](obj: T) = {
val d = double[T](obj)
d*d
}
def double[T: Intable](obj: T) = {
implicitly[Intable[T]].toInt(obj)*2
}
}
trait Intable[T] {
def toInt(obj: T): Int
}
现在它已成功编译并运行:
% scalac test.scala
% scala Main
400
另一种可能更简单的理解问题的方法如下。我们的squareAndDouble[T]函数接受无界类型参数T,但它尝试调用另一个函数double[T:Intable],其类型参数是有界的。这是不允许的:如果它是合法的,那么就有可能用任何类型调用squareAndDouble[T],即使是没有Intable typeclass实例的类型,这也会破坏double函数。因此,我们也必须为squareAndDouble函数添加不可改变的边界,以便它编译:squareAndDouble[T:Intable],然后它就可以完美地工作了。
事实上,这与使用类继承而不是隐式值的上限/下限非常相似。非常感谢,这样一个愚蠢的错误让我损失了更多的时间=真丢脸