Its usage is simple and feels like using any other Python library
There is support for multithreading (not Python threads)
It returns all potential solutions, not just the first one
Under the hood, it actually calls your local install of swipl. This makes the setup quite straightforward. All data is returned as a JSON object.
For this demo, I’m using the code from my Completionist repository. This takes a list of objects, locations and constraints on location access to generate a new adventure every time, similar to what’s done in randomizers.
Here’s what using swiplserver looks like:
# Import the library, it uses a message queue interface to communicate with Swiplfrom swiplserver import PrologMQI# Create an execution thread. You can create several of these prolog_thread = mqi.create_thread()# Now we use the query method to do what we want# This supports pretty much everything # let's start by importing the knowledge base prolog_thread.query("[generator]")# now we generate a "world" # I'm using assertz and dynamics in my code, so this is supported as wellprolog_thread.query("generate()")# Now let's make an actual query # You can notice here that it indeeds returns all solutions not only the first match rslt = prolog_thread.query("location(Location)")print(rslt)>> [{'Location': 'mido_house'}, {'Location': 'sword_chest'}, {'Location': 'deku_tree'}, {'Location': 'hyrule_castle'}, {'Location': 'temple_of_time'}, {'Location': 'ganon'}] # Very nice, but we can also obviously use built-in predicates # Let's get all locations with a findall rslt = prolog_thread.query("findall(Location, location(Location), Locs)")print(rslt)>> [{'Location': '_', 'Locs': ['mido_house', 'sword_chest', 'deku_tree', 'hyrule_castle', 'temple_of_time', 'ganon']}] # Finally, we'll get the items locations which have set by the procedure # Once again, no issues we can get access to data that has been set at runtime rslt = prolog_thread.query("contains(Location, Item)")print(rslt)>> [{'Location': 'mido_house', 'Item': 'sword'}, {'Location': 'sword_chest', 'Item': 'bow'}, {'Location': 'hyrule_castle', 'Item': 'light_arrows'}, {'Location': 'temple_of_time', 'Item': 'ocarina'}]# You can also set a timeout for your queries prolog_thread.query("contains(Location, Item)", query_timeout_seconds=3.0)# Or perform an async query prolog_thread.query_async("contains(Location, Item)", find_all=True, query_timeout_seconds=3.0)# Don't forget to stop the message queue! mqi.stop()
I’m quite happy about the ease of use swiplserver! It does require a local install of swipl but apart from that it’s quite nice and allows the use of all Prolog features.
I’d also like to try out Prolog in different environments, there’s support to compile SWI-Prolog in the browser using Wasm compilation so I definitely want to test it out.
Next time, we’ll be using the output from our Prolog algorithm to serve as input for ChatGPT in a fully automated way. Stay tuned!