This question crossed my mind recently and I wasn't able to find an answer anywhere.
Is there a realistic case where someone would pass a function as a parameter to another function instead of just calling the outside function from within?
It's the exact same thing as with any other parameter: you expect that your function needs to work with many different things, decided by the caller, so you cannot hard-code just one thing into the body. In the case of functions, those things are actually behaviors, which is a very powerful idea, but the concept is the same.
say_hello can say hello to anyone or anything. say_hello_world can only say hello to the world. You might try writing several almost identical functions to cover all of your use cases – say_hello_arnie, say_hello_betty, say_hello_cecilia and so on – but that approach is not very scalable, and neither is it as flexible as the single function say_hello.
There are many functions in the standard library that take functions (including closures) as parameters; the most familiar ones are probably the iterator functions such as map and filter. Their entire existence is based on the concept that you pass them functions that tell them what to do.