Consider a function `f` and a list `p={a,b,c}`. I want to get a list of

    f[a,a]
    f[a,b]
    f[a,c]
    f[b,a]
    f[b,b]
    f[b,c]
    f[c,a]
    f[c,b]
    f[c,c]
In real use, `p` can have higher dimensions, and f may take more arguments, e.g. I may  need to generate a list of `f[a,a,a,a]` through `f[z,z,z,z]`. Is there a cleaner way to do this other than making lists that approximately repeat the elements of `p` then use `MapThread`? e.g.
```
p={a,b}
p1 = {a, a, b, b}
(* or: p1 = {p, p}\[Transpose] // Flatten *)
p2 = {a, b, a, b}
(* or: p2 = {p,p}//Flatten *)
MapThread[f, {p1, p2}]
```