I was wondering if you can write a generic function that takes a curried function and then inverts the arguments, like this:
def foo(a: String)(b: Boolean)(c: Int): String
val bar = invert(foo _)
foo("baz")(false)(12) must be equalTo(bar(12)(false)("baz"))
The below is working as long as you add implicit inverters for the specific cases you want to address. But I am more interested in the more general case – that is, the case that deals with any number of curried arguments.
trait Inverter[V, W] {
def invert(v: V): W
}
implicit def function2Inverter[X, Y, Z] =
new Inverter[(X, Y) => Z, (Y, X) => Z] {
def invert(v: (X, Y) => Z) = {
def inverted(y: Y, x: X) = v(x, y)
inverted _
}
}
implicit def curried2Inverter[X, Y, Z] =
new Inverter[X => Y => Z, Y => X => Z] {
def invert(v: (X) => (Y) => Z) = {
def inverted(y: Y)(x: X) = v(x)(y)
inverted _
}
}
def invert[V, W](v: V)(implicit inverter: Inverter[V, W]): W =
inverter.invert(v)
Oh, and I would love to have a solution that works with Scala 2.9 as well.
Yes, it matters. The arguments must be given in the order the function expects them.
In other terms, currying is when a function — instead of taking all arguments at one time — takes the first one and returns a new function, which takes the second one and returns a new function, which takes the third one, etc. until all arguments are completed.
In mathematics and computer science, currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument.
TL;DR: Use this gist. Explanation of the same follows:
First, define a type class (and cases) to partially apply a function with the last parameter:
trait PopLast[A, Last, Rem] {
def pop(f: A, v: Last): Rem
}
trait LowPrioPopLast {
implicit def popEnd[A,B] = new PopLast[A => B, A, B] {
def pop(f: A => B, v: A) = f(v)
}
}
object PopLast extends LowPrioPopLast {
implicit def popOne[A, B, C, Last, IRem](
implicit iPop: PopLast[B => C, Last, IRem]) =
new PopLast[A => B => C, Last, A => IRem] {
def pop(f: A => B => C, v: Last) = { a: A => iPop.pop(f(a), v) }
}
}
Then, create the inverter type class: recursively do partial application of last parameter and invert the result.
trait Inverter[A] {
type Out
def invert(f: A): Out
}
trait LowPrioInverter {
implicit def invertEnd[A,B] = new Inverter[A => B] {
type Out = A => B
def invert(f: A => B) = f
}
}
object Inverter extends LowPrioInverter {
implicit def invertStep[A, Last, Rem](implicit pop: PopLast[A, Last, Rem],
inv: Inverter[Rem]) = new Inverter[A] {
type Out = Last => inv.Out
def invert(f: A) = { a: Last => inv.invert(pop.pop(f, a)) }
}
}
Finally, encapsulate into a function:
def invert[A](f: A)(implicit inv: Inverter[A]) = inv.invert(f)
And there we go:
def foo(a: String)(b: Boolean)(c: Int): String = "bar"
val bar = invert(foo _)
// bar: Int => Boolean => String => String
bar(1)(true)("foo")
1) Extract type information from your function to HList
(from shapeless) Ts
and type argument Res
.
For Int => String => Long => String
Ts
is Int :: String :: Long :: HNil
and Res
is String
:
import shapeless.{HList, HNil, ::}
trait TypeExtractorAux[A, R, Ts <: HList, Res]
trait LowPriorityTypeExtractorAux{
implicit def apply1[A, R]: TypeExtractorAux[A, R, A :: HNil, R] = new TypeExtractorAux[A, R, A :: HNil, R]{}
}
object TypeExtractorAux extends LowPriorityTypeExtractorAux{
implicit def applyN[A1, A2, R, TsNext <: HList, Res](implicit te: TypeExtractorAux[A2, R, TsNext, Res]): TypeExtractorAux[A1, A2 => R, A1 :: TsNext, Res] =
new TypeExtractorAux[A1, A2 => R, A1 :: TsNext, Res]{}
}
2) Convert curried function to Ts => Res
:
trait FunctionConverterAux[Ts <: HList, Res, A, R]{
def apply(f: A => R): Ts => Res
}
object FunctionConverterAux{
implicit def apply1[L, Res]: FunctionConverterAux[L :: HNil, Res, L, Res] =
new FunctionConverterAux[L :: HNil, Res, L, Res]{
def apply(f: L => Res): L :: HNil => Res = hl => f(hl.head)
}
implicit def applyN[L1, L2, Rt <: HList, Res, R](implicit fc: FunctionConverterAux[L2 :: Rt, Res, L2, R]): FunctionConverterAux[L1 :: L2 :: Rt, Res, L1, L2 => R] =
new FunctionConverterAux[L1 :: L2 :: Rt, Res, L1, L2 => R]{
def apply(f: L1 => L2 => R): L1 :: L2 :: Rt => Res = hl => fc(f(hl.head))(hl.tail)
}
}
For Int => String => Long => String
result is Int :: String :: Long :: HNil => String
3) Invert Ts
using shapeless.HList.ReverseAux
and collect all arguments to HList
. You'll get Ts
. Then apply your Ts => Res
function to HList
of arguments:
trait ArgumentsGetterAux[Prev <: HList, Rest <: HList, NR, Ts, Res]{
def apply(p: Prev, f: Ts => Res): NR
}
object ArgumentsGetterAux{
implicit def applyHNil[Prev <: HList, L, Res]: ArgumentsGetterAux[Prev, L :: HNil, L => Res, L :: Prev, Res] =
new ArgumentsGetterAux[Prev, L :: HNil, L => Res, L :: Prev, Res]{
def apply(p: Prev, f: L :: Prev => Res): L => Res = l => f(l :: p)
}
implicit def applyHList[Prev <: HList, L1, R <: HList, NR, Ts, Res](implicit aga: ArgumentsGetterAux[L1 :: Prev, R, NR, Ts, Res]): ArgumentsGetterAux[Prev, L1 :: R, L1 => NR, Ts, Res] =
new ArgumentsGetterAux[Prev, L1 :: R, L1 => NR, Ts, Res]{
def apply(p: Prev, f: Ts => Res): L1 => NR = l => aga(l :: p, f)
}
}
invert
method:
import shapeless.HList.ReverseAux
def invert[A, R, Ts <: HList, Res, RevTs <: HList, NR](f: A => R)(
implicit te: TypeExtractorAux[A, R, Ts, Res],
r: ReverseAux[Ts, RevTs],
ag: ArgumentsGetterAux[HNil, RevTs, NR, Ts, Res],
fc: FunctionConverterAux[Ts, Res, A, R]
): NR = ag(HNil, fc(f))
Usage:
scala> invert((i: Int) => (s: String) => (l: Long) => s"i: $i; s: $s; l: $l")
res0: Long => (String => (Int => String)) = <function1>
scala> res0(666L)("str")(1)
res1: String = i: 1; s: str; l: 666
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