您可以为此使用无形的多态函数:
// 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
这些类型类成为一个特例。