Also the integral procedure defined at the top isn't a function, it's an operator. It returns functions as results, not values.
The expression of the integral operator as a function in code is contrary to that with how people usually think about functions and code.
The only language I know that properly manages to represent integrals as code is Wolfram Mathematica by using rich rewrite systems.
That is Integrate(f,a,b) is not code but a data structure to be interpreted by an external (and customizable) integration context that defines numerical types, algorithms, lazyness, etc.
From the links I know of Wolfram Mathematica and lisp this could well be what you meant, but it is quite different from giving a single integration algorithm.
Proof assistants do something similar btw, they also encode mathematical expressions as (often recursive) data types and then prove things about those definitions.
edit: to be fair though, the LISP implementation proposed to use the Risch algorithm, which actually does give you symbolic antiderivatives. So that wouldn't be a valid critique of the implementation. There more salient points are a) that the Risch algorithm only works for a certain class of functions (those that have an elementary antiderivative) and b) that by not separating the definition of an integral from its evaluation, you're not able to manipulate it directly as an expression or to evaluate it via different methods (e.g. symbolic vs. numerical methods).
Just think about inputting $complicatedIntegral - $complicatedIntegral. This is clearly zero, but if your integral is "just a function", you're not able to see that and will spend an unreasonable amount of time computing it (twice, even), or worse, will fail to produce a result.
I was mostly referring to this. Mathematica is simply my only experience with this kind of approach.