Some background: miniKanren is a logic programming language in the neighborhood of Prolog (they are similar in the way that Python and Javascript are similar). The language is described and implemented in the book The Reasoned Schemer, which is a sequel to the classic The Little Schemer. The major implementations of miniKanren are in Clojure and Racket.
I was reading TRS and wanted to run the code, but I didn't have (and didn't feel like getting) Clojure or Racket. At the same time, I wanted to try my hand at some serious Elisp programming. The obvious answer was to put miniKanren into Emacs, and that's what this is.
Fun! I highly recommend watching Dan Friedman and William Byrd give their miniKanren conference talk (they've done it a number of times slightly differently) e.g. this one at the "Conj" Clojure convention: https://www.youtube.com/watch?v=5Q9x16uIsKA
It's really a fun presentation and does a great job introducing logic programming and miniKanren to lay programmers.
Snarky aside: you can also install a real Prolog (e.g. SWI Prolog) and run that in an Emacs session, and will be more performant and expressive than anything you would build on top of Emacs Lisp
To be fair I always thought MiniKanren was supposed to be more of a tutorial than a full fledged prolog. I once tried my hand at 'porting' it to Mathematica: it's been mostly an exercise in adapting the semantics rather than the syntax.
Now, I am all for logic programming, but i have rarely found a place where such a solution haven't been to much of a mental burden for a project with more contributors than only myself. I love to find problems where logic programming is a good fit, and I love the elegance of such solutions, but I can'tc expect my team to actually grok it.
I tried to integrate a C++ logic programming library (Castor) with Sqlite and a linear constraint solver to make a dynamically sizing MFC (Windows GUI) layout library. I thought it would "dog on surfboard with jet pack" awesome, but it turned out more like "taking Space Shuttle to McDonald's drive-thru." That is, it made a few hard things simple but also made a lot of easy things complicated.
I still think logic programming has untapped potential for use in "regular" programs, but we haven't invented the best way to integrate it yet. In order to take advantage of bi-directional nature of logic relations (every argument can be input or output) you really need all the consituent pieces of that computation to be relational, as well. (To do what William Byrd calls "running backwards".) But to support this in an imperative language, where some of your relation is purely logical and some of it is implemented with user-defined callbacks, requires careful defensive programming in the C++ callback functions.
Moreover the inherently lazy/on-demand nature of logic programming does not fit well with the way debuggers for imperative programs work. This is not specific to logic programming (for example, debugging a regular expression that is inside a C++ program is a similar case). What is needed is a way to tell the debugger to display the file, line, and character position where the regex or logic relation or SQL statement was defined and not the position inside the engine which is interpreting that data.
I would contrast the "fully integrated, compute on-demand" way I was attempting to use a logic programming library with the way Sqlite natively works. With Sqlite, the steps of preparing a statement, looping over the rows, and finalizing the statement are all distinct steps (as called from C/C++ code). The Sqlite query planner can calculate the entire plan from the SQL string and the tables it manages internally. When Sqlite computes each next row as you step the prepared statement object, it is free to use whatever evaluation order was chosen by the query plan, because it all happens synchronously in the "step" call and then returns.
Of course the drawbacks of this approach (the famous impedance mismatch) are well-known to anyone who has used any kind of SQL database API from a C-family language. So I am not saying it is the best we can do. However I believe this API interaction strategy is one way (surmised - I haven't tried it yet) that logic programming could be integrated with an imperative program without it devolving into an un-traceable mess: treat it like using an SQL engine. Or like compiling a regex, stepping through, and reading the captures.
I find this answer ultimately disappointing, because I feel that such a powerful technique ought to transform how you think about and structure your whole program. And instead of solving the impedance-mismatch problem, it gives up on it. But having tried things the other way (coroutining logic and imperative code), if I were to try again, I would try this other way (treating the logic program like a database or query engine).
My first real gigs were CAD/CAM based. I really thought both constraint and parametric programming were the future.
Finally gave up on constraints for UI layout. My alternative was to hand code the heuristics, esthetics. I created DesignGridLayout (for Java), based on canonical grids (a graphic design notion). It's now defunct, but if I ever circle back to UI work, that's where I'll resume.
--
That impedance mismatch has tripped me up my entire career.
I forfeited in a different way. I inverted the work flow.
I now use working examples to generate the code.
In the case of databases: instead of an ORM to generate SQL, I use SQL to generate the JDBC/ODBC wrappers.
I can't speak to implementing a query planner (way beyond my intellect), or your notion of starting with a different mental model, but it's food for thought.
For my GUI layout library I decided "snaplines" could be a more fundamental concept than grids (because a grid can be defined with horizontal and vertical snaplines). Essentially it's just a linear constraint like "there exists a variable X_snap1, such that box1.right <= X_snap1 and box2.left >= X_snap1." The key is that if you re-use the same snapline variable for subsequent rows, it aligns a column.
Another key idea was that the snaplines are completely independent from the logical (hierarchical, tree) structure of the "document" (aka form). So like if you wanted controls above and below something to be horizontally aligned with each other, but not affect content in the middle, you could do that by just only associating some controls with those snaplines; the fact that the snapline cuts across the middle content need not affect it because it's an abstract "membership" in a group not a physical barrier.
How I came to this design was I had done XAML layouts with WPF on Windows, and while I preferred to use the Grid tag to solve most layout problems, I was also frustrated with its limitations: you kind of have to plan ahead, if you discover you need another alignment line you have to renumber things, or maybe you need to fuse certain cells, etc. I realized that I was using the grid to get alignment lines, but didn't (necessarily) want all of the other behaviors which come with it.
Definitely agree about using the SQL to generate the code and not the other way around. For simple cases that's what I now do. For complex cases, I've grown to accept the idea that the translation between in-memory (in-program) representation and in-file representation is an important translation layer and actually should be hand-coded to a moderate extent.
Bahaha, 3 of the 30 front page stories are about Lisp. Yessss, rise, Rise! Let the Lisp dominance commence! Take to the streets and take macros into your hearts! Demand a formal holiday to celebrate all conses! Tear off those strongly typed shackles and write a macro system to expand your grocery lists! https://github.com/sctb/lumen
I have been seriously looking into a good APL (or deciding between A+ / J / etc.) for use in scripting. Right now I think AWK to massage the data and ${APL choice} to analyze it might be the right way to go.
APL seems to require buying another keyboard just to edit it. Maybe an IDE can help me type SD←((+/((X - AV←(T←+/X)÷⍴X)2))÷⍴X)0.5 but I'm not really sure I want it to.
AWK is highly specialized to interpret ad hoc data structures encoded in strings and spit out new similarly ad hoc strings, like all other unix utils. Personally I hope that's not the future. In fact I wish for the day our terminals and coreutils are replaced with utilities that speak JSON or something.
Dyalog and the emacs APL mode both let you use a prefix key for the special characters. By default it's either . or `. So .. gives you a period, and .r gives you rho. It's not so bad if you practice with it for a bit each day for a couple weeks. I became reasonably proficient after about a month of 30 minutes a day of APL practice earlier this year. It would be harder if you don't touch type yet (I suspect).
Some APLs do require non ASCII key usage, but you can use a regular keyboard with Shift, Alt, etc. J uses all regular ASCII characters however ; as does K and Q.
Lumen looks interesting, but where is the implementation source? Both compiler.js and compiler.lua look like they were produced by some compiler out of an original source file that I can't find in the repo itself.
One of the coolest projects. Lumen is entirely self-hosted, so compiler.js is built from compiler.l which represents an algorithm that defines how to build compiler.js.
I was reading TRS and wanted to run the code, but I didn't have (and didn't feel like getting) Clojure or Racket. At the same time, I wanted to try my hand at some serious Elisp programming. The obvious answer was to put miniKanren into Emacs, and that's what this is.