具有多个类型参数的类型类的值类

时间:2018-02-21 21:09:52

标签: scala typeclass

通常的做法是提供一个帮助值类来访问类型类 - 比如

object Show {
  def apply[A](implicit sh: Show[A]): Show[A] = sh

  def show[A: Show](a: A) = Show[A].show(a)

  implicit class ShowOps[A: Show](a: A) {
    def show = Show[A].show(a)
  }

  implicit val intCanShow: Show[Int] =
    new Show[Int] {
      def show(int: Int): String = s"int $int"
    }
}

在我的例子中,我有一个带有2个参数的类型。我无法让值类工作(MapperOps)。我一直在“找不到参数映射器的隐含值”

trait Mapper[T, D] {
  def toDto(i: T): D
}

object Mapper {
  def map[T, D](i: T)(implicit mapper: Mapper[T, D]): D = implicitly[Mapper[T, D]].toDto(i)

  def apply[T, D](implicit mapper: Mapper[T, D]): Mapper[T, D] = mapper

  implicit def optionMapper[T, D](implicit mapper: Mapper[T, D]): Mapper[Option[T], Option[D]] = {
    new Mapper[Option[T], Option[D]] {
      override def toDto(i: Option[T]): Option[D] = i.map(mapper.toDto)
    }
  }

  implicit def seqMapper[T, D](implicit mapper: Mapper[T, D]): Mapper[Seq[T], Seq[D]] = {
    new Mapper[Seq[T], Seq[D]] {
      override def toDto(i: Seq[T]): Seq[D] = i.map(mapper.toDto)
    }
  }
   // Neither works
  implicit class MapperOps1[T,D](a: T) {
     def toDto = Mapper[T,D].toDto(a)
   }
   implicit class MapperOps2[T,D](a: T) {
     def toDto(implicit mapper: Mapper[T,D]) = Mapper[T,D].toDto(a)
   }
   implicit class MapperOps3[T,D](a: T) {
     def toDto[D](implicit mapper: Mapper[T,D]): D Mapper[T,D].toDto(a)
   }
}

1 个答案:

答案 0 :(得分:3)

注意

中的重要区别
implicit class ShowOps[A: Show](a: A) { ... }

你有这个A : Show部分,基本上已经进入了

implicit class ShowOps[A](a: A)(implicit s: Show[A]) { ... }

由于双参数“Type-Pair-Classes”没有整齐的[T : MyTypeClass] - 语法,因此必须将隐式Mapper作为单独提供 构造函数的参数:

implicit class MapperOps1[T, D](a: T)(implicit m: Mapper[T, D]) {
  def toDto = Mapper[T, D].toDto(a)
}

以下小测试编译并输出“42”:

implicit object IntToStringMapper extends Mapper[Int, String] {
  def toDto(i: Int): String = i.toString
}

import Mapper._

val s: String = 42.toDto
println(s)

或者,您可以尝试使用隐式转换(如果您未明确启用此功能,编译器会向您发出警告,如果您不明智地使用此功能,用户会抱怨您):

class MapperOps1[T,D](a: T, mapperTD: Mapper[T, D]) {
  def toDto = {
    implicit val m: Mapper[T, D] = mapperTD
    Mapper[T,D].toDto(a)
  }
}
import scala.language.implicitConversions
implicit def wrapIntoMapperOps1[T, D]
  (a: T)
  (implicit m: Mapper[T, D]): MapperOps1[T, D] = new MapperOps1(a, m)

我不会评论你的另外两个尝试:显然,编译器无法实例化那些,因为在它必须实例化一个包装器之前它没有获得关于类型参数的足够信息。