For my natural language execution engine I created a logical programming language to define the meaning of natural language phrases, and to specify the details of the execution. The language was a simplified version of Prolog, but I added expressions, conditionals, loops and mutable variables later, to make it easier to use. It got easier to use, but it also became more unpredictable to the application programmer (which is just me for the moment).
The problem was that the logical constructs could create many bindings and one would just not expect that from an imperative language. A simple for loop would add multiple variables to the application and each goal of the body would create not just one set of bindings, but one for every iteration of the loop. The number of bindings could easily become unwieldy and extra care had to be taken to suppress them. This could be done by adding an extra set of mutable variables. It was possible, but very uncomfortable.
After rethinking the language I decided to split the logical part and the imperative part and gave them each a separate language construct.
Logical
male(peter).male(john).female(mary).parent(peter, john).parent(mary, john).father(X, Y) :- male(X), parent(X, Y).mother(X, Y) :- female(X), parent(X, Y).
too_old(A) :- [ birth(A, Birth) A := age(Birth) A > 40 ]
Imperative
function hypothenuse(Width, Height) {WidthSquared = Width * WidthHeightSquared = Height * HeightHypo = sqrt(WidthSquared + HeightSquared)return Hypo}
hypothenuse(Width, Height) => Hypo {WidthSquared := Width * WidthHeightSquared := Height * HeightHypo := go:sqrt(WidthSquared + HeightSquared)}
Combining logical and imperative
for [size(E, S) E > 5] {/* statements */}