The nature of $ x^2-2x+3$ depends on the nature of $x$. If $x$ is a variable, then we have for instance:

```
sage: var('x');
sage: E = x^2 - 2*x + 3
sage: E
x^2 - 2*x + 3
sage: type(E)
<class 'sage.symbolic.expression.Expression'>
sage: f(x) = x^2 - 2*x + 3
sage: f
x |--> x^2 - 2*x + 3
sage: type(f)
<class 'sage.symbolic.expression.Expression'>
sage: type(f(x))
<class 'sage.symbolic.expression.Expression'>
sage: f(f(x))
(x^2 - 2*x + 3)^2 - 2*x^2 + 4*x - 3
sage: type( f(f(x)) )
<class 'sage.symbolic.expression.Expression'>
```

However, if all computations involve only polynomial (or up so some complexity rational) expressions, then it may be useful to work inside a polynomial ring (or respectively tacitly inside the related fraction field).
Here, we define $x$ to be the transcendent generator of the polynomial ring over the needed field (of coefficients). I will work in the samples below over $\Bbb Q$. In this case:

```
R.<x> = PolynomialRing(QQ)
f(x) = x^2 - 2*x + 3
type(f)
f(f(x))
type( f(f(x)) )
```

delivers:

```
<class 'sage.symbolic.expression.Expression'>
(x^2 - 2*x + 3)^2 - 2*x^2 + 4*x - 3
<class 'sage.symbolic.expression.Expression'>
```

But we can "do better", work with $f$ as a polynomial. So replace `f(x)`

which is in sage "something else" by `f`

, so that `f`

becomes a polynomial.

```
R.<x> = PolynomialRing(QQ)
f = x^2 - 2*x + 3
type(f)
f(f(x))
type( f(f(x)) )
```

This gives:

```
<class 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint'>
x^4 - 4*x^3 + 8*x^2 - 8*x + 6
<class 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint'>
```

Here, sage implements the "universal property of a polynomial", namely the fact that we can plug in inside $f=f(\cdot)=f(x)$. So we can plug in something like $x^2$ instead of $x$, written as `f( x^2 )`

. Also, we can plug in the polynomial `f(x)`

itself, so we write `f( f(x) )`

as above, and we get as a result also a polynomial, an element of the polynomial ring $R=\Bbb Q[x]$. And in this case there is no need for a simplification. For instance:

```
R.<x> = PolynomialRing(QQ)
f = x^2 - 2*x + 3
g = x + 4
f(g(f(g(f(g(f(g(x))))))))
```

This is:

```
x^16 + 48*x^15 + 1120*x^14 + 16800*x^13 + 180900*x^12 + 1480464*x^11
+ 9516352*x^10 + 48983520*x^9 + 204005360*x^8 + 689831040*x^7
+ 1888420352*x^6 + 4144340736*x^5 + 7156320280*x^4 + 9415022880*x^3
+ 8921080320*x^2 + 5457719232*x + 1632644838
```

(Result was manually adjusted.)

Alternatively, we can build some recursion, to see also the in between results:

```
def compose(polynomial_list):
h = x
for pol in polynomial_list[::-1]:
h = pol(h)
print(h)
```

And this function delivers:

```
sage: compose( [f, f, g, g, f] )
x^2 - 2*x + 3
x^2 - 2*x + 7
x^2 - 2*x + 11
x^4 - 4*x^3 + 24*x^2 - 40*x + 102
x^8 - 8*x^7 + 64*x^6 - 272*x^5 + 1098*x^4 - 2728*x^3 + 6448*x^2 - 8080*x + 10203
```

Doing the same with bare hands:

```
sage: f(x)
x^2 - 2*x + 3
sage: g(f(x))
x^2 - 2*x + 7
sage: g(g(f(x)))
x^2 - 2*x + 11
sage: f(g(g(f(x))))
x^4 - 4*x^3 + 24*x^2 - 40*x + 102
sage: f(f(g(g(f(x)))))
x^8 - 8*x^7 + 64*x^6 - 272*x^5 + 1098*x^4 - 2728*x^3 + 6448*x^2 - 8080*x + 10203
```

Even if the expressions are not polynomial / rational, inserting a simplification in (a rewritten version of) the above code should be easy and lead to a good solution.