Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I apply an HList of arbitrary functions to an arbitrary value?

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
  1. How can I fix this to work with any String input?
  2. Can I change genericise this even further to work with any input type T?
like image 1000
William Carter Avatar asked Jun 20 '19 15:06

William Carter


People also ask

How do you pass arbitrary number of arguments to a function?

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.

Can you use a list as a parameter?

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.

What is arbitrary list?

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.

How do we write arbitrary arguments while defining a function?

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.


1 Answers

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.

like image 182
Travis Brown Avatar answered Sep 22 '22 08:09

Travis Brown