This replacement is clearly not sound, unless somehow you can guarantee
that (f a) and (g b) will always have the same result.
So a pure functional language with referential transperancy will never
be able to do that, and for good reasons.
However, one can replace the call (f a) with the *result* of this
call, so the next time it is invoked it won't have to do the
computation again. That is actually done in Haskell, for example, by
the compiler without any need for programmer intervention.
- Dirk