He tratado de crear una operación sin forma que actúa en un hlist de objetos de un tipo dado. Sin embargo, no puedo averiguar cómo hacer que funcione en un hlist de objetos que sean un subtipo de ese objeto. Aquí hay un ejemplo:

    trait Transform[I, O] { def f: I => O }

    trait Transformer[I, TH <: HList] {
      type Out <: HList
      def transform(input: I, transforms: TH): Out
    }
    object Transformer {
      type Aux[I, Transforms <: HList, Out0] = Transformer[I, Transforms] { type Out = Out0 }

      def apply[I, Transforms <: HList](implicit transformer: Transformer[I, Transforms]): Aux[I, Transforms, transformer.Out] = transformer

      implicit def hnilTransformer[I]: Aux[I, HNil, HNil] =
        new Transformer[I, HNil] {
          type Out = HNil
          override def transform(input: I, transforms: HNil): HNil = HNil
        }

      implicit def hconsTransformer[I, O, TIn <: HList, TOut <: HList]
        (implicit t: Transformer.Aux[I, TIn, TOut]): Aux[I, Transform[I, O] :: TIn, O :: TOut] =
          new Transformer[I, Transform[I, O] :: TIn] {
            type Out = O :: TOut
            override def transform(input: I, transforms: Transform[I, O] :: TIn): Out = {
              transforms.head.f(input) :: t.transform(input, transforms.tail)
            }
          }
    }

    def applyTransforms[I, TH <: HList](transforms: TH)(input: I)
                                       (implicit transformer: Transformer[I, TH]): transformer.Out = {
      transformer.transform(input, transforms)
    }

    val double = new Transform[Int, Int] { def f = _ * 2 }
    val int2Str = new Transform[Int, String] { def f = _.toString }

    applyTransforms(double :: int2Str :: HNil)(4) // shouldBe 8 :: "4" :: HNil

    trait SubTransform[I, O] extends Transform[I, O]
    val doubleSub = new SubTransform[Int, Int] { def f = _ * 2 }
    val int2StrSub = new SubTransform[Int, String] { def f = _.toString }

    applyTransforms(doubleSub :: int2StrSub :: HNil)(4) // fails to compile

El primer applyTransforms se compila con éxito, pero el segundo falla con el error:

ShapelessSpec.scala:124: could not find implicit value for parameter transformer: Transformer[Int,shapeless.::[SubTransform[Int,Int],shapeless.::[SubTransform[Int,String],shapeless.HNil]]]
    applyTransforms(doubleSub :: int2StrSub :: HNil)(4)

Sospecho que el problema es con el hconsTransformer pero no puedo ejercer por qué esto no debería estar en margen para SubTransform s.

1
William Carter 27 jun. 2019 a las 16:40

1 respuesta

La mejor respuesta

Modifique su paso de recursión si desea que esto trabaje con subtipos de Transform

implicit def hconsTransformer[I, O, TIn <: HList, TOut <: HList, X]
(implicit t: Transformer.Aux[I, TIn, TOut], ev: X <:< Transform[I, O]): Aux[I, X :: TIn, O :: TOut] =
  new Transformer[I, X :: TIn] {
    type Out = O :: TOut
    override def transform(input: I, transforms: X :: TIn): Out = {
      transforms.head.f(input) :: t.transform(input, transforms.tail)
    }
  }

O hacer Transformer contravariante con respecto a TH

trait Transformer[I, -TH <: HList] { ...
1
Dmytro Mitin 27 jun. 2019 a las 14:45