Как получить бесформенную опцию, которая действует на HList определенного подтипа - PullRequest
1 голос
/ 27 июня 2019

Я попытался создать бесформенную опцию, которая действует на HList объектов данного типа. Однако я не могу понять, как заставить его работать с HList объектов, которые являются подтипом этого объекта. Вот пример:

    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

Первый applyTransforms успешно компилируется, но второй завершается с ошибкой:

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)

Я подозреваю, что проблема с hconsTransformer, но я не могу понять, почему это не должно выходить за рамки SubTransform s.

1 Ответ

1 голос
/ 27 июня 2019

Измените шаг рекурсии, если хотите, чтобы он работал с подтипами 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)
    }
  }

или сделать Transformer контравариантным по отношению к TH

trait Transformer[I, -TH <: HList] { ...
...