Странное влияние неявного Mapper на неявный параметр Mapped

Предположим, у меня есть контейнер-маркер

case class TypedString[T](value: String)

и частичная функция трюк

abstract class PartFunc[Actual <: HList] {
    val poly: Poly

    def apply[L1 <: HList](l: L1)(implicit
                                  mapped: Mapped.Aux[Actual, TypedString, L1],
                                  mapper: Mapper[poly.type, L1]): L1 = l
}

Поли для Mapper

object f extends (TypedString ~>> String) {
    def apply[T](s : TypedString[T]) = s.value
}

и метод результата

def func[Actual <: HList] = new PartFunc[Actual] {
    val poly = f
}

Пример использования:

func[
    Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)

Этот код не выполняется во время компиляции, потому что компилятор не может найти Mapped неявный параметр:

could not find implicit value for parameter mapped: 
    shapeless.ops.hlist.Mapped[shapeless.::[Int,shapeless.::[String,shapeless.HNil]],nottogether.MapperTest.TypedString]{type Out = shapeless.::[nottogether.MapperTest.TypedString[Int],shapeless.::[nottogether.MapperTest.TypedString[String],shapeless.HNil]]}
        ](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)

Но если мы удалим Mapper неявный параметр из PartFunc.apply(...) подпись все отлично работает. Поэтому я понятия не имею, почему и как Mapper влияние на Mapped,

1 ответ

Решение

Компилятор жалуется на Mapped в то время как фактическая проблема с Mapper, Я не уверен, почему, но, похоже, что-то не так с получением Mapped для одиночного типа poly.type когда poly является абстрактным значением или аргументом конструктора PartFunc,

Решение было бы сделать poly P <: Poly и передавая тип синглтон вместе с Actual когда мы создаем PartFunc:

import shapeless._
import ops.hlist.{Mapped, Mapper}
import poly.~>>

abstract class PartFunc[Actual <: HList, P <: Poly] {
  val poly: P
  def apply[L1 <: HList](l: L1)(implicit
    mapped: Mapped.Aux[Actual, TypedString, L1],
    mapper: Mapper[P, L1]
  ): mapper.Out = mapper(l)
}

def func[Actual <: HList] = new PartFunc[Actual, f.type] { val poly = f }

Или с обычным классом:

class PartFunc[Actual <: HList, P <: Poly](poly: P) {
  def apply[L1 <: HList](l: L1)(implicit
    mapped: Mapped.Aux[Actual, TypedString, L1],
    mapper: Mapper[P, L1]
  ): mapper.Out = mapper(l)
}

def func[Actual <: HList] = new PartFunc[Actual, f.type](f)

Обратите внимание, что теперь мы должны написать mapper(l), так как l map poly все равно будет искать Mapped[poly.type, L1],

Теперь мы можем позвонить func:

func[
  Int :: String :: HNil
](TypedString[Int]("42") :: TypedString[String]("hello") :: HNil)
// String :: String :: HNil = 42 :: hello :: HNil

Я уверен, что кто-то, обладающий более глубокими знаниями о системе типов Scala, сможет дать нам более четкое объяснение и, возможно, лучшее решение этой проблемы.

Другие вопросы по тегам