It is.  You never request the actual calculation to be done on each point, you only request that the last value be calculated.  It takes 1.8 seconds to get to the end of the 4-variable cartesian product.<p>This was my theory, and I tested it by importing Debug.Trace, and then changing the line in question to:<p><pre><code>    let result = [traceShow [j,k,l,m] (distance j k l m) | j &#60;- rangeLat, k &#60;- rangeLng, l &#60;- rangeLat, m &#60;- rangeLng]
</code></pre>
This will print "show [j,k,l,m]" when the actual distance is required.  When I run the program, it prints what you'd expect:<p><pre><code>    [[-90.0,-180.0,-90.0,-180.0]
    0.0,[-90.0,-180.0,-90.0,-177.5]
    2.2737639212328448e-32,[-90.0,-180.0,-90.0,-175.0]
    9.095050533892612e-32,[-90.0,-180.0,-90.0,-172.5]
    2.046381347867479e-31]
    [90.0,180.0,90.0,180.0]
    0.0
</code></pre>
The stuff in brackets is the traceShow output, and it's mixed in with the printing of each result.  Lazy evaluation is; it calculates only what is necessary.  In this case, it's every (j,k,l,m) quad (so it knows which one is last), and then the 5 values you request.<p>FWIW, your program runs in 14.28 seconds on my (old) machine, and the strict version runs in 48.94.  (This includes a foldl' over the list that ensures every value is evaluated.  It may add a bit of overhead.)