【问题标题】:Implicit parameter precedence隐式参数优先级
【发布时间】:2021-09-03 21:17:43
【问题描述】:

我试图将泛型类型转换为 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,Repr], implicit len: shapeless.ops.hlist.Length.Aux[Repr,N], 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, Int)].apply((1, 2))) // expected 1 :: 2 :: HNil

我想优先考虑ToHList.toHList 而不是ToHList.default。 我该如何解决这个错误?

【问题讨论】:

    标签: scala implicit shapeless


    【解决方案1】:

    如果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.Refuteshapeless.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
      }
    

    【讨论】:

      猜你喜欢
      • 2012-01-27
      • 1970-01-01
      • 1970-01-01
      • 2020-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-04
      • 1970-01-01
      相关资源
      最近更新 更多