Those are nice examples, thanks.
I was imagining you might achieve this optimization by inlining the function. So if you have
getClosest(points, p) = findInTree(buildTree(points), p)
And call it like
myPoints = [...]
map (getClosest(myPoints, $)) myPoints
Then the compiler might unfold the definition of getClosest and give you
map (\p -> findInTree(buildTree(myPoints), p)) myPoints
Where it then notices the first part does not depend on p, and rewrite this to
let tree = buildTree(myPoints) in map (\p -> findInTree(tree, p)) myPoints
Again, pretty contrived example. But maybe it could work.