Сопоставить Shapeless hlist типа F[T1] :: :: F[Tn] :: HNil с типом T1 :: :: Tn :: HNil (последовательность на уровне типов)

Я создаю общую функцию, которая принимает HList формы F[T1] :: ... :: F[Tn] :: HNil, преобразует ее в F[T1 :: ... :: Tn :: HNil], а затем должна передать ее в переданный блок. Однако для того, чтобы это работало, мне нужно извлечь Тип HList в этом F[_]. Я нашел что-то отдаленно относящееся к Shapeless' hlistconstraints:

/**
 * Type class witnessing that every element of `L` has `TC` as its outer type constructor. 
 */
trait UnaryTCConstraint[L <: HList, TC[_]]

... но это можно использовать только для проверки того, что переданный hlist действительно состоит только из F[_]; однако, похоже, нет способа извлечь этот _ бит, так сказать, в собственный hlist.

Где я должен искать, чтобы найти что-то, чтобы сделать работу? Или я должен просто не ожидать найти что-то из коробки и вместо этого самостоятельно построить вычисление типа?

Раскрытие информации: этот вопрос является вспомогательным к Generic transform/fold/map над кортежем/hlist, содержащим некоторые F[_], но, тем не менее, по моему мнению, не менее полезен, чем отдельный вопрос.


person Erik Kaplun    schedule 21.10.2014    source источник
comment
Можете ли вы привести пример ввода и ожидаемого результата для простого случая?   -  person Nate    schedule 22.10.2014
comment
Это вообще возможно? Разве List[Int] :: List[String] :: HList не сможет стать List[Int :: String :: HList] только в том случае, если каждый список будет одинаковой длины, или вы хотите получить перекрестное произведение?   -  person Nate    schedule 22.10.2014
comment
@Nate: хорошая мысль; скажем так, он должен работать с такими вещами, как Validation и Option, в которых содержится не более одного элемента.   -  person Erik Kaplun    schedule 22.10.2014
comment
Это будет работать как продукт... Так, например, если у вас есть List("a")::List(1,2)::HNil, результатом такой операции будет List("a"::1::HNil,"a"::2::HNil)   -  person Alejandro Navas    schedule 20.11.2018


Ответы (2)


Похоже, Sequencer уже делает это:

import scala.language.higherKinds

class Wrap[TC[_]] {
  def foo[L1 <: HList, L2 <: HList](xs: L1)(implicit
    seq: Sequencer.Aux[L1, TC[L2]] // L2 is the type we're looking for
  ): L2 = ???
}

val ret = new Wrap[Option].foo(1.some :: 2.some :: HNil)
// ret now has type Int :: Int :: HNil

... но в настоящее время я не могу придумать способ сделать это лучше,

  • избавление от класса-обертки;
  • Scala выводит TC как Option.

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

person Erik Kaplun    schedule 22.10.2014

Для этого необходимо, чтобы F обладал хотя бы аппликативными возможностями. Как только вы это убедитесь, это вполне возможно, как в этом коде:

trait HApplicative[Eff[_], InL <: HList] extends {
  type OutL <: HList
  def product: InL => Eff[OutL]
}

object HApplicative {
  case class HAppAux[Eff[_], InL <: HList, OutL0 <: HList](zipper: InL => Eff[OutL0]) extends HApplicative[Eff, InL] {
    type OutL = OutL0

    override def product: InL => Eff[OutL0] = zipper
  }

  implicit def nilHApp[Eff[_]](implicit app: Applicative[Eff]): HApplicative[Eff, HNil] {type OutL = HNil} =
    HAppAux[Eff, HNil, HNil](_ => app.pure(HNil))


  implicit def consHApp[Eff[_], InH, InT <: HList](
    implicit tailHApp: HApplicative[Eff, InT],
    app: Applicative[Eff]
  ): HApplicative[Eff, Eff[InH] :: InT] {type OutL = InH :: tailHApp.OutL} = HAppAux[Eff, Eff[InH] :: InT, InH :: tailHApp.OutL] {
    case (inH: Eff[InH]) :: inT => app.map2(inH, tailHApp.product(inT)) {
      case (head, tail) => head :: tail
    }
  }

  def product[Eff[_],InL<:HList](inL:InL)(implicit happ:HApplicative[Eff,InL]):happ.OutL = happ.product(inL)
}
person Alejandro Navas    schedule 15.11.2018