> Maybe I'm just spooking myself out, but the identity of facts and queries feels reminiscent of Lisp's homoiconicity between code and data.
Wait until you discover Prolog's homoiconicity ;-)
You can treat Prolog terms as data and also add them to the database as code and call the resulting goals like all others:
?- MyFact = f(x), assert(MyFact).
MyFact = f(x).
?- f(A).
A = x.
?- MyRule = (f(g(X)) :- f(X)), assert(MyRule).
MyRule = (f(g(X)):-f(X)).
?- f(A).
A = x ;
A = g(x) ;
A = g(g(x)) .
That is, asserting the "data" "f(x)" and "f(g(X)) :- f(X)" behaves the same as putting the identical-looking "code" in a source file.
You can also query the code and get back it back as identical-looking "data":
?- Head = f(_), clause(Head, Body).
Head = f(x),
Body = true ;
Head = f(g(_G1014)),
Body = f(_G1014).
(The first of these represents the fact "f(X)." as "f(X) :- true." to make things more uniform internally.)
As for what this is good for, you can use it to write your own metainterpreters that execute Prolog code in the normal way, or in some nonstandard or enhanced way, recording tracing data for example. There is a good writeup on metainterpreters at https://www.metalevel.at/acomip/