Scala中“地图”的通用容器类型(高级容器)

时间:2018-09-22 15:13:16

标签: scala generics higher-kinded-types

我正在尝试定义一个用容器类型(高级类型)参数化的案例类。只要定义了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}},SeqSet等)

有什么办法告诉scala编译器容器类型必须具有List方法吗?

1 个答案:

答案 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,并且因此,它不等于您想要的那个。