Upon reading the Prolog chapter in Seven Languages in Seven Weeks, I wanted to attempt the Sudoku example in Logos.

I quickly found out that Logos had quite a few less helpers than Prolog. In Prolog, you just say fd_all_different on all the rows and columns, not so in Logos. Then I read fd_all_different is implemented as ≢'ing all elements. But then again, Logos does not yet support negation.

So rather than saying “a number can not be on the same row twice” I had to say “this row must contain all numbers from 1 to 9 once”. This is done in permutation-o.

The beauty of Logos and logic programming in general is that it allows you to tell the computer the data and the rules, instead of telling it what to do. You’d imagine this to be shorter as well. Furthermore, miniKanren is quite efficient, and Logos equally so.

So, how does this code compare to a regular Clojure version? To tell you the truth, not good. The plain Clojure version is both shorter and faster. By how much?

(nssudoku(:useclojure.pprint)(:require[clojure[set:ass]]))(def_nil)(defboard[_7___8__5__83_1___163__489798____4__2__1_5__9__4____313469__178___4_79__7__8___6_])(defhard[_6__5_32_; 3 added___3___9_7__6___1___6_3_4____4_7_1____5_9_8___4___1__6_3___8____29_4__5_]); can be either 7 or 9(deffrom-blocks[09181101921120312214132251423615247162581726273645283746293847303948314049324150334251344352354453546372556473566574576675586776596877606978617079627180])(defto-blocks[01227282954555634530313257585967833343560616291011363738636465121314394041666768151617424344697071181920454647727374212223484950757677242526515253787980])(defnreduceable-board[board](for[cellboard](if(number?cell)cell(set(range110)))))(defnprune[group](let[nrs(set(filternumber?group))](for[cellgroup](cond(number?cell)cell(=(countcell)1)(firstcell):else(s/differencecellnrs)))))(defntranspose[oldmapping](applyassoc(vecold)(interleavemappingold)))(defnslice[board](let[rows(mapprune(partition9board))cols(mapprune(applymaplistrows))blocks(mapprune(partition9(transpose(flattencols)to-blocks)))](transpose(flattenblocks)from-blocks)))(defnsudoku[board](loop[board(reduceable-boardboard)](let[next-board(sliceboard)](pprint(partition9next-board))(if(or(=boardnext-board)(every?number?next-board)(some#(=#{}%)next-board))next-board(recurnext-board)))))

The Clojure code is just a tad shorter, but not significantly so. With pattern matching and negation in place(more on that later), I think the difference becomes negligible.

In speed, however, the difference is huge. The Clojure version can solve a Sudoku in a matter of seconds, I had to leave the Logos version running overnight. Truth be told, for smaller fields or very, very easy boards, Logos can do it in half a second as well. This is why most examples implement mini-sudoku.

There is light at the end of the tunnel though! A recent update to Logos adds pattern matching, which allows for a more Prolog-ish way to express relations.