Как я могу применить HList произвольных функций к произвольному значению? - PullRequest
7 голосов
/ 20 июня 2019

Я бы хотел иметь возможность применить произвольный список Function1[I, ?] s к произвольному вводу I.Это то, что у меня есть:

    type StringInputFunction[T] = Function[String, T]

    val strLen: String => Int = _.length
    val strRev: String => String = _.reverse

    val functions = strLen :: strRev :: HNil
    val expected = 4 :: "evif" :: HNil

    object applyTo5 extends (StringInputFunction ~> Id) {
      override def apply[T](f: StringInputFunction[T]): Id[T] = f("five")
    }
    def applyFunctionsTo5[FH <: HList, OH <: HList](fs: FH)
        (implicit constrain: UnaryTCConstraint[FH, StringInputFunction],
         mapper: Mapper.Aux[applyTo5.type, FH, OH]): mapper.Out = {
      fs.map(applyTo5)
    }
    applyFunctionsTo5(functions) shouldBe expected

    class ApplyTo(string: String) extends (StringInputFunction ~> Id) {
      override def apply[T](f: StringInputFunction[T]): Id[T] = f(string)
    }
    def applyFunctionsTo[FH <: HList, OH <: HList]
        (fs: FH, input: String)
        (implicit constrain: UnaryTCConstraint[FH, StringInputFunction],
         mapper: Mapper.Aux[ApplyTo, FH, OH]): mapper.Out = {
      val applyTo = new ApplyTo(input)
      fs.map(applyTo)
    }
    applyFunctionsTo(functions, "five") shouldBe expected

Это приводит к ошибке компиляции:

ShapelessSpec.scala:81: could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[applyTo.type,FH]
      fs.map(applyTo)
ShapelessSpec.scala:83: could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper.Aux[ApplyTo,shapeless.::[String => Int,shapeless.::[String => String,shapeless.HNil]],OH]
    applyFunctionsTo(functions, "five") shouldBe expected
  1. Как это исправить, чтобы работать с любым String вводом?
  2. Могу ли я изменить это обобщение еще больше для работы с любым типом ввода T?

1 Ответ

5 голосов
/ 20 июня 2019

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

import shapeless.{ ::, HNil }

val strLen: String => Int = _.length
val strRev: String => String = _.reverse

val functions = strLen :: strRev :: HNil

Затем вы можете написать это:

scala> functions.zipApply(functions.mapConst("five"))
res0: Int :: String :: shapeless.HNil = 4 :: evif :: HNil

Или это:

scala> def foo(in: String) = functions.zipApply(functions.mapConst(in))
foo: (in: String)Int :: String :: shapeless.HNil

scala> foo("six")
res1: Int :: String :: shapeless.HNil = 3 :: xis :: HNil

Это будет работать с любыми списками изфункции из определенного типа, применяемые к этому конкретному типу.

Суть дает несколько альтернативных подходов, но zipApply плюс mapConst кажется мне безусловно лучшим.

...