
LogicJS adds logic programming to JavaScript - adamnemecek
https://github.com/mcsoto/LogicJS
======
whiddershins
A quick read of the comments here gives me the impression a lot of people are
unfamiliar with what logic programming is, how it works, and what it might be
used for.

I am among them. Really appreciate the post just for inspiring me to learn
more about it. Anyone have a favorite go to resource?

~~~
romaniv
Well, the classic logic programming language is Prolog, so learning it would
be a good start. Here is something fairly lightweight on the subject:

[http://www.amzi.com/AdventureInProlog/advtop.php](http://www.amzi.com/AdventureInProlog/advtop.php)

There was a great PDF that explained how unification and substitution of
symbols worked in a concise and readable fashion, but I can't find it right
now. Hm, maybe this one:

[http://www.cs.ubbcluj.ro/~gabis/plf/Doc/SWI-
Prolog/AnIntrodu...](http://www.cs.ubbcluj.ro/~gabis/plf/Doc/SWI-
Prolog/AnIntroductionToProlog.pdf)

~~~
the_watcher
Could you describe a classic use case? I'm also interested in learning some
logic programming, but it's hard for me to conceptualize a use case that isn't
already covered by the languages I regularly use.

~~~
romaniv
Back in the day the two common use cases were expert systems and natural
language processing (which are somewhat related). In this day and age I would
most likely use Prolog for rule-based simulations or high-level game AI.

------
sriku
One of the issues with standard logic programming is that sooner or later you
want to be controlling the search strategies. Mozart/Oz has the notion of
"computational spaces" as a primitive on which to build custom strategies.
Quite a while back, I wrote tan implementation of this in JS called FD.js[1]
for finite domain constraint programming. Might be useful for those interested
in such in JS.

[1] [https://github.com/srikumarks/FD.js](https://github.com/srikumarks/FD.js)

------
jond3k
To solve the readability issue, why not use Babel to transpile JS expressions
into the equivalent syntax?

To generate the first example in the readme:

    
    
      import {solve} from 'logic'
      
      function father(x,y) {
          return x =='mcbob' && y == 'bob' ||
              x =='bob' && y == 'bill';
      }
      
      solve(father) // a noop function used as a marker by babel

~~~
AnkhMorporkian
If you're going ES6, why not just:

    
    
      import {solve} from 'logic'
      
      solve((x,y) => x === "mcbob" && y === "bob" || x === "bob" && y === "bill");

~~~
danappelxx
Just because you can use the shorthand syntax, doesn't mean you always should.

~~~
shriek
I wouldn't say it's that bad. A curly brace would make it a bit clearer
though.

~~~
elbear
I prefer my code to be more than "it's not that bad" when it comes to
readability.

------
stevebmark
How easy would it be to encode and solve "Einstein's Riddle"
[https://udel.edu/~os/riddle.html](https://udel.edu/~os/riddle.html) in
LogicJS? I looked into starting it but there's things like this:

> _the Norwegian lives next to the blue house_

Which I'm not sure how encode without a huge decision tree, or some sort of
dynamic logic encoding.

~~~
riboflava
For reference, Clojure's core.logic: [https://gist.github.com/rm-
hull/6952960](https://gist.github.com/rm-hull/6952960) Or, from Norvig,
presumably as an update to the solution he made with Lisp and Prolog, here's a
Python version that doesn't use any fancy logic engine:
[https://gist.github.com/joyrexus/5301901](https://gist.github.com/joyrexus/5301901)

~~~
StavrosK
Jesus, Norvig's version is so dauntingly short...

------
galfarragem
A bit off topic, I like his JS style [1], almost doesn't look JS..

[1]
[https://github.com/mcsoto/LogicJS/blob/master/logic.js](https://github.com/mcsoto/LogicJS/blob/master/logic.js)

~~~
dsego
Actually, this is the canonical JS with prototypes. The more common style
nowadays is to (ab)use the module pattern.

~~~
csallen
I think the parent is referring to the particular stylistic decisions that
differ from what's most common in the JS community. The author uses tabs to
indent (instead of spaces), snake case (instead of camel), no semicolons, etc.

~~~
dsego
Wow, didn't even notice that. Now that I look at it, it does seem more airy
and spacious. The style is also a bit eclectic and unsound. Dropping the curly
braces on that else on L698 irks me to no end.

~~~
galfarragem
This style could be called 'No fear JS' :)

~~~
gavinpc
Not even using a linter. See the bug?

[https://github.com/mcsoto/LogicJS/blob/master/logic.js#L32](https://github.com/mcsoto/LogicJS/blob/master/logic.js#L32)

~~~
wbercx
Ha, good get.

------
ysavir
Nifty, but having "and", "or" and the rest _before_ both of the arguments
instead of in the middle is incredibly confusing to read. Very unintuitive.
Why not restructure it to be chainable with something like:

    
    
      firstVar.or(secondVar.eq('Bob').and(thirdVar.eq(41)));

~~~
lmkg
Which style is more readable depends on whether in practice you end up with
more sequence-like structures, or more tree-like structures. Chaining makes
sense for sequences. For tree-like structures, they get confusing because the
nesting levels aren't apparent.

For example: In your code sample, it's a bit unclear what nesting level the
.and() call is occurring at. It could be a method call from the chain that
starts at secondVar, or the chain that starts at firstFar. Distinguishing
which one requires counting all the intervening parentheses. This okay for the
given example, but gets out of hand as the conditions get complicated.

Ergonomics aside, I don't think the prefix notation is unintuitive. It's a bit
verbose, since JavaScript doesn't have operator overloading, but to me it
seems the most sensible and straightforward way of structuring the
relationships.

~~~
ysavir
Good point, and I agree that the code in my example wasn't perfectly clear,
though I think some formatting could help it go a long way.

I don't think the prefix notation is much more readable than what I suggested,
if it is more readable at all... But without any hands on experience with the
library, I am in no position to make that argument.

In the spirit of excessive, needless suggestions: Maybe they can make it like
the testing libraries, where you can import different packages for different
syntaxes... (not serious)

------
bcheung
Very cool. The syntax is a bit verbose though.

What about expressing it as a tree based data structure?

~~~
whateveracct
Prefix notation already makes it a tree. Why would an explicit data structure
tree instead of the AST make it better? Outside of being able to manipulate
logic program structure at runtime.

~~~
ralusek
Not only is being able to manipulate logic program structure at runtime super
cool, but it would be great to be able to pass around logic as data more
easily.

------
dsego
Looks similar to a business rule engine (like nools
[http://c2fo.io/nools](http://c2fo.io/nools)).

~~~
cpitman
Business rules systems and logic programming are definitely similar. Most
business rules are based on "Forward Chaining"
([https://en.wikipedia.org/wiki/Forward_chaining](https://en.wikipedia.org/wiki/Forward_chaining)),
basically using what we know to satisfy the left hand side condition and then
using the right hand side to assert new facts. Logic programming is usually
based on "Backward Chaining"
([https://en.wikipedia.org/wiki/Backward_chaining](https://en.wikipedia.org/wiki/Backward_chaining)),
where the system attempts to work backwards from a goal to what facts are
needed to satisfy the goal.

~~~
theknarf
What would be the use case for using something like nools? Compared to just
writing it in JS or Java or whatever the rest of your business logic is
written in.

~~~
dsego
Any business rules that need to be changed easily later, that is almost
anything and everything. Examples include discount rates, loyalty points,
insurance premiums and so on. Especially when you have domain experts creating
and updating the rules.

Take a look here at the drools (the java equivalent to nools) documentation:
[https://docs.jboss.org/drools/release/5.4.0.CR1/drools-
exper...](https://docs.jboss.org/drools/release/5.4.0.CR1/drools-expert-
docs/html/ch01.html)

------
PuercoPop
How does this compare with constraintJS[1]

[1]: [http://cjs.from.so](http://cjs.from.so)

~~~
dymk
It's an entirely different concept; this is logic based programming and
constraint solving, nothing to do with DOM elements or user input.

~~~
PuercoPop
Maybe the example of the front page is misleading, but fwir ConstraintJS is a
JS library adding constraints to JS, the DOM/CSS are use cases but the library
is not limited to those use cases

~~~
junke
From [0] and [1], it seems that ConstraintJS does not try to solve
constraints, it propagates changes among variables when they change. In
particular, variables are not tied to domains.

[0] [https://github.com/soney/ConstraintJS/wiki/Constraint-
Variab...](https://github.com/soney/ConstraintJS/wiki/Constraint-Variables)

[1]
[https://github.com/soney/constraintjs/wiki/Introduction](https://github.com/soney/constraintjs/wiki/Introduction)

------
voiper1
Interesting. Took a bit to even understand what logic programming was.

------
caub
is it necessary with ES6 Array methods?

------
formula1
Why is this at 44 points? I respect this is trying to hit a nitch but the
nitch looks more like a step backwards than forwards. This does not seem more
reasonable than what we have available. Maybe when it handles promises will it
have a more obvious use case.

~~~
dymk
There's absolutely nothing about this that would benefit from having promises
- none of the operations performed are blocking. It's constraint solving, not
I/O.

~~~
hrjet
Promises can be helpful in a pure CPU bound workload as well: they help to
avoid blocking the main thread, by yielding more often. This is typically done
by breaking a long running computation into a series of sub-computations that
are scheduled on a timer, and the top level API returns a Promise that
completes when all computations are over.

The same goal could also be achieved with multiple threads (webworkers for
example). But the promises and async/await syntax is more convenient.

