I'm curious how to optimize this code :
fun n = (sum l, f $ f0 l, g $ g0 l)
where l = map h [1..n]
Assuming that f
, f0
, g
, g0
, and h
are all costly, but the creation and storage of l
is extremely expensive.
As written, l
is stored until the returned tuple is fully evaluated or garbage collected. Instead, length l
, f0 l
, and g0 l
should all be executed whenever any one of them is executed, but f
and g
should be delayed.
It appears this behavior could be fixed by writing :
fun n = a `seq` b `seq` c `seq` (a, f b, g c)
where
l = map h [1..n]
a = sum l
b = inline f0 $ l
c = inline g0 $ l
Or the very similar :
fun n = (a,b,c) `deepSeq` (a, f b, g c)
where ...
We could perhaps specify a bunch of internal types to achieve the same effects as well, which looks painful. Are there any other options?
Also, I'm obviously hoping with my inline
s that the compiler fuses sum
, f0
, and g0
into a single loop that constructs and consumes l
term by term. I could make this explicit through manual inlining, but that'd suck. Are there ways to explicitly prevent the list l
from ever being created and/or compel inlining? Pragmas that produce warnings or errors if inlining or fusion fail during compilation perhaps?
As an aside, I'm curious about why seq
, inline
, lazy
, etc. are all defined to by let x = x in x
in the Prelude. Is this simply to give them a definition for the compiler to override?