隐式参数优先级

问题描述

我试图将泛型类型转换为 HList:

trait ToHList[T] {
  type Out <: HList
  def apply(value: T): Out
}

trait LowPriorityToHList {
  implicit def default[T]: ToHList.Aux[T,T :: HNil] =
    new ToHList[T] {
      override type Out = T :: HNil

      override def apply(value: T): T :: HNil = value :: HNil
    }
}

object ToHList extends LowPriorityToHList {
  type Aux[T,Out0] = ToHList[T] { type Out = Out0 }

  def apply[T](implicit toHList: ToHList[T]): Aux[T,toHList.Out] = toHList

  implicit def toHList[T,Repr <: HList,N <: Nat](implicit
      gen: Generic.Aux[T,Repr],len: Length.Aux[Repr,N],lt: LT[Nat._0,N]): ToHList.Aux[T,Repr] =
    new ToHList[T] {
      override type Out = Repr

      override def apply(value: T): Repr = gen.to(value)
    }
}

object Main extends App {
  println(ToHList.apply[Int].apply(1)) // expected 1 :: HNil
  println(ToHList.apply[(Int,Int)].apply((1,2))) // expected 1 :: 2 :: HNil
}

我打算优先考虑ToHList.toHList而不是ToHList.default,但是 此代码导致以下编译错误

[error] ToHList.scala:39:24: ambiguous implicit values:
[error]  both method toHList in object ToHList of type [T,Repr <: shapeless.HList,N <: shapeless.Nat](implicit gen: shapeless.Generic.Aux[T,implicit len: shapeless.ops.hlist.Length.Aux[Repr,implicit lt: shapeless.ops.nat.LT[shapeless.Nat._0,N])ToHList.Aux[T,Repr]
[error]  and method default in trait LowPriorityToHList of type [T]=> ToHList.Aux[T,T :: shapeless.HNil]
[error]  match expected type ToHList[(Int,Int)]
[error]   println(ToHList.apply[(Int,2))) // expected 1 :: 2 :: HNil

我想优先考虑ToHList.toHList而不是ToHList.default。 如何修复此错误

解决方法

如果 toHListdefault 都适用,则它们具有相同的优先级,因此它们会产生歧义。事实上,虽然 default 定义在一个低优先级的超特征中,但它比 toHList 更具体。查看Why is this implicit ambiguity behaviour happening?

中的详细信息

因此没有理由将 default 放入低优先级的超特征中,这不会产生预期的影响。但是,如果您将 toHListdefault 放在同一个对象中,default 会因更具体而获胜。从 expected 1 :: 2 :: HNil 开始,您似乎希望反之亦然 toHList 获胜。您可以使用 shapeless.LowPriority

object ToHList {
  type Aux[T,Out0] = ToHList[T] { type Out = Out0 }

  def apply[T](implicit toHList: ToHList[T]): Aux[T,toHList.Out] = toHList

  implicit def toHList[T,Repr <: HList,N <: Nat](implicit
    gen: Generic.Aux[T,Repr],len: Length.Aux[Repr,N],lt: LT[Nat._0,N]
  ): ToHList.Aux[T,Repr] =
    new ToHList[T] {
      override type Out = Repr
      override def apply(value: T): Repr = gen.to(value)
    }

  implicit def default[T](implicit 
    lowPriority: LowPriority
  ): ToHList.Aux[T,T :: HNil] =
    new ToHList[T] {
      override type Out = T :: HNil
      override def apply(value: T): T :: HNil = value :: HNil
    }
}

或者,在这种特定情况下,您可以使用 shapeless.Refute,shapeless.OrElse

implicit def default[T](implicit
  orElse: OrElse[Refute[Generic[T]],Generic.Aux[T,HNil]]
): ToHList.Aux[T,T :: HNil] =
  new ToHList[T] {
    override type Out = T :: HNil
    override def apply(value: T): T :: HNil = value :: HNil
  }