[HN Gopher] An embeddable Prolog scripting language for Go
___________________________________________________________________
An embeddable Prolog scripting language for Go
Author : triska
Score : 112 points
Date : 2022-01-26 20:28 UTC (1 days ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| gmfawcett wrote:
| Every language should come with a Prolog interpreter. :) Well
| done, ichiban team!
| para_parolu wrote:
| I never used prolog. In my mind it is some old unused language.
| What is good about it and why should it be embedded into other
| languages?
| Avshalom wrote:
| My elevator pitch for Prolog is this:
|
| It's a constraint solving language, linear problem solving
| language, a database query language, a parsing language, and
| an expert systems language. All unified (sorry that's a pun)
| transparently in a single language suitable for general
| purpose programming instead of half a dozen partially
| interoperable DSLs.
| Jtsummers wrote:
| A practical (but not fielded) use that I found with Prolog:
| programs are multi-use. That is, if you mentally model (and I
| think this is natural for new Prolog programmers) a program
| as running in one direction, you will be pleasantly surprised
| to find that you can use the same program to accomplish
| multiple effects. Using the built-in _append /3_ as an
| example: % find the list resulting from
| appending two lists append([1],[2],C). %% => C =
| [1,2] % find the prefix given a suffix
| append(A,[2],[1,2]). %% => A = [1] % find the suffix
| given a prefix append([1],B,[1,2]). %% => B = [2]
| % find all prefixes and suffixes append(A,B,[1,2]).
| A = [], B = [1,2]; A = [1], B = [2]; A =
| [1,2], B = []; false ;; no more results
|
| The first line is what most programmers new to Prolog will be
| used to, a function performs a computation in a forward
| fashion (here: given two knowns, calculate something). But
| all the others come along "for free" (if you write it
| correctly, which isn't too hard once you've learned a bit of
| the language and style).
|
| My practical use for this was with a (prototyped, not
| fielded) scenario generator. Using the rules (specification
| for the protocol) I could generate arbitrary scenarios. I
| could constrain them by filling in some variables, but
| leaving others blank. And I could take an existing scenario
| and determine if it was even valid. Using _append_ again:
| append(A,[3],[1,2]). %% => false
|
| Now, in my situation instead of a hardcoded [1,2] I had a
| recording of an exchange between nodes, and I could use this
| to get an answer to _why_ it didn 't conform to the spec. One
| program that could be used in many different ways.
|
| The other reason to use Prolog, this was a much higher level
| of abstraction to work with than what we ended up using. It
| was _much_ shorter, but less familiar to others. So we ended
| up with a C# + SQL solution where we had to write each
| variation of the program (generate, test for validity,
| identify problems).
| ModernMech wrote:
| For the same reasons that LINQ is used in C# programs.
| zimbatm wrote:
| Yet another explanation: we have imperative languages,
| functional languages, and declarative languages. Ordered by
| level of abstraction. Prolog fits in the latter category,
| together with SQL and LINQ.
| YeGoblynQueenne wrote:
| Prolog, like Lisp, excels in problems where the best solution
| is to create a language in which to describe your problem,
| and then solve it by using some kind of inference engine.
| Unlike Lisp, it comes with an inference engine built-in, one
| that is as good as possible (formally speaking, it's sound
| and complete). So if that's the kind of use case you have
| your options are either: use Prolog; embed it; or implement
| an informally-specified, bug-ridden, slow implementation of
| half of Prolog.
| convolvatron wrote:
| rule-based programming is a slightly different take than
| procedural programming, and can be quite a bit more
| expressive and efficient to evaluate than procedural for the
| right kind of problems.
|
| 'make' is pretty good example, but 'package manager' might be
| more relevant to people. instead of having explicit control
| flow you have rules that say 'this is the case if these other
| things are the case', and they get triggered to evaluate
| implicitly (and recursively).
|
| variables and values in declarative languages are implicitly
| quantified to be sets of objects, so we say 'for all X where'
| .. instead of '_the_ X where', which makes it similar to SQL.
|
| actually there you go - pretend this is an embedded database
| - that's clearly useful, except with a better query language.
| slaymaker1907 wrote:
| Unification and constraint solving is a big reason why.
| Prolog is definitely the easiest language to write a type
| checker in because it not only has unification built in, it
| is also excellent at navigating trees like type hierarchies.
|
| Something I've personally used it for was navigating the
| parse tree of some T-SQL I was analyzing (it was 100k+ LoC so
| manual analysis was out of the question). There is a pretty
| good C# library for parsing T-SQL, but it is unwieldy to use
| since it relies on visitors and has tons of different typed
| nodes with a huge inheritance hierarchy. I ended up exporting
| this to JSON including type information and initially used jq
| for analysis. However, even that ended up being hard to
| manage so I switched to Prolog and it was way easier to
| write. In terms of this project, I particularly liked that
| the code was so dense and expressive that I could write the
| program by first testing things out in the REPL.
| Zababa wrote:
| Some of them do, for type resolution. Rust has Chalk:
| https://github.com/rust-lang/chalk, Haskell has something that
| can be used too: https://aphyr.com/posts/342-typing-the-
| technical-interview.
| LeonidBugaev wrote:
| If you are interested in logic programming itself, check
| http://minikanren.org/. Go community has a bunch of its
| implementations.
| RcouF1uZ4gsC wrote:
| What are the trade offs of embedding a Prolog interpreter versus
| having a library that implements prolog logic without having to
| have another language.
|
| Are there any high quality prolog as a library implementations in
| languages such as Rust, C++, Java, Go?
| aarroyoc wrote:
| IMHO you loose a lot of expresiveness. Real Prolog code is not
| just simple rules and facts. They can be more complex than
| that. For example, in my case, I'd like to use DCGs a lot,
| which are a bit difficult to translate using "normal languages"
| constructs. Many Prolog systems implement DCGs with Prolog
| macros because Prolog is homoiconic, which most normal
| languages aren't (Lisp is homoiconic too).
|
| Also "logic" arithmetic (clpz, clpfd,...) is not the one that
| comes with normal languages and that breaks a lot of logical
| properties. Doing the sum of two variables in Java side will
| not respect logical properties. Standard is/2 in Prolog is also
| bad, but at least you can replace it with #= and that's it.
| jfmc wrote:
| Some Prolog systems (like Ciao Prolog https://github.com/ciao-
| lang/ciao/blob/master/core/lib/forei...) implement
| bidirectional foreign interfaces. Once you have C bindings it
| is easy to write bindings from Rust, C++, or any other language
| (that can interoperate with C). I give here some details about
| Ciao because this is the system I know better but it should be
| similar for other popular Prolog implementations.
|
| The tradeoffs depend on the complexity of the Prolog code and
| your needs for performance and features: pure LP, Prolog
| (search+unification+cut), garbage collection, dynamic database
| updates, constraint domains, etc. The Ciao Prolog engine is
| around 300-400KB. Adding a few libraries, compiler, etc. it
| goes to 2MB. Naive Prolog systems can be one order of magnitude
| smaller at the cost of sacrificing ISO compatibility,
| performance, etc. Note that "performance" can be very
| misleading. Some Prolog programs may run particularly fast in
| some Prolog system and very badly in others.
| exDM69 wrote:
| > What are the trade offs of embedding a Prolog interpreter
| versus having a library that implements prolog logic without
| having to have another language.
|
| A library that implements Prolog logic is an interpreter.
|
| The difference here is that Prolog source code is passed in as
| a string as opposed to having an embedded domain specific
| language where you construct the abstract syntax tree.
|
| Go is a very simple language and it doesn't have the kind of
| metaprogramming facilities that are typically used for creating
| embedded DSLs. Hence it's a reasonable choice to just have a
| parser and pass in the logic code as a string. There's probably
| a way to pass in the AST as well but it may not be very
| ergonomic.
|
| Parsing the source and constructing the AST isn't very
| expensive and it's only done once per program, so the runtime
| cost isn't huge.
|
| There are Prolog and Prolog-like embeddable interpreters and
| logic programming systems for many other prorgamming languages,
| including Rust, C++ and Java.
|
| The best known ones are Clojure's core.logic (based on the
| logic interpreter in the SICP book) and Minikanren.
| tgv wrote:
| Indeed. Text is "unreasonably efficient" in this case.
| Passing a struct is easy enough in go, but that's all it can
| do for you. So you'd have to pass arguments like
| PLClauseList{PLLoadClause{...}, ..., PLFact{Head:
| PLAtom{"human"}, Arguments: PLArgList{PLAtom{"socrates"}}},
| ...}. Unwieldy, unreadable, and hard to edit.
| vvern wrote:
| There are advantages to embedding. You can retain the host
| language type system and and object model. If you have a
| great query language and model but have to write a ton of
| code to marshal back and forth, it might not be adding that
| much value (classic impedance mismatch).
|
| While go's compile time metaprogamming is virtually non-
| existent, it's runtime metaprogramming with reflection is
| more or less complete. There's a runtime cost to using it,
| but that can be mitigated.
|
| See https://github.com/cockroachdb/cockroach/tree/master/pkg/
| sql... for a reflection-driven, embedded logic query language
| in go that achieves pragmatic goals of writing logic queries
| over data structure graphs at reasonable performance and
| pretty good expressibility.
| Avshalom wrote:
| I've never used it but for Java there's
| https://gitlab.com/pika-lab/tuprolog/2p-in-kotlin (
| documentation seems to be lagging but here's the 3.3 manual
| https://gitlab.com/pika-lab/tuprolog/2p/-/wikis/uploads/d67e...
| )
|
| And specifically for Javascript there's http://tau-prolog.org/
|
| Also pretty much every lisp that's gotten past toy stage has a
| Prolog implementation
|
| Although both Sicstus and SWI have strong Java and C(++)
| interop
| https://sicstus.sics.se/sicstus/docs/latest4/html/sicstus.ht...
| https://www.swi-prolog.org/pldoc/doc_for?object=packages
| vvern wrote:
| What does the interop with go look like? Is there a story for
| writing prolog queries over go data structures and iterating the
| results. If this could be made ergonomic, it'd be very cool.
| zimpenfish wrote:
| From the given example, basically the same as `database/sql` -
| you `Exec` or `Query` a Prolog doodah, iterate over the
| solutions with `Next`, and `Scan` the results into your
| structs.
| stryan wrote:
| I've been keeping an eye on this to use for the rules engine in a
| card game I'm writing[0]. Very excited to get back into using
| Prolog; I think it's fallen by the wayside a bit in the last
| decade or two but there's some sectors that still have strong
| arguments for using it if not as the main language then at least
| an extension language.
|
| [0] Inspired by a HN comment a while back about Gleemin, the MTG
| expert engine in Prolog: https://github.com/stassa/Gleemin
| YeGoblynQueenne wrote:
| Oh, hey, that's cool! Good luck with your card game, I hope
| we'll get to see a "Show HN" soon (no pressure!) :)
| stryan wrote:
| Thanks! Your post was really inspiring to me because I'd been
| grappling with the issue for a while; the rules are all
| hardcoded in Go right now and I'd been slowly trying to
| migrate the engine to either Guile or (more likely, since
| Guile in Go is through cgo) Lua. Reading your comment
| reminded me that A) that's similar to how Magic Arena handles
| cards and B) I studied Prolog back in uni and totally could
| do something similar.
|
| If something interesting comes out of it I'd love to do a
| Show HN but I'd probably have to work things out with my
| colleague first: he's the brains behind the game design and
| project, I'm just the code monkey :).
| mfollert wrote:
| Have you had a look at Expr? https://github.com/antonmedv/expr
|
| We are using it for ETL processing and are very happy with it.
| The performance is excellent and it is easily extendable.
| stryan wrote:
| You know, I didn't think about Expr for this project but I
| did futz around with it as a plugin-like system for another
| project of mine. I liked it well enough, but I feel like it's
| not quite out-of-the-box robust enough for how I'm currently
| designing the engine.
|
| On the other hand, definitely something to keep in mind for
| the matchmaking system I'm writing along with the game, so I
| appreciate you bringing it up.
| spicybright wrote:
| I would love to hear more about this! Do you blog?
| stryan wrote:
| Unfortunately I don't have anything written about it now (or
| a blog at all), but once things get more solid and I have
| code I'm not embarrassed to show I'd love to do a writeup or
| two.
| mst wrote:
| Usually the best time to show code - and, indeed, to
| publish a blog post - is when you're still at least a bit
| embarassed by it.
|
| I've learned over time that what I consider a barely
| acceptable first draft of either is often even so of great
| interest to other people and reminding myself that my own
| perfectionism is in fact mostly counterproductive. Ok, for
| critical code that really really needs to work reliably I
| let myself go full pure math grad on the problem but for
| everything else ... shipping is a feature.
|
| A suggestion to try and help with this - I'll often show
| stuff to a couple of trusted friends first and if the
| result is mostly approving noises that helps a lot as
| leverage against my brain to get it to agree I should hit
| publish in spite of my reservations.
|
| Either way, good luck, and I hope whatever else happens you
| have fun working on this :D
___________________________________________________________________
(page generated 2022-01-27 23:02 UTC)