Welcome to the CIS Seminar Laziness is good for you!
Mar 13, 2016
Welcome to the CIS Seminar
Laziness is good for you!
Scheduling
Nov 17: StephenDec 1: DarekDec 8: Tim
A Tale of Two Students
Prof: Read Chapter 4Darek:
Ha! Chapter 4 is a waste of time. I'll wait until I see the test and read it real quick during the test if I have to.
Daniel:Better read that right now! You never know what will
be on the test.
A Tale of Two Students
Prof: Read Chapter 4Darek:
Ha! Chapter 4 is a waste of time. I'll wait until I see the test and read it real quick during the test if I have to.
Daniel:Better read that right now! You never know what will
be on the test.
Prof: Test Time!
Daniel:Drat – he never asked a
question from chapter 4!
Darek:Score! Sure glad I got all
that extra sleep!
What's Going On Here?
Suppose that there are no "real time" constraints – the test isn't time limited.
Darek doesn't bother to do work that's not part of his final grade – he's lazy
Daniel does work that might not have an impact on his grade – he's eager.
These guys are representative of two basic programming language styles.
We know which one is "right"!
Back to Lambda Calculus
If you remember Lambda Calculus (hah!) it was a way of expressing computation that didn't commit you to any specific sequence of operations.
You had a big cloud of "work" to do and you could do any piece of work (reduction) you wanted to. No matter which strategy you choose you eventually get the same answer.
Lazy Lambda Calculus
Here's the big idea: you take your program and don't run (reduce) anything unless it's part of the "answer". That way you know all of your effort "counts".
This sounds sensible! So what's the alternative?
Other Programming Languages
Most programming languages do work at times they don't have to:
x = f(y, z) // Will we really NEED x later?
a = f(g(b), h(b)) // Does f really use both?
Lazy Languages
In a lazy language, we don't do any work that isn't part of the "answer". What does that mean?
x = f(y)doesn't DO anything unless we really want
to know the value of x.
Every Language is a Little Lazy
Even in regular old languages like Java, there are constructs that do behave lazily:
if (y > 10) z = f(x) else z = g(x);
We don't go out and evaluate both branches of the if – we wait until we know which is needed.
Note that && and || are lazy too!
A Lazy Functioniterate :: (a -> a) -> a -> [a]iterate f x = x : iterate f (f x)
What the heck? This defines an infinite sequence of value:
[x, f(x), f(f(x)), f(f(f(x))), …]Now in languages like Java this is an infinite loop!
But not in a lazy language …You have to be careful not to print the entire result:take 10 (iterate (*2) 1)
Lazy Example
addPairs :: [Integer] -> [Integer]addPairs (x:y:rest) = x+y : addPairs (y:rest)addPairs _ = [ ]
Check this: addPairs [0,1,2,1,0] = [1,3,3,1]
Pascal's Triangle
pascal :: [[Integer]]pascal = iterate f [1] where f x = addPairs ([0] ++ x ++ [0])
combinations n m = pascal !! (n-1) !! (m-1)
Let's fire up Haskell and see this in action!
Generalizing
Pascal's triangle can be explained in terms of "convolution". This is a big deal in the signal processing world in which systems are:
* linear (if you double the input, you double the output) * time-invariant (they do the same thing today and
tomorrow) * We'll also assume causality: output can't precede inputIf you know the response to a single 1 (the impulse
response) you know the response to any input.This is discrete convolution: signals are an infinite stream of
discrete samples – integration is now summation
Discrete Convolutionconvolve, addSeries :: [Integer] -> [Integer] -> [Integer]convolve [] x = []convolve (x:xs) y = addSeries (map (*x) y) (0 : convolve xs y)
addSeries [] x = xaddSeries x [] = xaddSeries (x:xs) (y:ys) = x+y : addSeries xs ys
A General Pascal Trianglepascal2 = iterate (convolve [1,1]) [1]
But wait! We can do more!
dice = iterate (convolve [0,1,1,1,1,1,1]) [1]
test6 = dice !! 3
Convolution
You CS guys can't hide from convolution: it's the basis for TONS of signal processing software (speech recognition, filtering, sound and image synthesis)
Know Math to know CS!
Beyond the Infinite …
The program we just did isoverly convoluted becausethe streams are finite. Let'sdeal with infinite streams instead.
Infinite Streams / ConvolutionconvolveI , addSeriesI :: [Integer] -> [Integer] -> [Integer]convolveI (x:xs) y = addSeries (map (*x) y) (0 : convolveI xs y)addSeriesI (x:xs) (y:ys) = x+y : addSeriesI xs ys(or using magic Haskell juju:)addSeriesI = zipWith (+)
Contemplating InfinityInfinity is great but we can't see it all at once.Let's see how Pascal is doing:
zeros = 0 : zerosimpulse = 1 : zerospascalSignal = 1 : 1 : zerosinfinitePascal = iterate (convolveI pascalSignal) impulsetest7 = take 6 (map (take 6) infinitePascal)
Back to Programming Langs
Haskell is a language without control constructs:
* for loop: NO * while loop: NO * break: NO * if / then / else: YES (why?)
Why Not?
We don't need control constructs when we have lazy evaluation.
Things that require control constructs can be accomplished using ordinary data operations.
ComparisonJava: for (i = 0; i < 10; i++) if (prime(i)) System.out.print(i);Haskell:filter isPrime (takeWhile (< 10) (iterate (+ 1) 0))
isPrime n = head (dropWhile (<n) primes) == nprimes = sieve [2..] where sieve (p:ns) = p : sieve [n | n <- ns, n `mod` p > 0]
What is "="?
In Java, it is an "action": it makes something happen.
In Haskell, it is a "definition" – it doesn't do anything except allow a computation to look up the value of a name if needed.
Haskell = mathematicsJava = confusion
Why Laziness?
Laziness is NOT about avoiding work (sorry, Darek!)
Laziness is about defining things in a natural way without having to worry about the underlying computational process.
Lazy programs are far more "plastic" that others: we can abstract at ANY point.
Conclusion