I love LATERALs, but this still fits within set theory and a bulk application rather than an iterative for-loop. It may even be implemented as a for-loop within the engine, but SQL being declarative abstracts that away from the query interface.
It's sets all the way down. A set of f(x) is still a set.
CREATE TEMP TABLE temp_results(value int, value_squared int);
DO $$
DECLARE
r int;
BEGIN
FOR r IN SELECT generate_series FROM generate_series(1,5)
LOOP
INSERT INTO temp_results VALUES (r, r * r);
END LOOP;
END$$;
SELECT * FROM temp_results;
At the very least mention that re-running the SELECT in the same connection would include the prior results as well because they are preserved across commands within the same connection.
Ha! plpgsql's seemingly sole purpose is to inject imperative code into a set-based environment. Probably does it more smoothly than most pl languages, but that's at the cost of imperative clarity.
But you're right. Postgres does allow for-loops like this. (They're also slower than the equivalent set-oriented approach.)