Recall that we defined goldbachPairs as follows.
goldbachPairs g = [(p, k) | k <- takeWhile ((< g) . (*2) . (^2)) [1 .. ],
p <- [g - 2 * k * k], isPrime p]
Let's define a predicate satisfiesGoldbach as follows.
satisfiesGoldbach g = not (null (goldbachPairs g))
So, for example,
> goldbachPairs 101
[(83,3),(29,6),(3,7)]
and
> satisfiesGoldbach 101
True
My question is: how many of the Goldbach pairs are computed before satisfiesGoldbach returns True?
Here's a tool you can use to help you find out.
At the start of the file add the line
import Debug.Trace -- See https://hackage.haskell.org/package/base-4.9.0.0/docs/Debug-Trace.html
Then modify goldbachPairs as follows
goldbachPairs g = [(p, k) | k <- takeWhile ((< g) . (*2) . (^2)) [1 .. ], trace ("k = " ++ show k) True,
p <- [g - 2 * k * k], isPrime p, trace ("Returning " ++ show (p, k)) True]
(The function show in Haskell is like toString in Java. It doesn't do output. It just converts its argument to a String. We must call it to do String concatenation (++).)
Run both goldbachPairs 101 and satisfiesGoldbach 101.
What's the difference in the trace output? Can you explain why. What does this have to do with Haskell being lazy?
Most importantly: how does Haskell manage to do what you see?