And of course, Haskell can be faster than all of these.
I coded it in haskell, and it is faster, if it's a correct translation. Without parallelizing, it clocks at 1.8 seconds, compiled with $ ghc --make. If anyone plans to compile this you'll need to $ cabal install geodetic, before $ ghc --make filename.hs
1.8 Seconds seems a little too fast though...
This was my theory, and I tested it by importing Debug.Trace, and then changing the line in question to:
let result = [traceShow [j,k,l,m] (distance j k l m) | j <- rangeLat, k <- rangeLng, l <- rangeLat, m <- rangeLng]
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: [[-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
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.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.)