So I understand that call-by-need is simply a memoized version of call-by-name. In Martin Odersky's FP Course on Coursera, in lecture 7.3 (Lazy Evaluation), he mentions that if Streams were implemented using call-by-name, then it could potentially lead to a blowup in computational complexity.
What would be an example of such a blowup?
Call-by-name:
def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
def head = hd
def tail = tl
...
}
Call-by-need:
def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
def head = hd
lazy val tail = tl
...
}
Technically, lazy evaluation means call-by-name plus Sharing. A kind of opposite is eager evaluation. Lazy evaluation is part of operational semantics, i.e. how a Haskell program is evaluated. The counterpart in denotational semantics, i.e. what a Haskell program computes, is called Non-strict semantics.
Lazy Evaluation in Scala Haskell is a functional programming language that uses lazy evaluation by default.
For example the Fibonacci series, implemented by adding the previous two elements to form the successor. Without memoization, that would have a linear slowdown (and stack growth) in the length of the sequence:
scala> lazy val fib: Stream[Int] = Stream.cons(0,
| Stream.cons(1, fib.zip(fib.tail).map(p => p._1 + p._2)))
fib: Stream[Int] = Stream(0, ?)
Example lazily (-sic-) copied from this blog
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