scala: memoize a function no matter how many arguments the function takes? - scala

i want to write a memoize function in scala that can be applied to any function object no matter what that function object is. i want to do so in a way that lets me use a single implementation of memoize. i'm flexible about the syntax, but ideally the memoize appears somewhere very close to the declaration of the function as opposed to after the function. i'd also like to avoid first declaring the original function and then a second declaration for the memoized version.
so some ideal syntax might be this:
def slowFunction(<some args left intentionally vague>) = memoize {
// the original implementation of slow function
}
or even this would be acceptable:
def slowFUnction = memoize { <some args left intentionally vague> => {
// the original implementation of slow function
}}
i've seen ways to do this where memoize must be redefined for each arity function, but i want to avoid this approach. the reason is that i will need to implement dozens of functions similar to memoize (i.e. other decorators) and it's too much to ask to have to copy each one for each arity function.
one way to do memoize that does require you to repeat memoize declarations (so it's no good) is at What type to use to store an in-memory mutable data table in Scala?.

You can use a type-class approach to deal with the arity issue. You will still need to deal with each function arity you want to support, but not for every arity/decorator combination:
/**
* A type class that can tuple and untuple function types.
* #param [U] an untupled function type
* #param [T] a tupled function type
*/
sealed class Tupler[U, T](val tupled: U => T,
val untupled: T => U)
object Tupler {
implicit def function0[R]: Tupler[() => R, Unit => R] =
new Tupler((f: () => R) => (_: Unit) => f(),
(f: Unit => R) => () => f(()))
implicit def function1[T, R]: Tupler[T => R, T => R] =
new Tupler(identity, identity)
implicit def function2[T1, T2, R]: Tupler[(T1, T2) => R, ((T1, T2)) => R] =
new Tupler(_.tupled, Function.untupled[T1, T2, R])
// ... more tuplers
}
You can then implement the decorator as follows:
/**
* A memoized unary function.
*
* #param f A unary function to memoize
* #param [T] the argument type
* #param [R] the return type
*/
class Memoize1[-T, +R](f: T => R) extends (T => R) {
// memoization implementation
}
object Memoize {
/**
* Memoize a function.
*
* #param f the function to memoize
*/
def memoize[T, R, F](f: F)(implicit e: Tupler[F, T => R]): F =
e.untupled(new Memoize1(e.tupled(f)))
}
Your "ideal" syntax won't work because the compiler would assume that the block passed into memoize is a 0-argument lexical closure. You can, however, use your latter syntax:
// edit: this was originally (and incorrectly) a def
lazy val slowFn = memoize { (n: Int) =>
// compute the prime decomposition of n
}
Edit:
To eliminate a lot of the boilerplate for defining new decorators, you can create a trait:
trait FunctionDecorator {
final def apply[T, R, F](f: F)(implicit e: Tupler[F, T => R]): F =
e.untupled(decorate(e.tupled(f)))
protected def decorate[T, R](f: T => R): T => R
}
This allows you to redefine the Memoize decorator as
object Memoize extends FunctionDecorator {
/**
* Memoize a function.
*
* #param f the function to memoize
*/
protected def decorate[T, R](f: T => R) = new Memoize1(f)
}
Rather than invoking a memoize method on the Memoize object, you apply the Memoize object directly:
// edit: this was originally (and incorrectly) a def
lazy val slowFn = Memoize(primeDecomposition _)
or
lazy val slowFn = Memoize { (n: Int) =>
// compute the prime decomposition of n
}

Library
Use Scalaz's scalaz.Memo
Manual
Below is a solution similar to Aaron Novstrup's answer and this blog, except with some corrections/improvements, brevity and easier for peoples copy and paste needs :)
import scala.Predef._
class Memoized[-T, +R](f: T => R) extends (T => R) {
import scala.collection.mutable
private[this] val vals = mutable.Map.empty[T, R]
def apply(x: T): R = vals.getOrElse(x, {
val y = f(x)
vals += ((x, y))
y
})
}
// TODO Use macros
// See si9n.com/treehugger/
// http://stackoverflow.com/questions/11400705/code-generation-with-scala
object Tupler {
implicit def t0t[R]: (() => R) => (Unit) => R = (f: () => R) => (_: Unit) => f()
implicit def t1t[T, R]: ((T) => R) => (T) => R = identity
implicit def t2t[T1, T2, R]: ((T1, T2) => R) => ((T1, T2)) => R = (_: (T1, T2) => R).tupled
implicit def t3t[T1, T2, T3, R]: ((T1, T2, T3) => R) => ((T1, T2, T3)) => R = (_: (T1, T2, T3) => R).tupled
implicit def t0u[R]: ((Unit) => R) => () => R = (f: Unit => R) => () => f(())
implicit def t1u[T, R]: ((T) => R) => (T) => R = identity
implicit def t2u[T1, T2, R]: (((T1, T2)) => R) => ((T1, T2) => R) = Function.untupled[T1, T2, R]
implicit def t3u[T1, T2, T3, R]: (((T1, T2, T3)) => R) => ((T1, T2, T3) => R) = Function.untupled[T1, T2, T3, R]
}
object Memoize {
final def apply[T, R, F](f: F)(implicit tupled: F => (T => R), untupled: (T => R) => F): F =
untupled(new Memoized(tupled(f)))
//I haven't yet made the implicit tupling magic for this yet
def recursive[T, R](f: (T, T => R) => R) = {
var yf: T => R = null
yf = Memoize(f(_, yf))
yf
}
}
object ExampleMemoize extends App {
val facMemoizable: (BigInt, BigInt => BigInt) => BigInt = (n: BigInt, f: BigInt => BigInt) => {
if (n == 0) 1
else n * f(n - 1)
}
val facMemoized = Memoize1.recursive(facMemoizable)
override def main(args: Array[String]) {
def myMethod(s: Int, i: Int, d: Double): Double = {
println("myMethod ran")
s + i + d
}
val myMethodMemoizedFunction: (Int, Int, Double) => Double = Memoize(myMethod _)
def myMethodMemoized(s: Int, i: Int, d: Double): Double = myMethodMemoizedFunction(s, i, d)
println("myMemoizedMethod(10, 5, 2.2) = " + myMethodMemoized(10, 5, 2.2))
println("myMemoizedMethod(10, 5, 2.2) = " + myMethodMemoized(10, 5, 2.2))
println("myMemoizedMethod(5, 5, 2.2) = " + myMethodMemoized(5, 5, 2.2))
println("myMemoizedMethod(5, 5, 2.2) = " + myMethodMemoized(5, 5, 2.2))
val myFunctionMemoized: (Int, Int, Double) => Double = Memoize((s: Int, i: Int, d: Double) => {
println("myFunction ran")
s * i + d + 3
})
println("myFunctionMemoized(10, 5, 2.2) = " + myFunctionMemoized(10, 5, 2.2))
println("myFunctionMemoized(10, 5, 2.2) = " + myFunctionMemoized(10, 5, 2.2))
println("myFunctionMemoized(7, 6, 3.2) = " + myFunctionMemoized(7, 6, 3.2))
println("myFunctionMemoized(7, 6, 3.2) = " + myFunctionMemoized(7, 6, 3.2))
}
}
When you run ExampleMemoize you will get:
myMethod ran
myMemoizedMethod(10, 5, 2.2) = 17.2
myMemoizedMethod(10, 5, 2.2) = 17.2
myMethod ran
myMemoizedMethod(5, 5, 2.2) = 12.2
myMemoizedMethod(5, 5, 2.2) = 12.2
myFunction ran
myFunctionMemoized(10, 5, 2.2) = 55.2
myFunctionMemoized(10, 5, 2.2) = 55.2
myFunction ran
myFunctionMemoized(7, 6, 3.2) = 48.2
myFunctionMemoized(7, 6, 3.2) = 48.2

I was thinking that you could do something like this and than use a DynamicProxy for the actual implementation.
def memo[T<:Product, R, F <: { def tupled: T => R }](f: F )(implicit m: Manifest[F]):F
The idea being that becuase functions lack a common super type we use a structural type to find anything that can be tupled (Function2-22, you still need to special case Function1).
I throw the Manifest in there so you can construct the DynamicProxy from the function trait that is F
Tupling should also help with the memoization as such as you simple put the tuple in a Map[T,R]

This works because K can be a tuple type so memo(x,y,z) { function of x, y, z } works:
import scala.collection.mutable
def memo[K,R](k: K)(f: => R)(implicit m: mutable.Map[K,R]) = m.getOrElseUpdate(k, f)
The implicit was the only way I could see to bring in the map cleanly:
implicit val fibMap = new mutable.HashMap[Int,Int]
def fib(x: Int): Int = memo(x) {
x match {
case 1 => 1
case 2 => 1
case n => fib(n - 2) + fib(n - 1)
}
}
It feels like it should be possible to somehow wrap up an automatic HashMap[K,R] so that you don't have to make fibMap (and re-describe the type) explicitly.

Related

compiler error message when using State monad for memoization

I have a problem to make a working version of the Euler project problem 31 with the use of State trait (inspired from scalaz)
First, I have a solution with a mutable HashMap for memoization. It works but i would like to use the State monad, to understand it and to improve my skills.
I have used it with the fibonacci example, but when i attempt to apply the same technique to my case, i have a compiler error that i don't understand.
I use this implementation for State :
trait State[S, A] {
val run: S => (S, A)
def apply(s: S): (S, A) = run(s)
def eval(s: S): A = run(s)._2
def map[B](f: A => B): State[S, B] =
State { s: S =>
val (s1, a) = run(s)
(s1, f(a))
}
def flatMap[B](f: A => State[S, B]): State[S, B] =
State { s: S =>
val (s1, a) = run(s)
f(a)(s1)
}
}
object State {
def apply[S, A](f: S => (S, A)): State[S, A] = new State[S, A] {
final val run = f
}
def init[S, A](a: A) = State { s: S => (s, a) }
def update[S, A](f: S => S): State[S, Unit] = State { s: S => (f(s), ()) }
def gets[S, A](f: S => A): State[S, A] = State { s: S => (s, f(s)) }
}
my attempt to use it is here :
val coins = List(1, 2, 5, 10, 20, 50, 100, 200)
type MemoKey = (List[Int], Int)
type MemoType = Map[MemoKey, Int]
def ways(listCoins: List[Int], amount: Int): Int = {
def ways_impl(coins: List[Int], sum: Int): State[MemoType, Int] = (coins, sum) match {
case (Nil, 0) => State.init(1)
case (Nil, _) => State.init(0)
case (c :: cs, _) =>
for {
memoed <- State.gets { m: MemoType => m.get((coins, sum)) }
res <- memoed match {
case Some(way) => State.init[MemoType, Int](way)
case None =>
(for {
i <- 0 to sum / c
r <- ways_impl(cs, sum - i * c)
_ <- State.update { m: MemoType => m + ((coins, sum) -> r) }
} yield r).sum
}
} yield res
}
ways_impl(listCoins, amount) eval (Map())
I have a compiler error at this line :
r <- ways_impl(cs, sum - i * c)
The compiler said :
type mismatch; found : State[MemoType,Int] (which expands to) State[scala.collection.immutable.Map[(List[Int], Int),Int],Int] required: scala.collection.GenTraversableOnce[?]
For information, here is my first version with mutable map :
import scala.collection.mutable._
val memo = HashMap[(List[Int], Int), Int]()
val coins = List(1, 2, 5, 10, 20, 50, 100, 200)
def memoWays(coins: List[Int], sum: Int): Int = {
memo.getOrElse((coins, sum), {
val y = ways(coins, sum)
memo += ((coins, sum) -> y)
y
})
}
// brute force method with memoization
def ways(coins: List[Int], sum: Int): Int = (coins, sum) match {
case (Nil, 0) => 1
case (Nil, _) => 0
case (c :: cs, n) =>
(for {
i <- 0 to n / c
r = memoWays(cs, n - i * c)
} yield r).sum
}
println(s"result=${Mesure(ways(coins, 200))}")
What does that error mean ? Why the compiler want a GenTraversableOnce instead of State ?
What kind of thing i don't understand on State monad ?
And, if i may, I have an optional question :
Is my way to memoize with State Monad, is a good choice, or my first implementation with mutable map is better anyway ?
The problem is that your for comprehension is attempting to flatMap two unrelated types: a Range and a State. You're going to have to refactor, although off the top of my head, it's not clear to me how you'll be able to leverage State in a simple way. I'd probably use an immutable Map for the memo, a List to represent the future iterations to be tried, and simple recursion to iterate.

Using lambda with generic type in Scala

The second argument of myFunc is a function with complex arguments:
def myFunc(list : List[String],
combine: (Map[String, ListBuffer[String]], String, String) => Unit) = {
// body of myFunc is just a stub and doesn't matter
val x = Map[String, ListBuffer[String]]()
list.foreach ((e:String) => {
val spl = e.split(" ")
combine(x, spl(0), spl(1))
})
x
}
I need to pass second argument to myFunc, so it can be used with various types A, B instead of specific String, ListBuffer[String].
def myFunc(list : List[A], combine: (Map[A, B], A, A) => Unit) = {
val x = Map[A, B]()
list.foreach(e => {
combine(x, e)
})
}
How to declare and call such construct?
You can do the following,
def myFunc[A, B](list : List[A], combine: (Map[A, B], A, A) => Unit) = {
val x = Map[A, B]()
list.foreach (e => combine(x, e, e))
x
}
Ad use it like
myFunc[String, Int](List("1","2","3"), (obj, k, v) => obj.put(k, v.toInt) )
It seems that you are looking to generalise the container being used. Were you looking for something like this? Here we import scala.language.higherKinds so that we can take Container, a kind which takes a single type parameter as a type parameter to addPair.
import scala.language.higherKinds
def addPair[K, V, Container[_]](map: Map[K, Container[V]],
addToContainer: (Container[V], V) => Container[V],
emptyContainer: => Container[V],
pair: (K, V)): Map[K, Container[V]] = {
val (key, value) = pair
val existingValues = map.getOrElse(key, emptyContainer)
val newValues = addToContainer(existingValues, value)
map + (key -> newValues)
}

How to compose two different `State Monad`?

When I learn State Monad, I'm not sure how to compose two functions with different State return types.
State Monad definition:
case class State[S, A](runState: S => (S, A)) {
def flatMap[B](f: A => State[S, B]): State[S, B] = {
State(s => {
val (s1, a) = runState(s)
val (s2, b) = f(a).runState(s1)
(s2, b)
})
}
def map[B](f: A => B): State[S, B] = {
flatMap(a => {
State(s => (s, f(a)))
})
}
}
Two different State types:
type AppendBang[A] = State[Int, A]
type AddOne[A] = State[String, A]
Two methods with differnt State return types:
def addOne(n: Int): AddOne[Int] = State(s => (s + ".", n + 1))
def appendBang(str: String): AppendBang[String] = State(s => (s + 1, str + " !!!"))
Define a function to use the two functions above:
def myAction(n: Int) = for {
a <- addOne(n)
b <- appendBang(a.toString)
} yield (a, b)
And I hope to use it like this:
println(myAction(1))
The problem is myAction is not compilable, it reports some error like this:
Error:(14, 7) type mismatch;
found : state_monad.State[Int,(Int, String)]
required: state_monad.State[String,?]
b <- appendBang(a.toString)
^
How can I fix it? Do I have to define some Monad transformers?
Update: The question may be not clear, let me give an example
Say I want to define another function, which uses addOne and appendBang internally. Since they all need existing states, I have to pass some to it:
def myAction(n: Int)(addOneState: String, appendBangState: Int): ((String, Int), String) = {
val (addOneState2, n2) = addOne(n).runState(addOneState)
val (appendBangState2, n3) = appendBang(n2.toString).runState(appendBangState)
((addOneState2, appendBangState2), n3)
}
I have to run addOne and appendBang one by one, passing and getting the states and result manually.
Although I found it can return another State, the code is not improved much:
def myAction(n: Int): State[(String, Int), String] = State {
case (addOneState: String, appendBangState: Int) =>
val (addOneState2, n2) = addOne(n).runState(addOneState)
val (appendBangState2, n3) = appendBang(n2.toString).runState( appendBangState)
((addOneState2, appendBangState2), n3)
}
Since I'm not quite familiar with them, just wondering is there any way to improve it. The best hope is that I can use for comprehension, but not sure if that's possible
Like I mentioned in my first comment, it will be impossible to use a for comprehension to do what you want, because it can not change the type of the state (S).
Remember that a for comprehension can be translated to a combination of flatMaps, withFilter and one map. If we look at your State.flatMap, it takes a function f to change a State[S,A] into State[S, B]. We can use flatMap and map (and thus a for comprehension) to chain together operations on the same state, but we can't change the type of the state in this chain.
We could generalize your last definition of myAction to combine, compose, ... two functions using state of a different type. We can try to implement this generalized compose method directly in our State class (although this is probably so specific, it probably doesn't belong in State). If we look at State.flatMap and myAction we can see some similarities:
We first call runState on our existing State instance.
We then call runState again
In myAction we first use the result n2 to create a State[Int, String] (AppendBang[String] or State[S2, B]) using the second function (appendBang or f) on which we then call runState. But our result n2 is of type String (A) and our function appendBang needs an Int (B) so we need a function to convert A into B.
case class State[S, A](runState: S => (S, A)) {
// flatMap and map
def compose[B, S2](f: B => State[S2, B], convert: A => B) : State[(S, S2), B] =
State( ((s: S, s2: S2) => {
val (sNext, a) = runState(s)
val (s2Next, b) = f(convert(a)).runState(s2)
((sNext, s2Next), b)
}).tupled)
}
You then could define myAction as :
def myAction(i: Int) = addOne(i).compose(appendBang, _.toString)
val twoStates = myAction(1)
// State[(String, Int),String] = State(<function1>)
twoStates.runState(("", 1))
// ((String, Int), String) = ((.,2),2 !!!)
If you don't want this function in your State class you can create it as an external function :
def combineStateFunctions[S1, S2, A, B](
a: A => State[S1, A],
b: B => State[S2, B],
convert: A => B
)(input: A): State[(S1, S2), B] = State(
((s1: S1, s2: S2) => {
val (s1Next, temp) = a(input).runState(s1)
val (s2Next, result) = b(convert(temp)).runState(s2)
((s1Next, s2Next), result)
}).tupled
)
def myAction(i: Int) =
combineStateFunctions(addOne, appendBang, (_: Int).toString)(i)
Edit : Bergi's idea to create two functions to lift a State[A, X] or a State[B, X] into a State[(A, B), X].
object State {
def onFirst[A, B, X](s: State[A, X]): State[(A, B), X] = {
val runState = (a: A, b: B) => {
val (nextA, x) = s.runState(a)
((nextA, b), x)
}
State(runState.tupled)
}
def onSecond[A, B, X](s: State[B, X]): State[(A, B), X] = {
val runState = (a: A, b: B) => {
val (nextB, x) = s.runState(b)
((a, nextB), x)
}
State(runState.tupled)
}
}
This way you can use a for comprehension, since the type of the state stays the same ((A, B)).
def myAction(i: Int) = for {
x <- State.onFirst(addOne(i))
y <- State.onSecond(appendBang(x.toString))
} yield y
myAction(1).runState(("", 1))
// ((String, Int), String) = ((.,2),2 !!!)

How to compose tupled unary functions by combining their input tuples

I've been playing around with shapeless for a bit now.
But, yesterday I got stuck when trying to compose tupled functions.
What I was looking into specifically is composing two unary functions f1: T => R and f2: R => U => S into f: TU => S where T is a TupleN and TU := (t1, ... , tn, u)
import shapeless.ops.tuple._
implicit class Composable[T <: Product, R](val f1: T => R) extends AnyVal{
def compose2[U, S](f2: R => U => S)(implicit p: Prepend[T, Tuple1[U]]): (p.Out => S) = {
// how to provide the two required implicits for Last[p.Out] and Init[p.Out]?
tu => f1.andThen(f2)(tu.init)(tu.last)
}
}
val f1: ((Int, Int)) => Int = x => x._1 * x._2
val f2: ((Int, Int, Int)) => Int = f1.compose2((y: Int) => (x3: Int) => x3 + y).apply _
What I've been struggling with is providing the implicit proof for the tuple operations last and init, so the above code won't compile!
From a logical perspective it feels trivial as result of Prepend, but I couldn't figure out a way. So any idea is welcome :)
Using shapeless's facilities to abstract over arity I got somehow closer:
import shapeless.ops.function.{FnFromProduct, FnToProduct}
import shapeless.{::, HList}
implicit class Composable[F](val f: F) extends AnyVal{
// the new param U is appended upfront
def compose2[I <: HList, R, U, S](f2: R => U => S)
(implicit ftp: FnToProduct.Aux[F, I => R], ffp: FnFromProduct[U :: I => S]): ffp.Out = {
ffp(list => f2.compose(ftp(f))(list.tail)(list.head))
}
}
val f1: (Int, Int) => Int = (x1,x2) => x1 * x2
val f2: (Int, Int, Int) => Int = f1.compose2((y: Int) => (x3: Int) => x3 + y).apply _
This works, but then again I was really looking for compose2 to work on unary tupled Function1s. Also, this results in f: (U, t1, ..., tn) => S rather than f: TU => S with TU := (t1, ... , tn, u).
As Miles says, this would be more convenient with an undo for Prepend, but since the length of the second part is fixed, an approach similar to the one in my other answer isn't too bad at all:
import shapeless.ops.tuple._
implicit class Composable[T <: Product, R](val f1: T => R) extends AnyVal {
def compose2[U, S, TU](f2: R => U => S)(implicit
p: Prepend.Aux[T, Tuple1[U], TU],
i: Init.Aux[TU, T],
l: Last.Aux[TU, U]
): (p.Out => S) =
tu => f1.andThen(f2)(i(tu))(l(tu))
}
And then:
scala> val f1: ((Int, Int)) => Int = x => x._1 * x._2
f1: ((Int, Int)) => Int = <function1>
scala> val f2: ((Int, Int, Int)) => Int =
| f1.compose2((y: Int) => (x3: Int) => x3 + y).apply _
f2: ((Int, Int, Int)) => Int = <function1>
scala> f2((2, 3, 4))
res1: Int = 10
The trick is adding the output of Prepend to the type parameter list for compose2—which will generally be inferred—and then using Prepend.Aux to make sure that it's inferred appropriately. You'll often find in Shapeless that you need to refer to the output type of a type class in other type class instances in the same implicit parameter list in this way, and the Aux type members make doing so a little more convenient.

Lifting methods to function values in Scala

Does the Scala library provide any support for lifting a method of a given type to a function value?
For example, suppose I want to lift String.length. I can write
val f: String => Int = _.length
or
val f = { s: String => s.length }
However, this syntax is not always ideal (particularly in the midst of a larger expression). I think I'm looking for something that will enable expressions like
Lift[String](_.length)
Lift[Option[Int]].lift(_.filter)
and I have in mind something like this:
class Lift[T] {
def apply[R](f: T => R): T => R = f
def lift[A, R](f: (T) => (A) => R): (T, A) => R =
f(_)(_)
def lift[A1, A2, R](f: (T) => (A1, A2) => R): (T, A1, A2) => R =
f(_)(_,_)
// ... etc. ...
}
object Lift {
def apply[T] = new Lift[T]
}
Question 1: Does the standard library (or any library) provide something like this?
Question 2: If not, is it possible to write it in such a way that Option.filter can be lifted as above (rather than as Lift[Option[Int]].lift[Int => Boolean, Option[Int]](_.filter))? Without supplying the type parameters on the lift method I get the following error:
error: missing parameter type for expanded function ((x$1) => x$1.filter)
Lift[Option[Int]].lift(_.filter)
^
Update:
Apparently, the problem I'm running in to has something to do with the overloaded lift method. If I rename the overloads, I can lift Option.filter without all the extra type parameters.
What is the problem with
(_: String).length
(_: Option[Int]).filter _
?
I finally came up with a solution that I'm happy with. This version supports simple syntax and a single entry point to the API, while also providing control over the form of the lifted function (i.e. uncurried, partly curried, or fully curried).
Examples:
I'll use the following class definition in the examples below:
class Foo {
def m1: Int = 1
def m2(i: Int): Int = i
def m3(i: Int, j: Int): Int = i + j
}
The simplest form of lifting is to return the method as a partially applied function, equivalent to invoking ((_: Foo).method _):
scala> lift[Foo](_.m1) // NOTE: trailing _ not required
res0: (Foo) => Int = <function1>
scala> lift[Foo](_.m2 _) // NOTE: trailing _ required
res1: (Foo) => (Int) => Int = <function1>
scala> lift[Foo](_.m3 _)
res2: (Foo) => (Int, Int) => Int = <function1> // NOTE: the result is partly curried
By importing some implicits, one can request curried or uncurried forms:
scala> {
| import CurriedLiftables._
| lift[Foo](_.m3 _)
| }
res3: (Foo) => (Int) => (Int) => Int = <function1>
scala> {
| import UncurriedLiftables._
| lift[Foo](_.m3 _)
| }
res4: (Foo, Int, Int) => Int = <function3>
Implementation:
class Lift[T] {
def apply[R,F](f: T => R)(implicit e: (T => R) Liftable F): F = e.lift(f)
}
object lift {
def apply[T] = new Lift[T]
}
class Liftable[From, To](val lift: From => To)
class DefaultLiftables {
implicit def lift[F]: F Liftable F = new Liftable(identity)
}
object Liftable extends DefaultLiftables
class UncurriedLiftable1 extends DefaultLiftables {
implicit def lift1[T, A, R]: (T => A => R) Liftable ((T, A) => R) =
new Liftable( f => f(_)(_) )
}
class UncurriedLiftable2 extends UncurriedLiftable1 {
implicit def lift2[T, A1, A2, R]: (T => (A1, A2) => R) Liftable ((T, A1, A2) => R) =
new Liftable ( f => f(_)(_,_) )
}
// UncurriedLiftable3, UncurriedLiftable4, ...
object UncurriedLiftables extends UncurriedLiftable2
class CurriedLiftable2 extends DefaultLiftables {
implicit def lift2[T, A1, A2, R]: (T => (A1, A2) => R) Liftable (T => A1 => A2 => R) =
new Liftable( f => (x: T) => (a1: A1) => (a2: A2) => f(x)(a1, a2) )
}
// CurriedLiftable3, CurriedLiftable4, ...
object CurriedLiftables extends CurriedLiftable2
My previous solution required a separate lift method for each arity:
import Lift._
val f1 = lift0[String](_.length)
val f2 = lift1[Option[Int]](_.filter)
val f3 = lift2[Either[String, Int]](_.fold)
Implementation:
class Lift0[T] {
def apply[R](f: T => R): T => R = f
}
class Lift1[T] {
def apply[A, R](f: (T) => (A) => R): (T, A) => R =
f(_)(_)
}
class Lift2[T] {
def apply[A1, A2, R](f: (T) => (A1, A2) => R): (T, A1, A2) => R =
f(_)(_,_)
}
// ... etc. ...
object Lift {
def lift0[T] = new Lift0[T]
def lift1[T] = new Lift1[T]
def lift2[T] = new Lift2[T]
// ... etc. ...
}
Passing in filter as partially applied method seems to do the job:
scala> class Lift[T] {
| def apply[R](f: T => R): T => R = f
| }
defined class Lift
scala> object Lift {
| def apply[T] = new Lift[T]
| }
defined module Lift
scala> val ls = Lift[String](_.length)
ls: (String) => Int = <function1>
scala> val los = Lift[Option[Int]](_.filter _)
los: (Option[Int]) => ((Int) => Boolean) => Option[Int] = <function1>