我正在尝试定义一个用容器类型(高级类型)参数化的案例类。只要定义了map方法,此容器类型就可以是任何类型。
我想通过以下代码实现此结果:
import scala.language.higherKinds
case class Test[A, C[A]](init: A, trans: Map[A,C[A]]) {
def convert[B](conv: A => B): Test[B, C[B]] = {
val _init = conv(init)
val _trans = trans map {case (k,v) => (conv(k) -> (v map {x => conv(x)})}
Test(_init, _trans)
}
}
问题出在代码的v map {x => conv(x)}
部分。由于没有为C[A]
定义边界,因此显然不会编译。
问题在于,此容器类型可以是Id
(Scalaz样式,但是使用map
而不是|>
)和Option
或集合({ {1}},Seq
,Set
等)
有什么办法告诉scala编译器容器类型必须具有List
方法吗?
答案 0 :(得分:3)
实现所需目标的最佳方法是使用Functor类型类。
例如,下面是使用Cats的示例。
(您可以使用Scalaz做同样的事情)。
import scala.language.higherKinds
import cats.Functor
import cats.syntax.functor.toFunctorOps
final case class Test[A, C[_]](init: A, trans: Map[A,C[A]])(implicit CFunctor: Functor[C]) {
def convert[B](conv: A => B): Test[B, C] = {
val _init = conv(init)
val _trans = trans map { case (k,v) => conv(k) -> v.map(x => conv(x)) }
Test(_init, _trans)
}
}
但是,如果由于某种原因您不能或不想使用Cats / Scalaz,则可以尝试Structural Types。
import scala.language.higherKinds
import scala.language.reflectiveCalls
final case class Test[A, C[A] <: { def map[B](f: A => B): C[B]}](init: A, trans: Map[A,C[A]]){
def convert[B](conv: A => B): Test[B, C] = {
val _init = conv(init)
val _trans = trans map { case (k,v) => conv(k) -> v.map(x => conv(x)) }
Test(_init, _trans)
}
}
不过,请注意,最后一个将对Option
有效,但对List
无效,这很简单,因为List中的map
方法接收到隐式CanBuildFrom
,并且因此,它不等于您想要的那个。