Scala function transformation

closures, functional-programming, scala

Solution

This can be done in fairly straightforwardly using shapeless's facilities for abstracting over function arity,

import shapeless._
import HList._
import Functions._

def wrap_fun[F, T <: HList, R](f : F)
  (implicit
    hl :   FnHListerAux[F, (Int :: T) => R],
    unhl : FnUnHListerAux[(Int :: T) => R, F]) =
      ((x : Int :: T) => f.hlisted(x.head*2 :: x.tail)).unhlisted

val f1 = wrap_fun(fun _)
val f2 = wrap_fun(fun1 _)
val f3 = wrap_fun(fun2 _)

Sample REPL session,

scala> f1(2)
res0: Int = 4

scala> f2(2, 4)
res1: Int = 4

scala> f3(2, Map(), Seq())
res2: Int = 4

Note that you can't apply the wrapped function immediately (as in the question) rather than via an assigned val (as I've done above) because the explicit argument list of the wrapped function will be confused with the implicit argument list of `wrap_fun`. The closest we can get to the form in the question is to explicitly name the `apply` method as below,

scala> wrap_fun(fun _).apply(2)
res3: Int = 4

scala> wrap_fun(fun1 _).apply(2, 4)
res4: Int = 4

scala> wrap_fun(fun2 _).apply(2, Map(), Seq())
res5: Int = 4

Here the explicit mention of `apply` syntactically marks off the first application (of `wrap_fun` along with its implicit argument list) from the second application (of the transformed function with its explicit argument list).

Problem

Say I've got a function taking one argument ``` def fun(x: Int) = x ``` Based on that, I want to generate a new function with the same calling convention, but that'll apply some transformation to its arguments before delegating to the original function. For that, I could ``` def wrap_fun(f: (Int) => Int) = (x: Int) => f(x * 2) wrap_fun(fun)(2) // 4 ``` How might one go about doing the same thing, except to functions of any arity that only have the part of the arguments to apply the transformation to in common? ``` def fun1(x: Int, y: Int) = x def fun2(x: Int, foo: Map[Int,Str], bar: Seq[Seq[Int]]) = x wrap_fun(fun1)(2, 4) // 4 wrap_fun(fun2)(2, Map(), Seq()) // 4 ``` How would a `wrap_fun` definition making the above invocations work look like?

Original source