Bjorn Regnell
unread,Jan 26, 2013, 9:56:02 AM1/26/13Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to scala...@googlegroups.com
/*
Hi,
I have recently given a graduate course in Scala at Lund University and the following idea about a possible generalization of "lazy" not only for "val" but also for "def" came up in our course discussion. The module lazyDef below shows one interpretation of what we could mean with a "lazy def".
The idea is that IF you know that your def have no side effects and is a "pure" function in the sense that for every argument there is exactly one result, then you could mark it lazy and the result is only evaluated once then cached for next access.
This memoization semantics is not exactly lazy in the Haskell sense, but at least it is "lazy" in the sense that it only cares to do the work of calculating a result once and it could perhaps be seen as a generalization of what a lazy val is, as the case with a lazy def with no parameters is similar to a lazy val according to the proposed semantics below.
The proposal means that
lazy def fib(n: Int): BigInt = if (n <= 2) 1 else fib(n-1) + fib(n-2)
would expand to:
import lazyDef._ // module lazyDef defined below
val fib: Int => BigInt = memoize {
(n: Int) => if (n <= 2) 1 else fib(n-1) + fib(n-2)
}
which runs much faster for a large n than this (try e.g. with n = 42):
def fibSlow(n: Int): BigInt = if (n <= 2) 1 else fibSlow(n-1) + fibSlow(n-2)
***Do you think this would be a feasible/sensible/useful generalization of lazy to def in Scala?
*/
object lazyDef { //proposed memoization sematics of "lazy def"
class CachedFunction0[+R](f: () => R) extends Function0[R] {
private[this] lazy val result = f()
def apply(): R = result
}
class CachedFunction1[-T1,+R](f: T1 => R) extends Function1[T1,R] {
private[this] var cache = collection.mutable.Map.empty[T1,R]
def apply(p1: T1): R = cache.getOrElseUpdate(p1, f(p1))
}
class CachedFunction2[-T1,-T2,+R](f: (T1,T2) => R) extends Function2[T1,T2,R] {
private[this] var cache = collection.mutable.Map.empty[(T1, T2),R]
def apply(p1: T1, p2: T2) : R = cache.getOrElseUpdate((p1, p2), f(p1, p2))
}
class CachedFunction3[-T1,-T2,-T3,+R](f: (T1,T2,T3) => R) extends Function3[T1,T2,T3,R] {
private[this] var cache = collection.mutable.Map.empty[(T1,T2,T3),R]
def apply(p1: T1, p2: T2, p3: T3) : R = cache.getOrElseUpdate((p1, p2, p3), f(p1, p2, p3))
}
//... etc.
def memoize[R](f: () => R): CachedFunction0[R] =
new CachedFunction0[R](f)
def memoize[T1,R](f: T1 => R): CachedFunction1[T1,R] =
new CachedFunction1[T1,R](f)
def memoize[T1,T2,R](f: (T1,T2) => R): CachedFunction2[T1,T2,R] =
new CachedFunction2[T1,T2,R](f)
def memoize[T1,T2,T3,R](f: (T1,T2,T3) => R): CachedFunction3[T1,T2,T3,R] =
new CachedFunction3[T1,T2,T3,R](f)
//... etc.
}
import lazyDef._
val fib: Int => BigInt = memoize {
(n: Int) => if (n <= 2) 1 else fib(n-1) + fib(n-2)
}
def fibSlow(n: Int): BigInt =
if (n <= 2) 1 else fibSlow(n-1) + fibSlow(n-2)
// scala> fibSlow(42)
// scala> fib(42)
/*
lazy def const() = 42 //expands to: val const = memoize { () => 42 } //cf. lazy val
lazy def twice(x: Int) = 2 * x //expands to: val twice = memoize { (x: Int) => 2*x }
lazy def add(x: Int, y: Int) = x + y //expands to: val add = memoize { (x: Int, y: Int) => x + y }
*/