I tried to explain that. Goal expansion is poorly related to modules and it is generally extremely hard to apply it in all places where you would expect it. Consider e.g.,
?- maplist(prove, [Goal1, Goal2, ...]).
One solution to all this is to perform the goal expansion as a very last step in dynamic calling. SICStus does that, but it seriously slows down dynamic calling.
For the toplevel is interferes with DWIM (Do What I Mean) in the sense that we must correct goals and fix modules before doing goal expansion … unless the users wants to expand a goal not related to a predicate first. But, nothing can tell these two cases reliably apart.
Just defining predicates do not have these problems. What makes it impossible to use predicates?
Quite easy. If we have @j4n_bur53’s
println(X) :- write(X), nl.
And we somehow think that is too slow, we can simply replace a call to println(X) by
write(X), nl instead of calling the predicate. That is not much more than getting the clause and replacing the call with the clause body. In addition we can use possible known arguments to only include matching clauses and we may specialize calls, e.g., a call(X) with a known X simply becomes X. This isn’t completely trivial. We must respect the scope of cuts. We must deal with recursion, for example changing a maplist/N call into a specialized recursive call or a sequence of calls if one of the lists is given.
This way we do not have all the problems of goal expansion. We can do the transformation on user request using e.g.
:- inline println/1. by default under certain optimization levels or even lazily based on hotspot observation.