Declarative Arithmetic

Ovid on 2008-02-28T17:40:11

Three years ago (my god, has it been that long already?), Richard Freytag approached me about writing a proper parser for AI::Prolog. I was really excited about this project at the time. I had, regrettably, implemented an ad-hoc parser for the language. Prolog's simplicity made this a simple task, but regrettably, it wasn't easy to implement math. Eventually, I hit on the idea of writing a math pre-processor which could do thing like convert this math statement:

Answer is 9 / (3 + (4+7) % ModValue) + 2 / (3+7).

Into this Prolog equivalent:

is(Answer, plus(div(9, mod(plus(3, plus(4, 7)), ModValue)), div(2, plus(3, 7)))).

However, I also wanted to add definite clause grammars to my Prolog and while I could add another pre-processor, the work seemed daunting and I never got around to it. When Freytag offered to write the parser, I was ecstatic as that meant I could easily extend the grammar.

It didn't work. Well, it did work and it worked very well, but the Prolog ran backwards. Freytag's work was brilliant, but I wasn't smart enough to fix it. In pure declarative programming, running backwards would be fine (just try that with Perl, eh?), but Prolog isn't pure. It has cuts (!), asserts (assert/1), retracts (retract/1), and many other "extra logical predicates" which alter the behavior in such a way that a the order in which a program runs is extremely important. Math, regrettably, is also extra logical. It runs one direction only.

Consider this, for example:

  convert(Celsius, Fahrenheit) :-
       Celsius is (Fahrenheit - 32) * 5 / 9.

If you issue the query convert(Celcius, 32)., you get the result "0", as you would expect. Unfortunately, convert( 0, Fahrenheit ) gives you an "unbound variable" error.

Today on Lambda The Ultimate, I saw a link to pure Prolog declarative math (pdf). No cuts. No introspection. No asserts or retracts. Just pure logic programming. This means that the following failures in "out of the box" Prolog won't be failures:

?- plus(A,B,C).
ERROR: plus/3: Arguments are not sufficiently instantiated
?- plus(A,B,5).
ERROR: plus/3: Arguments are not sufficiently instantiated
?- plus(5,A,B).
ERROR: plus/3: Arguments are not sufficiently instantiated

Despite some people's belief that Perl maps to the human brain better than Prolog (a not uncommon belief, I might add), when handling relational data, Prolog trounces Perl. (Yes, yes, Perl often trounces Prolog, too. There is no "best" language).