Поли складной тип возврата неверно выведен - PullRequest
0 голосов
/ 19 января 2019

Я пытаюсь создать функцию поли, которая сворачивается в кортеж из Foo s:

case class Foo[A](a: A)

object extractFold extends Poly2 {
  implicit def default[A, As <: HList]: Case.Aux[Foo[A], Foo[As], Foo[A :: As]] = {
    ???
  }
}

def extract[In, A <: HList, B <: HList](keys: In)
  (implicit
    gen: Generic.Aux[In, A],
    folder: RightFolder.Aux[A, Foo[HNil], extractFold.type, Foo[B]],
    tupler: Tupler[B])
: Foo[tupler.Out] = {
  ???
}

val result = extract((Foo(1), Foo("a")))

Функция работает во время выполнения, но выводимый компилятором тип результата всегда равен Foo[Unit], что неверно - в этом примере это должно быть Foo[(Int, String)]

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

Почему вы думаете, что

тип результата, который выводится компилятором, всегда Foo [Unit]

?

Следующий код

import shapeless.ops.hlist.{RightFolder, Tupler}
import shapeless.{::, Generic, HList, HNil, Poly2}
import scala.reflect.runtime.universe.{typeOf, Type, TypeTag}

object App {
  def getType[T: TypeTag](t: T): Type = typeOf[T]

  case class Foo[A](a: A)

  object extractFold extends Poly2 {
    implicit def default[A, As <: HList]: Case.Aux[Foo[A], Foo[As], Foo[A :: As]] = 
      at { case (Foo(a), Foo(as)) => Foo(a :: as) }
  }

  def extract[In, A <: HList, B <: HList](keys: In)(implicit
    gen: Generic.Aux[In, A],
    folder: RightFolder.Aux[A, Foo[HNil], extractFold.type, Foo[B]],
    tupler: Tupler[B]
  ): Foo[tupler.Out] =
    Foo(tupler(folder(gen.to(keys), Foo(HNil)).a))

  val result = extract((Foo(1), Foo("a")))

  def main(args: Array[String]): Unit = {
    println(
      getType(result)
    )
  }
}

печатает

App.Foo[(Int, java.lang.String)]

Более того, если вы измените строку

val result: Foo[Unit] = extract((Foo(1), Foo("a")))

, код не скомпилируется.


ПоКстати, то же самое можно сделать с

import shapeless.PolyDefns.~>
import shapeless.ops.hlist.{Comapped, NatTRel, Tupler}
import shapeless.{Generic, HList, Id}

object App {
  case class Foo[A](a: A)

  def extract[In, A <: HList, B <: HList](keys: In)(implicit
    gen: Generic.Aux[In, A],
    comapped: Comapped.Aux[A, Foo, B],
    natTRel: NatTRel[A, Foo, B, Id],
    tupler: Tupler[B]
  ): Foo[tupler.Out] =
    Foo(tupler(natTRel.map(new (Foo ~> Id) { def apply[T](foo: Foo[T]) = foo.a }, gen.to(keys))))

  val result = extract((Foo(1), Foo("a")))

  def main(args: Array[String]): Unit = {
    println(result)//Foo((1,a))
  }
}
0 голосов
/ 19 января 2019

Может быть, кто-то с лучшим пониманием бесформенного может дать вам лучший ответ.Согласно моему пониманию, проблема заключается в этапе вывода типа.Если вы указываете все типы явно, как в

val result: Foo[(Int, String)] = extract[(Foo[Int], Foo[String]),
    Foo[Int] :: Foo[String] :: HNil,
    Int :: String :: HNil]((Foo(1), Foo("a")))

, код правильно проверяет типы.Очевидно, что вы не хотите явно указывать эти типы.

Насколько я понимаю, компилятор не может вывести хорошие значения B и tupler.Out, поскольку они недостаточно тесно связаны с In и A.Один из способов обойти это - ввести промежуточную черту, например:

trait Extractor[L <: HList, HF] {
  type FR <: HList
  type TR
  val folder: RightFolder.Aux[L, Foo[HNil], HF, Foo[FR]]
  val tupler: Tupler.Aux[FR, TR]
}

object Extractor {
  type Aux[L <: HList, HF, FR0 <: HList, TR0] = Extractor[L, HF] {type FR = FR0; type TR = TR0}

  implicit def wrap[L <: HList, In, HF, FR0 <: HList, TR0](implicit folder0: RightFolder.Aux[L, Foo[HNil], HF, Foo[FR0]],
                                                           tupler0: Tupler.Aux[FR0, TR0]) = new Extractor[L, HF] {
    type FR = FR0
    type TR = TR0
    override val folder = folder0
    override val tupler = tupler0
  }
}

, а затем использовать ее следующим образом:

def extract[In, A <: HList, B <: HList, C](keys: In)
                                          (implicit gen: Generic.Aux[In, A],
                                           extractor: Extractor.Aux[A, extractFold.type, B, C])
: Foo[C] = {
  val hli = gen.to(keys)
  val fr = extractor.folder(hli, Foo(HNil))
  Foo(extractor.tupler(fr.a))
}

Это хакерское решение, но, по крайней мере, онокажется, работает (см. также онлайн демо ).

...