Partial function application in Scala for arbitrary input arguments
Asked Answered
T

1

0

I am wondering whether the following is possible in Scala:

Given some vector x = (x_1, x_2, ..., x_n) in R^n and a function f that maps R^n to R, I would like to replicate this concept in Scala. The idea of Scala's partial function/currying should hold here (i.e. when applying a single value x_i, return a function that is defined only for a subset of its input domain). For example, when n = 2 define f(x, y) = sin(x + y), then trivially, f(2, y) = sin(2 + y).

However, the dimension (n > 0) may vary from case to case and may even be provided in input.

Partial application for n = 2 is:

def leftPartialFunction(f: (Double, Double) => Double)(x: Double): Double => Double = f(x, _)

but how can this be generalized for arbitrary n? For example, how can I apply the function in position i? Something like this I assume would not work:

def partialFunction(f: IndexedSeq[Double] => Double)(xi: Double): IndexedSeq[Double] => Double =  .... // cannot work well with indexed seq as they are not "disjoint"
Thalassography answered 23/1, 2023 at 21:58 Comment(7)
leftPartialFunction looks like currying ((Double, Double) => Double) => Double => Double => DoubleEquinox
@DmytroMitin indeed it isThalassography
Are you trying to define ((A1, ..., An) => B) => A1 => ... => An => B for arbitrary n?Equinox
"Abstracting over arity" github.com/milessabin/shapeless/wiki/…Equinox
What do you mean by "indexed seq are not "disjoint""?Equinox
To give a bit more context, I am trying to write a numerical integrator for a function of N dimensions. Taking for example the simple Trapezoidal Rule, we can apply recursively the 1D Trapezoidal Formula to obtain a "functional implementation". In this case the 1D Trapezoidal Formula takes Double => Double and yields Double, and we recursively call this function to lower the dimensions of the N dimensional Trapezoidal Rule, there for going (for example) (Double, Double, Double) => Double to (Double, Double) => Double to Double => Double ...Thalassography
However, generalizing to such procedure for arbitrary N would require a data structure such as a Seq, which seems unfeasible for this purpose. Another level of complexity is that each recursive call, the function f changes type (i.e. goes from (Double, Double, Double) => ... to (Double, Double) => ... etc... )Thalassography
E
1

Try the following implementation of partialFunction:

import shapeless.{::, HList, HNil, Nat, Succ}
import shapeless.ops.function.{FnFromProduct, FnToProduct}
import shapeless.ops.hlist.{At, Drop, Prepend, Take}

def partialFunction[N <: Nat, F, X,
                    L <: HList, Y, L1 <: HList, L2 <: HList, L3 <: HList
                   ](i: N)(f: F)(xi: X)(implicit
  fnToProduct: FnToProduct.Aux[F, L => Y],
  at: At.Aux[L, N, X],
  take: Take.Aux[L, N, L1],
  drop: Drop.Aux[L, Succ[N], L2],
  prepend: Prepend.Aux[L1, L2, L3],
  fnFromProduct: FnFromProduct[L3 => Y],
  take1: Take.Aux[L3, N, L1],
  drop1: Drop.Aux[L3, N, L2],
  prepend1: Prepend.Aux[L1, X :: L2, L],
): fnFromProduct.Out =
  fnFromProduct(l3 => fnToProduct(f)(prepend1(take1(l3), xi :: drop1(l3))))

Testing:

import shapeless.Nat._1

val f: (Int, Boolean, Double) => String = (i, b, d) => s"i=$i, b=$b, d=$d"

f(1, true, 2.0) // i=1, b=true, d=2.0

val f1 = partialFunction(_1)(f)(true)

f1: ((Int, Double) => String)

f1(1, 2.0) // i=1, b=true, d=2.0

You can also write partialFunction(Nat(1))(f)(true) instead of partialFunction(_1)(f)(true).

Equinox answered 24/1, 2023 at 6:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.