2

class A
class B extends A
class C extends A
class Container[+L <: HList](l: L)

什么是无形的编码方式如下?:

def foo[L <: HList](a: A): Container[L] = a match {
  case (b: B) => new Container(1 :: "a" :: HNil)
  case (c: C) => new Container(1.0 :: HNil)
  case _ => new Container(HNil)
}

然后以某种方式使用它:

val l1: Container[Int :: String :: HNil] = foo(new B)
val l2: Container[Double :: HNil] = foo(new C)
val l3: Container[String :: HNil] = foo(new C) // Compile-time error

请注意,上面的方法主要是不正确的,原因类似于“为什么 `List[B]` is not a subtype of `Seq[L]` when `class B extends A` and `L <: A`? ”。

4

2 回答 2

4

您可以为此使用无形的多态函数:

// Base case
class LowPrioFoo extends Poly1 {
  implicit def default[X] = at[X] { _ => new Container(HNil) } 
}

// Specific cases
object foo extends LowPrioFoo {
  implicit def atB = at[B] { _ => new Container(1 :: "a" :: HNil) }
  implicit def atC = at[C] { _ => new Container(1.0 :: HNil) }
}

现在您可以根据需要调用该函数foo

val x = foo(new A): Container[HNil]
val y = foo(new B): Container[Int :: String :: HNil]
val z = foo(new C): Container[Double :: HNil]

这与您所做的基本相同,但它被封装foo(并且接口与 shapeless 更好)。这样您就可以确保不会发生意外的转换。

附录

正如@MilesSabin 指出的那样,如果不使用参数的值,那么无形的多态函数没有多大用处。基于简单类型类的解决方案可能更好。这样的解决方案如下:

trait Foo[T] {
  type R
  def result: R
}

trait LowPrioFoo {
  implicit def default[X] = new Foo[X] {
    type R = Container[HNil]
    val result = new Container(HNil)
  }
}

object Foo extends LowPrioFoo {
  implicit val bFoo = new Foo[B] {
    type R = Container[Int :: String :: HNil]
    val result = new Container(1 :: "a" :: HNil)
  }
  implicit val cFoo = new Foo[C] {
    type R = Container[Double :: HNil]
    val result = new Container(1.0 :: HNil)
  }
}

def foo[A](x: A)(implicit f: Foo[A]): f.R = f.result

请注意,这已经非常接近Poly. 比较 traitFoo和 traitCaseAux以及Poly#Case哪些模型参数作为HList并允许result取决于实际值。这使得Foo这些类型类成为一个特例。

于 2013-08-13T20:58:22.290 回答
0

Finally understood the problem essence... Seems like as follows:

implicit def fromA(a: A): Container[HNil] = new Container(HNil)
implicit def fromB(b: B): Container[Int :: String :: HNil] = new Container(1 :: "a" :: HNil)
implicit def fromC(c: C): Container[Double :: HNil] = new Container(1.0 :: HNil)

Then it is eligible to write:

val l1: Container[Int :: String :: HNil] = new B
val l2: Container[Double :: HNil] = new C
// val l3: Container[String :: HNil] = new C // Compile-time error
val l4: Container[HNil] = new A

Any better solutions are welcome.

于 2013-08-13T20:27:17.917 回答