Scala 类型类的反射

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

我正在为一些应用程序编写一个小的注册模块,并使用TypeClass来完成这项工作。主要逻辑基于喷洒路线:

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],然后它就可以完美地工作了。
事实上,这与使用类继承而不是隐式值的上限/下限非常相似。

非常感谢,这样一个愚蠢的错误让我损失了更多的时间=真丢脸