如何在扩展共同特性的对象的Hlist上进行映射?

时间:2019-06-14 09:41:45

标签: scala shapeless

我有一个特征和扩展它的对象。

trait Common[K] {
  def name: String
  def encode(k: K): String = name + k.toString
}

object A extends Common[Int] {
  override def name: String = "a"
}

object B extends Common[Int] {
  override def name: String = "b"
}

object C extends Common[Int] {
  override def name: String = "c"
}

我要创建此对象的列表并在其上进行映射:

val hl = A :: B :: C :: HNil
val result: List[Int => String] = hl.map(EncodePoly).toList

实现Poly功能的不同尝试:

object EncodePoly extends Poly1 {
  implicit def indCase[K]: Case.Aux[Common[K], K => String] = at[Common[K]] {
    common => k =>
      common.encode(k)
  }
}

object EncodePoly extends Poly1 {
  implicit def indCase[K, C <: Common[K]]: Case.Aux[C, K => String] = at[C] {
    common => k =>
      common.encode(k)
  }
}

编译器告诉我:

  

错误:(45,43)找不到参数映射器的隐式值:   shapeless.ops.hlist.Mapper [com.test.EncodePoly.type,com.test.A.type ::   com.test.B.type :: com.test.C.type :: shapeless.HNil] val结果:   List [Int => String] = hl.map(EncodePoly).toList

我也尝试将依赖类型用于普通特征而不是类型参数。似乎没有任何作用。我应该如何使用对象列表?

1 个答案:

答案 0 :(得分:5)

您的第二个EncodePoly已经接近,但是编译器不够聪明,无法推断K应该是Int,然后C应该是单例类型。您可以通过使用<:<而不是<:编码子类型关系来帮助类型推断:

trait Common[K] {
  def name: String
  def encode(k: K): String = name + k.toString
}

object A extends Common[Int] {
  override def name: String = "a"
}

object B extends Common[Int] {
  override def name: String = "b"
}

object C extends Common[Int] {
  override def name: String = "c"
}

import shapeless.{ ::, HNil, Poly1 }

object EncodePoly extends Poly1 {
  implicit def indCase[K, C](implicit ev: C <:< Common[K]): Case.Aux[C, K => String] = at[C] {
    common => k => common.encode(k)
  }
}

然后:

scala> val hl = A :: B :: C :: HNil
hl: A.type :: B.type :: C.type :: shapeless.HNil = A$@3f044518 :: B$@282b7aad :: C$@7c130749 :: HNil

scala> val result: List[Int => String] = hl.map(EncodePoly).toList
result: List[Int => String] = List(EncodePoly$$$Lambda$5555/1493211716@7c987ea3, EncodePoly$$$Lambda$5555/1493211716@10be689, EncodePoly$$$Lambda$5555/1493211716@5dd3c2f2)

如果您可以在K定义中将Int固定为EncodePoly,那也可以。