Написание класса типов с выбором, зависящим от контекста

Мой исходный код:

sealed trait Adder[L <: HList, U] extends DepFn2[L, Vector[U]]

object Adder {
  def apply[L <: HList, U: Ordering](implicit adder: Adder[L, U]): Aux[L, U, adder.Out] = adder

  type Aux[L <: HList, U, Out0] = Adder[L, U] { type Out = Out0 }

  implicit def found[T <: HList, U: Ordering]: Aux[Vector[U] :: T, U, Vector[U] :: T] =
    new Adder[Vector[U] :: T, U] {
      type Out = Vector[U] :: T
      def apply(l: Vector[U] :: T, collection: Vector[U]): Out = {
        (l.head ++ collection).sorted :: l.tail
      }
    }

  implicit def notFound[H, T <: HList, U: Ordering, OutT <: HList](implicit ut: Aux[T, U, OutT]): Aux[H :: T, U, H :: OutT] =
    new Adder[H :: T, U] {
      type Out = H :: OutT
      def apply(l: H :: T, collection: Vector[U]): Out = {
        val outT = ut(l.tail, collection)
        l.head :: outT
      }
    }

  implicit def empty[U: Ordering]: Aux[HNil, U, Vector[U] :: HNil] =
    new Adder[HNil, U] {
      type Out = Vector[U] :: HNil
      def apply(l: HNil, collection: Vector[U]): Out = collection :: HNil
    }
}

Я нашел ошибку, из-за которой вещи, не связанные с контекстом Ordering, передаются через notFound вместо found, что, на первый взгляд, неудивительно. Я попытался исправить ошибку, добавив еще один имплицит, который должен срабатывать при отсутствии Ordering:

  implicit def foundNoOrdering[T <: HList, U]: Aux[Vector[U] :: T, U, Vector[U] :: T] =
    new Adder[Vector[U] :: T, U] {
      type Out = Vector[U] :: T
      def apply(l: Vector[U] :: T, collection: Vector[U]): Out = {
        l.head ++ collection :: l.tail
      }
    }

Однако это приводит к двусмысленной неявной связи между foundNoOrdering и found. Как я могу иметь разные пути кода в зависимости от того, есть ли Ordering или нет?


person Reactormonk    schedule 08.03.2015    source источник


Ответы (1)


Стандартный трюк состоит в том, чтобы уменьшить приоритет, поместив неявное свойство в трейт-предок.

object Adder extends LowPriorityAdderImplicits {
   implicit def found...
}

trait LowPriorityAdderImplicits {
  implicit def foundNoOrdering....
}  

Некоторые из них вы найдете в стандартной библиотеке. LowPriorityImplicits кажется привычным в названии.

В спецификации:

  • SLS §7.2 Неявные параметры

Если есть несколько допустимых аргументов, которые соответствуют типу неявного параметра, будет выбран наиболее конкретный из них с использованием правил разрешения статической перегрузки (§6.26.3).

  • SLS §6.26.3: Соответствующий фрагмент слишком длинный, чтобы цитировать его полностью, но у вас есть кое-что о

Класс или объект C является производным от класса или объекта D, если выполняется одно из следующих условий:

• C является подклассом D или

• C является сопутствующим объектом класса, производного от D, или

• D является сопутствующим объектом класса, производным от которого является C.

и там выводится, что делает его более конкретным и получает приоритет в разрешении. Я считаю, что один был сделан только для неявного.

person Didier Dupont    schedule 08.03.2015
comment
Почти. Когда не добавляю найденное, получаю: [ошибка] обоих методов foundNoOrdering в трейте LowPriorityAdderImplicits типа [T ‹: shapeless.HList, U]=> Adder.Aux[shapeless.::[Vector[U],T ],U,shapeless.::[Vector[U],T]] [ошибка] и метод notFound в объектном сумматоре типа [H, T ‹: shapeless.HList, U, OutT ‹: shapeless.HList] (неявный ut : Adder.Aux[T,U,OutT])Adder.Aux[бесформенный.::[H,T],U,shapeless.::[H,OutT]] [ошибка] соответствует ожидаемому типу Adder.Aux[бесформенный. ::[Вектор[Foo],shapeless.HNil],Foo,Result] - person Reactormonk; 08.03.2015
comment
Я исправил это, переместив foundNoOrdering в низкий имплицитный трейт. - person Reactormonk; 08.03.2015