Web authoring with higher-order functions

Kurt Nørmark ©     normark@cs.aau.dk
Department of Computer Science, Aalborg University


In the earlier parts of this tutorial we have described the fundamental aspects of the HTML mirror in Scheme. We will now take a look at a slightly more advanced aspect, namely authoring with use of higher-order functions. This particular aspect pays tribute to the fact that in LAML, HTML is available as Scheme functions (not macros or just list structures).

The topic of 'higher-order functions' is a key area in functional programming. Therefore we find it important to cover higher-order functions in this tutorial. It should be noticed that LAML has been defined with particular emphasis on 'a good fit' between higher-order functions that work on lists, and the HTML/XML mirror functions.


1     Summing up

In this section we will briefly summarize our current knowledge of the HTML mirror functions in Scheme, as covered in details in the previous sections of this tutorial, most notably the section about the HTML mirrors.

1.1     Summing up

1.1     Summing up

Until now in this tutorial we have seen how to use the HTML mirror functions to produce relatively straightforward web pages. We have also seen how to make pages which include pieces of Scheme programs, such as the factorial function, or functions for managing lists of bookmarks. In addition we have studied the details of HTML mirror functions, in particular the interpretation of different kinds of parameters to these.

It is now time to go on from here. We are in a functional programming context, and therefore the ideas of higher-order function is important. In this section we will careful explain the link between web authoring and higher-order functions.

2     Higher-order functions
We start with a very brief account on higher-order functions. It is really just a few words with a reference to a LAML related teaching material, in which there are many more details.
2.1     Higher-order functions

2.1     Higher-order functions

Higher-order functions accept functions as parameters. In addition, a higher-order function can return a function as the result. Finally, functions can be organized in data structures, such as in lists (lists of functions).

I will not here go deeper into the general subject of higher-order functions. I have written a teaching material about functional programming in Scheme. Please take a look at the chapters that cover higher-order functions. I will recommend that you read the thematic view (text book like exposition) of this material, chapter 14 - 18. The direct link to the thematic view of chapter 14 is here.

3     Element Modification
Using element modification it is possible to generate specialized versions of a particular element, which binds attributes or part of the content to particular values
3.1     Element Modification

3.1     Element Modification

The function xml-modify-element allows us to bind attributes to constant values in a mirror function, before the mirror function is applied. To illustrate this idea, take a look at element-mod which uses the XHTML1.0 transitional mirror. The resulting page is also available.

The name ol-alpha is bound to a modified version of the HTML mirror function ol, in which the type attribute is pre-bound to the value "A". By using ol-alpha you will 'automatically' add the type attribute to the instance of olwith the value "A" .

The function a-target is defined to be a modification of the a HTML mirror function, in which the target attribute is pre-bound to the parameter t of a-target.

ol-roman-3 is a modified version of ol, in which we apply the attributes in ol-attributes. Notice the way we can pass a list (more precisely, a property list) of attributes to xml-modify-element.

The function named sli is a modified version of the mirror of the li element, in which we append the value of the (p) expression. Thus, we see that we can use xml-modify-element for 'modification' of the element contents as well as the attributes of the element.

When we use xml-modify-element to 'modify' the element contents, it is relevant to have a version which prepends the contents instead of appending it. The function xml-modify-element-prepend does that. See !!-li for an example. Again, it is instructive to see the effect on the generated HTML page .

4     Using higher-order list functions

In Lisp and Scheme, structured data is typically organized in list structures. In this section we will illustrate how to make use of mapping and filtering together with the HTML mirror functions.

4.1     Ordered and unordered lists
4.2     Tables
4.3     More Tables

4.1     Ordered and unordered lists

Now consider the examples in write-clause-lists. It may be tempting to pass a list of items to the ol or ul mirror functions. This can be done as in the second ul clause of the lists document. However, it is hardly attractive to use this form compared to the first one , in which we embed li elements in the ul form.

Things is a little different if we already have our data in a list, such as my-list. In that case it more reasonable to use the third form in lists, namely this one:

  (ul (li-map my-list))

Recall that li-map is defined explicitly in the document, by use of a curry-generalized map.

Let us also illustrate the use of filtering. In the list my-second-list some of the items are marked with a star. We want to eliminate the unstared items in the rendered list. The fourth example shows how it can be done by use of filtering.

The resulting web page is available such that you can see the actual presentation of the lists from this section.

4.2     Tables

Tables can in a natural way be organized as lists of rows or lists of columns. In HTML, a table is basically a list of rows. Using Scheme, it is attractive to represent tables as lists of lists (list of rows, where a row is a list of elements).

As the first example, let us look at the factorial table example from the getting started section. The original example is shown in fac-2, which already makes use of mapping.

We have refined the factorial example to that of fac-3. Let us go over the details.

We first redefine map to be curry generalized. The function curry-generalized makes it possible to use map in a curried way. Thus,

 (map td)

returns a td mapper. Without curry generalization, (map td) would cause an error, because map requires at least two parameters: a mapping function and a list.

After map and fac comes a specialization of the table mirror function table-with-border with the border bound to 1. The function make-n-fac-n makes a list of numbers, with elements such as (n (fac n)), with n running from 1 to an upper limit h.

Finally we see the write-html clause write-clause, in which we meet the crucial expression

    (compose tr (map (compose td as-string)))
    (make-n-fac-n 55))

The inner composition aggregates the td mirror function with the string converter function as-string. The outer composition aggregates the tr mirror function and the 'extended td mapper'. This composed function is mapped on a list like

  ((1 1)
   (2 2)
   (3 6)
   (4 24)

as formed by make-n-fac-n. The resulting page is similar to the pages from the earlier chapter.

4.3     More Tables

We will now take a look at another and more advanced table example, which can be seen here. The source document is more-tables.

We define sample-table as a list of rows. In write-html-clause we first render the table in a straightforward way .

Next we show a rendering of the table in which we compose the function switch with tr and (map td). The function switch switches the first and second element in a list. When applied via map on rows in a table, the effect is a switching the first and second column. See again the resulting HTML document for the effect of applying switch .

The final table made in the example is interesting because we apply the rowspan attribute in one of the table cells - the upper left one. We use the cell function, which is just an alias of list. In addition, we colorize all numeric cells (the third column) with a grey background. This is done by grey-numeric. Please study both the result and the generating expression carefully.

5     XML-in-LAML parameterization and abstraction

In this section we will discuss how to generate functions which obey the XML-in-LAML mirror function parameter passing rules. In the first variant, xml-in-laml-parametrization, we provide for a XML-in-LAML parametrization of an existing XML related function which uses straightforward positional parameters. In the second variant, xml-in-laml-abstraction, we provide for abstractions on top of existing mirror functions.

5.1     XML-in-LAML parametrizations
5.2     XML-in-LAML abstractions
5.3     More XML-in-LAML abstractions

5.1     XML-in-LAML parametrizations

Now take a look at color-frames, more specifically at the write-html clause at write-html-clause-color-frame. We will start by using the XHTML convenience function color-frame-width ( ) to produce a color framed text, see here (original color frame).

We wish to use the XML-in-LAML parameter passing techniques for color-frame-width. That is, instead of the call

  (color-frame-width "Some text" red 100)

we prefer the following call

  (new-color-frame-width "Some text" 'color red 'width "100")

or equivalently for instance

  (new-color-frame-width 'width "100" 'color red  "Some text" )

or perhaps something like

  (new-color-frame-width "Some" 'width "100" 'color red  "text" )

which more profoundly uses the merits of the mirror rules. In write-html-clause-color-frame we focus on the call of new-color-frame-width at .

Let us now develop new-color-frame-width using the XML-in-LAML library higher-order function xml-in-laml-parametrization . When we use xml-in-laml-parametrization we can - most importantly - carry out a transformation from the new XML-in-LAML parameter format to the old one. The local function med (for mediator) does that, just by accessing the attributes property list. The local val! procedure validates the new parameter list, using the generally useful functions color-string? and hex-numeric-string?. As prescribed in xml-in-laml-parametrization errors are reported using the function xml-check-error .

Notice the use of get-prop and defaulted-get-prop, which both work on property lists. These functions correspond to the functions get and defaulted-get, which work on association lists. All four functions are located in the library of generally useful Scheme functions.

Again, take a look at the resulting HTML page to assure yourself, that it works. Due to the non-numeric value of the width attribute, we get and error when we process color-frames.

5.2     XML-in-LAML abstractions

It is often convenient to introduce ad hoc abstractions, when you write a new web document. This is very easy in Scheme, because we can just write a simple function.

Things become a little more difficult if we want the new abstraction to obey the mirror function rules of LAML - see the section about HTML mirror rules. We will now see how to use xml-in-laml-abstraction in such situations. We switch our interest to a new example web document, li-extensions .

The functions spacy-li and question-li are both generated by xml-in-laml-abstraction. The function xml-in-laml-abstraction is similar to xml-in-laml-parametrization. But the function xml-in-laml-abstraction is intended to carry out abstractions on top of an XML-in-LAML mirror function. In other words, the lambda expressions passed to xml-in-laml-abstraction should ultimately pass the content to a 'terminal' XML-in-LAML mirror function.

spacy-li is a variation of the HTML mirror function li, which embeds the list item in a p element (presumably to make a little white space before the next list item). In write-html-clause-li we see an example use of spacy-li ( ). In spacy-li notice that we pass the attribute property list to the li element and the contents to the nested p element. Also notice the general and characteristic 'LAML parameter profile' is now made available to the spacy-li function.

The function question-li introduces an ad hoc abstraction, which takes a keywords attribute. We use various HTML elements, such as p, b, br, and font to render a question-li Notice that we use spacy-li in the body of question-li. Notice again the versatility of the actual parameters in write-html-clause-li ( ).

The question-li function passes an attribute validator, generated by required-implied-attributes. The higher-order function required-implied-attributes is convenient for creation of functions that check attributes. Its first parameter is the list of required attributes, and the second parameter is the list of other possible attributes (called implied attributes in a DTD). - Notice that x attribute passed to the second and third list is not valid according to the generated checking procedure. Try process li-extensions to see the effect.

The HTML page generated by li-extensions is here .

5.3     More XML-in-LAML abstractions

We will show yet another example of the use of xml-in-laml-abstraction in the document more-li-extensions. The generated HTML document can be seen here. The example is made to illustrate the full generality and power of xml-in-laml-abstraction. Notice, however, that we still recommend the use of the XML in LAML framework, instead of programming several XML in LAML abstractions.

We program an abstraction question-item with the attributes exercise and eref. A question-item can contain a keywords clause (plural) which, in turn, can contain a number of keyword clauses. In write-html-clause-more-li we see a couple of examples ( ).

Both keywords and keyword return AST's in a designated adhoc language. The AST constructor make-ast is used for the construction. The second lambda expression parameters of xml-in-laml-abstraction of keywords ( ) and keyword ( ) check the validity of the new elements. In question-item we use traverse-and-collect-first-from-ast and traverse-and-collect-all-from-ast to decompose the constructed AST. The pieces are put together at .

Again - this example is probably too complex relative to the task at hand. But it shows that XML-in-LAML abstractions, made by xml-in-laml-abstraction, can play together in the way it is intended.

6     Postscript

For additional information about the use of higher-order function for web authoring you can consult lecture 4 of the teaching material Functional Programming in Scheme - a web-oriented approach.