I'd like to be able to apply an arbitrary list of Function1[I, ?]
s to an arbitrary input I
. This is what I have so far:
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
This results in the compile error:
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
String
input?T
? The * symbol is used to pass a variable number of arguments to a function. Typically, this syntax is used to avoid the code failing when we don't know how many arguments will be sent to the function.
You can send any data types of argument to a function (string, number, list, dictionary etc.), and it will be treated as the same data type inside the function.
An arbitrary argument list is a Python feature to call a function with an arbitrary number of arguments. It's based on the asterisk “unpacking” operator * . To catch an arbitrary number of function arguments in a tuple args , use the asterisk syntax *args within your function definition.
In order to pass multiple argument values to the function, Python provides us with Arbitrary Arguments also known as Python *args. In this, we use the asterisk (*) to denote this method before the parameter in the function. The asterisk (*) allows us to pass any number of values to the defined function.
I was thinking I'd done this exact operation before, and was able to find this gist from a few years ago. To summarize my example there, you can do this pretty nicely using only operations that are already provided by Shapeless, in a manner that looks a lot like how you'd do something like this with ordinary lists at the value level. Suppose you have the following setup:
import shapeless.{ ::, HNil }
val strLen: String => Int = _.length
val strRev: String => String = _.reverse
val functions = strLen :: strRev :: HNil
Then you can write this:
scala> functions.zipApply(functions.mapConst("five"))
res0: Int :: String :: shapeless.HNil = 4 :: evif :: HNil
Or this:
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
This will work with any hlists of functions from a particular type applied to that particular type.
The gist gives a few alternative approaches, but zipApply
plus mapConst
feels by far the best to me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With