Authors: Andrew Sung, Nathan Hsu
What is the problem?
We aim to optimally schedule trips for a group of people based on each person's availability and weather forecast data.
This problem involves something which we know can work well in Prolog -- namely constraint satisfaction, and something we have not yet explored in Prolog -- the use of APIs.
What is the something extra?
We will incorporate the use of a weather API to let the scheduler favour certain trips on days with a certain forecasted weather (e.g. prefer a beach trip to be scheduled on a sunny day rather than a rainy day).
What did we learn from doing this?
Programming in Prolog required a very different way of thinking than in imperative languages -- even more so than in Haskell. It took some time and experimentation before getting used to Prolog and understanding how we could write an app which generated schedules, but after that hurdle, we believe we were able to effectively utilize the language to rapidly modify and extend the app. It is difficult to explain in words, but it was especially eye-opening when realizing that applying constraints required very little 'explicit' code which dictated how values would be passed around and transformed; we simply defined relations, then Prolog took care of actually generating combinations and applying the constraints.
Instead of having to try different rules of inference and hoping one succeeds, Prolog greatly reduces the search space by allowing the production of all possible answers using the semicolon and the findall predicate. Although relatively simple Prolog code can generate results similar to other languages, Prolog emphasizes the need for accurate and specific case handling. Unlike Haskell, the ordering of cases significantly affect the efficiency of Prolog since Prolog attempts all possible combinations of the rules. While Haskell has strong typing, a Prolog function is allowed to have different numbers and types of parameters (even multiple predicates with the exact same parameters). In addition, Prolog is much better than Haskell at representing different states by using facts and setting up constraints.
Depending on the context, very specific rules with a high number of constraints may need to be applied in Prolog to eliminate superfluous or redundant answers and optimize the efficiency of a function. One of the hardest part of learning Prolog was realizing that rules are not treated as if-else statements. Instead, Prolog attempts to find all possible solutions by trying rules in all possible combinations. Therefore, rules need to be very specific to avoid possible misinterpretation of "truth" that does not exist. On the other hand, relatively "unspecific" rules with less constraints should be applied when writing a function that is expected to generate many results such as finding all combinations of elements in a list.
Looking back, we believe Prolog was well-matched for this application. Though we admittedly struggled on the initial implementation before we got used to Prolog's capabilities, it is easy now to imagine this same application requiring many more lines of code (and, in turn, potentially more scattered and less readable code) if it were to be written in an imperative language. Because we defined relations rather than explicitly implement pieces of logic in low-level code, we feel Prolog allowed us to dedicate more resources (both in terms of time and lines of code) to actually enriching the app's features.
Utilizing APIs in Prolog was also surprisingly easy, and the SWI-Prolog documentation provided convenient predicates for handling the retrieved JSON data. We were able to truly appreciate the power of Prolog's pattern matching when transforming this data into the structure we desired.