【发布时间】:2017-07-25 08:34:07
【问题描述】:
拥有用于将 Map 转换为案例类的类型类:
/**
* Type class for transforming a map of values into a case class instance.
*
* @tparam T Wanted case class type.
*/
@implicitNotFound("Missing ToCaseClassMapper implementation for type ${T}")
trait ToCaseClassMapper[T <: Product] {
def toCaseClass(map: Map[String, Any]): T
}
并且这个函数可以隐式获取正确的映射器
def toCaseClass[T <: Product](map: Map[String, Any])(implicit mapper: ToCaseClassMapper[T]): T = {
mapper.toCaseClass(map)
}
可以用作toCaseClass[User](aUserMap) // returns User
但我也希望能够将此函数与 Option[Map[]] 或 Future[Map[]] 或 List[Map[]] 一起使用。 所以我使用这样的仿函数实现了一个通用函数:
def toCaseClass[T <: Product, F[_]: cats.Functor](fOfMaps: F[Map[String, Any]])(implicit mapper: ToCaseClassMapper[T]): F[T] = {
cats.Functor[F].map(fOfMaps)(map => toCaseClass(map)(mapper))
}
但是现在这个函数必须被用作toCaseClass[User,List](listOfUserMaps) // returns List[User]。
但是,我希望能够将函数用作
toCaseClass[User](listOfMaps)
toCaseClass[User](futureOfMap)
toCaseClass[User](optionOfMap)
无需指定函子类型。
这有可能吗?
可以用Shapeless的Lazy来解决这个问题吗?
编辑:解决方案
感谢 @Jasper-m 和 @dk14 的回答。
所以解决这个问题的“技巧”是在 Functor 类型之前首先在类中捕获类型“T”。我喜欢 @Jasper-m 使用 'apply' 方法的解决方案,因为这样可以使语法几乎与以前相似。
不过我做了一些调整。由于已经有“ToCaseClassMapper”类也捕获了“T”类型,我决定将它与“ToCaseClass”类结合起来。此外,使用@Jasper-m 的方法,当映射到像Option(value).map(toCaseClass) 这样的某个值时使用“toCaseClass”函数时,toCaseClass 的用法必须在值是 Map 或 List[Map] 时有所不同。
我现在的解决方案如下:
@implicitNotFound("Missing ToCaseClassMapper implementation for type ${T}")
trait ToCaseClassMapper[T <: Product] {
def toCaseClass(map: Map[String, Any]): T
import scala.language.higherKinds
def toCaseClass[F[_]: cats.Functor, A](fOfMaps: F[Map[String, A]]): F[T] = {
cats.Functor[F].map(fOfMaps)(toCaseClass)
}
}
由于ToCaseClassMapper 实例在使用toCaseClass 函数的地方已经隐式可用,所以我决定丢弃该函数并用mapper.toCaseClass(_) 替换它。这清理了一些不需要的代码,现在使用映射器的语法是相同的,无论值是 Map 还是 Option、List、Future(或任何其他 Functor)。
【问题讨论】:
标签: scala functor shapeless scala-cats