Course:CPSC-312-2019-Prolog-Restaurant-Queries

From UBC Wiki

Authors: Cindy Hsu (q3a1b), Theodorus Jossoey (v9y1b)

What is the problem?

So far, we have done a fair amount of list manipulation in Prolog, but it has mostly been on words, pairs, or numbers. We are curious as to whether or not

functional programing (and Prolog specifically), are well-suited to querying larger objects, that more closely resemble what one would find in an OOP program

or as would be stored in a relational database. We will be writing a program that offers a list of restaurants that match the search parameters. We will be

judging the readability/maintainability of the resulting logic program's code, and noting any constraints we find. From this, we can form an opinion as to whether

functional programming is still a viable choice for working with imported or large objects.

What is the something extra?

Instead of a mock database, we will use http/json and http/http_client libraries to make calls from Prolog to the Yelp API to retrieve restaurants.

By working with real restaurant data, our findings will have higher validity and generalizability to filtering on other types of objects. It also increases

complexity due to having to parse nested JSON fields, working with strings text, and generating valid HTTP requests based on user queries.

What did we learn from doing this?

1. Prolog has a lot of good client/server built-ins.

It was impressive how easily it was to make an HTTP request in Prolog. The http_client comes with powerful built-ins that allow for the connection to be

opened, read, and closed, without any handling of streams by the user. Query parameters could be inputted as key=value pairs and automatically

converted to URI format. There was no need for an application_json header with the `http/http_json` module imported. The datatype read could be

customized between atoms and strings. There was no custom code needed to get anything in the right format for the request. However, it is very

hard to trace through this code due to all of the automatic conversions and header setting happening in the background, and the fact that we can't add

breakpoints in an ide like we would to debug Java or C++.

2. Error handling can be (over)simplified.

Because 4xx and 5xx http responses were throwing nasty looking exceptions, we looked into how we could catch and print error messages.

The try-catch block is widely used in most programming languages, and we were pleasantly surprised that Prolog has a `catch/3` method

that allows you to specify a "goal" to execute, and a handler function to call if an exception is thrown. There is also a `print_messages/2`

built-in that we ended up using, because it converted the Prolog error type to a human-readable message.

Our only gripe with this is that the error contexts are not specific enough, and that the messages are sometimes misleading.

When one adds a bad input to the search parameters, Yelp servers respond with 400 Bad Request. The context only has that, and not

anything about why the request is bad, which makes it hard for the user to correct their request. Also, I believe anything thrown by a GET

request is of type existence_error, which leads to the print_message "url ... does not exist", preceding the status code. This message somewhat

implies that the error is a 404 Not Found, which could be misleading.

3. How to manipulate a 'json' object and convert it into a list.

The Yelp api return a json object, but we wanted to remove/flatten/rename certain structures for a better presentation of information. In our approach,

the list is first converted into a list of json object, then the list needs to be converted again in order to be used for further filtering, such as removing

unecessary information in the returned data, and flattening nested structures like 'location'. In this process we learn how to manipulate a json object

and learn more about dealing with lists. It is apparently quite simple to convert json object into a list, by simply putting in json(Obj) to extract the Obj

inside it. There are no specific function to convert a json object into a list directly.

Conclusion

Prolog has a surprising amount of support for I/O as a functional programming language, and we were able to find many of the

language features we have come to expect in our modern languages. We believe that one could build full HTTP client/servers quite easily

by importing the SWI modules, which handle a lot of stuff like conversion to json, or setting headers automatically/with predicates. Writing

to stdout was easy with `format/2`, which acted similar to printf in any other programming language. One extra detail we thought was nice

was that the program could also be compiled into an executable, allowing it to be accessed on different machines and with the source obfuscated.

However, deconstructing the JSON into basic terms so we could compare the data with our filters was quite messy. Things had to be

passed back and forth between different functions since the JSON was heavily nested, and the notion of json being a key=value list made

it hard to reason what was happening since the execution was all over the file, and there were no global variables to store values as we went.

Also, since there were no classes, to really understand the structure of the object and we had to refer to the Yelp documentation where

the properties on the restaurant object and their nesting levels were clear.

After we got the deconstruction to work, filtering was similar to our class assignments, and there were no problems with writing the logic

for that. To do the filtering, there are a lot of different ways, in this project we filter the result after converting it from json object into a list. There

are not much differences between different ways to filter it because of small amount of returned data. However, the run time will be minimized if

the filter is executed while converting json object instead of converting all of them first then do the filtering. But, since we limit the data into 20

restaurant that will be shown, the running time difference is insignificant. In conclusion, Prolog is fully capable of handling real scenarios

(like being a lightweight HTTP client), and has many of the expected features in modern languages, but it may not be the preferred choice

for such applications due to the complexity of parsing highly nested objects.

Links to code etc