Interactive worksheets are a simple but powerful way for people to manage data and to solve data-related problems. Examples of interactive worksheets range from simple, single-user spreadsheets (such as the interactive grids of cells in systems like Numbers and Excel) to collaborative, multi-institutional planning and design tools.
The power and popularity of interactive worksheets stems from a combination of features.
Meaningful data display. Data is typically presented is presented on worksheets in forms suited to the type of data involved - as tables, charts, graphs, and so forth.
Modifiability. Data can be directly modified by the user in what-you-see-is-what-you-get fashion. Importantly, data can be changed in whatever order suits the user.
Constraint checking. Data is automatically checked for completeness and consistency with static and dynamic constraints. Users are alerted to problems; and, where possible, they are given guidance in eliminating those problems.
Automatic computation of results. Consequences of acceptable changes are automatically computed, and the presentation is updated to reflect those consequences.
While these features can be used in many information management settings, they have special value in certain types of applications, e.g. configuration tasks (such as product configuration worksheets and academic program sheets), teaching (such as interactive exercises and simulated environments), online games (such as Chess, Checkers, Pentago), and so forth.
The process of implementing interactive worksheets using traditional programming technologies is time-consuming and expensive. The good news is that Logic Programming can dramatically simplify this process. Making it easier for developers to create and maintain worksheets. And in many cases making it possible for non-developers to do the same. Creating and maintaining worksheets can and should be "do it yourself" (DIY). Just as it is possible for users without programming expertise to create and manage traditional spreadsheets, it should be possible for users without traditional programming expertise to create and manage worksheets on their own.
In this Chapter, we see some ways in which Logic Programming techniques can be used in creating interactive worksheets that operate in World Wide Web browsers. Although our discussion focusses on this one class of interactive worksheets, the techniques can easily be applied to building interactive worksheets in other technologies.
As an example of an interactive worksheet implemented as a web page, take a look at the academic program sheet shown here. This worksheet provides a means for a student to design a program of study that achieves his academic goals and at the same time meets the academic requirements of his university.
The worksheet includes a listing of courses available to the student. At the bottom on the left, there is a pie chart indicating the proportion of his selected courses in various subareas of Computer Science. In the middle, there is an indication of the number of units of credit the student is requesting for each selected course. And, on the right, there is a listing of professors responsible for those courses.
The student can change his program by selecting courses in whatever order he likes. Clicking an empty checkbox adds the corresponding course to his program of study. Clicking a checkbox that is already checked removes the corresponding course from his program. Once a course is selected, the student can change the number of units of credit for each course by using the slider associated with that course.
An important part of the update process is constraint checking. As each change is made, the worksheet checks that all academic requirements are satisfied. If there is a violation, the corresponding requirement turns red, indicating that there is something wrong. Once the requirement is satisfied once again, the requirement turns black.
As the program is modified, as changes are made, the worksheet is updated accordingly. For example, as each box is checked, it is added to the course list, and a photo of the associated professor appears. Moving the slider for a course changes the requested credit; and, as such changes are made, the pie chart automatically adjusts to show the portion of time the student is devoting to various subareas of the department.
This is a simple example, but it illustrates the key features of interactive worksheets - visibility of all relevant data, the ability to modify that data, automatic checking of constraints, and automatic calculation and display of consequences.
14.3 Web Pages
Many interactive worksheets are implemented as mobile or laptop applications. Others take the form of web-based applications (such as the example shown above). In this document, we focus on the creation of interactive worksheets as web pages encoded in HTML and accessed and managed via World Wide Web browsers.
The data underlying a web page appearing in a browser typically takes the form of a hierarchical data structure called a DOM (short for Document Object Model). The top node in this data structure represents the document, and its child nodes represent its components. Nodes in the DOM typically have attributes of various sorts (e.g. the width and height of a table); and, in some cases, those attributes are objects with attributes of their own (e.g. the style attribute of a node has attributes of its own (e.g. font family, font size, and so forth).
14.4 Web Page State
In order to use Dynamic Logic Programming to specify the appearance and behavior of a web page, we need vocabulary to represent the state of a DOM in the form of factoids that express this state.
First of all, we assign identifiers for the nodes of the DOM that we care about. In order to give them meaning, we assign each of our identifiers as the value of the id attribute of the corresponding node. For example, if we wanted to use the identifier mynode to refer to the input element in the HTML fragment shown below, we would list that identifier as the id attribute of that input widget, as shown in this example.
Next, we invent predicates to describe the various properties of these nodes. See below for the most commonly used predicates. For example, we use the binary predicate value to associate an input node (selector or type-in field or textarea) with its value.
value(widget,value): This factoid is true whenever the value associated with widget is value. The widget here may be a text field, selector, radio button field, slider, and so forth.
valuelist(widget,list): This factoid is true whenever list contains the values associated with the multi-valued node widget. The widget in this case is typically a multi-valued selector.
options(selector,list): This factoid is true whenever list contains the options for selector.
rows(table,list) - true whenever list contains the rows of table.
innerhtml(widget,content): This factoid is true whenever the innerHTML of widget is content. Note that content is typically a string of characters.
attribute(widget,property,value): This factoid is true whenever the property attribute of widget is value.
style(widget,property,value): This factoid is true whenever the property style of widget is value.
Given this vocabulary, we can encode relevant information in the form of a dataset. For example, the relevant state of the DOM fragment shown above can be represented by the dataset shown below.
14.5 Web Page Actions
User interaction with a webpage takes the form of user actions on webpages, sometimes called gestures (e.g. keystrokes and mouse-clicks). In order to talk about these gestures, we need appropriate vocabulary. For example, we use the constant click to represent the operation of clicking on a button. We use the constant select to represent the operation of selecting a specific option from a selector or entering a value in a type-in field.
click(widget): This action occurs when the user clicks on widget.
select(widget,value): This action occurs when the user enters or selects value as the value of widget. The widget here may be an object or text field, a selector, a multi-valued menu, a checkbox field, a radio button field, a slider, and so forth.
multiselect(multiselector,list): This action occurs when the user erases or deselects a value of multiselector. Here list is a list of selected values.
tick: This action occurs periodically. By default, it happens once per second. It is also occurs the user clicks the Run button or the Step button in a simulation control box.
load: This occurs when a page is first loaded.
We use this vocabulary to represent user actions. For example, if the user clicks a button with identifier a, we represent this as the action click(a). If the user selects 3 from a selector with identifier b, we represent this as select(b,3).
14.6 Web-Based Worksheets
Given a vocabulary for encoding data and gestures, we can describe the behavior of a worksheet by writing suitable operation definitions. The following examples illustrate how this can be done.
Consider the following buttons with identifiers orange, blue, purple, and black.
Suppose that we wanted the worksheet to change the color of this document (identified as page) whenever the user clicks one of these buttons. This behavior can be described with the following operation rules.
click(orange) :: style(page,color,orange)
click(blue) :: style(page,color,blue)
click(purple) :: style(page,color,purple)
click(black) :: style(page,color,black)
Alternatively, we can write these rules more compactly by using a variable, as shown below.
click(X) :: style(page,color,X)
This rule reads as follows. If a user clicks on button with X as id, then, in the next state of the worksheet, the color style of the node identified as page should be X.
Although these operation rules work fine, they are not quite complete. This is because, after clicking the above buttons, the state of our worksheet may include more than one fact of the form style(page,color,X). To completely specify the desired behavior, we need to remove the existing style factoids for page when a button is clicked. This can be done with the following operation rule.
The first rule states that, if a user selects value X for pagecolor, then style(page,color,X) should be true in the next state, i.e. the text color of the page should be X. The second rule say that, if a user selects value X for pagecolor and the style of page is Y and Y is different from X, then style(page,color,X) should not be true in the next state.
Unfortunately, this is not quite enough. Our transition changes the color of the page, but there is nothing changing the value of the pagecolor attribute. As a result, it will reset to black after the gesture is processed. The following transition rules update the selector.
As a final example, let's look at an example of interactions between input widgets. The operation rules for the four buttons in the first example change the color of the page correctly. However, they do not update the color indicated in the selector.
The transition rule shown below prescribes the desired behavior. When the user clicks a button with identifier X, then we want the value of the value of the selector to be updated and we want any previous value to be removed.
Combining these rules with the rules shown above allows the user to click either the buttons or make selections and get the same effects in both cases.
In our examples so far, we saw that a single gesture can have multiple effects. For example, changing the value of a selector named pagecolor sets the value of the selector and changes the color of the page. To implement this behavior, we need to manage both conditions in our transition rules, and we need to store both conditions in our dataset. Moreover, if we are not careful, our definitions might get out of sync with each other, and we would not get the behavior we want.
The good news is that it is sometimes possible to write view definitions that describe such behavior more economically and in a way that is less prone to mistakes. By defining some of our predicates as views of other predicates, we do not need to store as much information and we can get by with fewer transition rules.
Instead of storing values, attributes, and styles of widgets, suppose we were to store more general information and then define these values, attributes, and styles in terms of these general features. As a very simple example, suppose we had a unary relation theme that records the chromatic theme for our web page.
In order to manage this them information, we could replace the operation rules shown in the last section with the four shown below.
click(X) :: theme(X)
click(X) :: theme(Y) ==> ~theme(Y)
select(pagecolor,X) :: theme(X)
select(pagecolor,X) :: theme(Y) ==> ~theme(Y)
We could then write view definitions for the key features of our page.
value(pagecolor,X) :- theme(X)
style(page,color,X) :- theme(X)
There are fewer rules here, and they mention fewer predicates. In particular, there is no mention of the style of the page. That property is fully determined by the value of theme, and so we do not need to store or update this information in our rules. Instead, the worksheet computes the style using the view definition given above.
The easiest way to create a web-based interactive worksheet is to start with an HTML page and to add mark-up code to enable the functionality described here. The following is an example of an interactive worksheet built in this way. The page has some introductory text and a selector that lists four different colors. Making a different selection changes the the introductory text to the corresponding color.
Simple Reactive WorksheetThe following selector changes my color.
select(textcolor,X) :: value(textcolor,X)
select(textcolor,X) :: value(textcolor,Y) ==> ~value(textcolor,Y)
style(mytext,color,X) :- value(textcolor,X)
This example illustrates the five things that must be done to convert an ordinary HTML page into an interactive worksheet.
Third, as mentioned in an earlier section, every node that we want to control and every input widget that we want to handle must be given an id. In this case, the introductory text is named mytext.
Fourth, every input widget that we want to handle must be given an appropriate handler attribute. As described earlier, the specific handler must correspond to the type of gesture. For example, if we wanted to respind to a clicking event on a node, we would specify handle(this) as its onclick attribute. If we wanted to respond to a selection event, we would specify handle(this) as its onchange attribute.
Finally, we need to supply any initial data and our view defintions and operation definitions. The initial data is specified in a dataset element, and the rules are specified in a ruleset element. The dataset element typically has id set to lambda, and the ruleset element typically has id set to library. Furthermore, it is customary to specify a display style of none on these elements to prevent them from appearing to the user.
Try saving the text at the beginning of this section as a file, load the file in a browser, and play around with it. Change the file in various ways and see what happens. For example, add additional options to the selector, additional text nodes, additional widgets, and so forth. Try changing the initial data and/or the rules.