Getting started with LAML

Kurt Nørmark ©
Department of Computer Science, Aalborg University

Abstract. This is a chapter that is indented to introduce the most fundamental aspects of LAML. It is part of the overall elucidative tutorial of the LAML software package. In later parts of the tutorial we will come back to many of the issues, which are introduced in this chapter.

1     Introduction

We will here assume that you successfully have downloaded and installed LAML on your computer. Now it is time to use it. It will be good for you if you already have some Scheme experience, but it is also possible to learn Scheme while you go along. There are many ways to do so. We have developed a web-based material about functional programming with Scheme which you may consider. This particular material is with web programming examples, which are oriented toward LAML.

This tutorial, and the other tutorials, are elucidative programs. The frame you are reading now contains the tutorial text. The other frame, which is usually shown to the right of the current frame, is intended to show programs. The upper right menu presents all the programs of this tutorial. The mutual navigation between documentation and program is one of the key ideas of elucidative programming. For more info about elucidative programming click one of the yellow question mark icon in the top control frame.

Please notice that you can navigate from a Scheme program to a relevant place in the Scheme report, by clicking on the bold face keywords or the (slightly) brown procedure/function names. You can also navigate to LAML manual pages by clicking on the (slightly) dark green procedure/function names. Such names appear in both the tutorial text and in the programs. Also notice that all the circle shaped color marks - called source markers - can be navigated. Try out the elucidative navigation possibilities right away!


2     Factorial pages
We start with a couple of versions of a simple web page which shows a table of factorial numbers.
2.1     A factorial page
2.2     Another factorial page

2.1     A factorial page

In this section we will be concerned with generation of one of more HTML pages from a LAML source document. Let us start with a simple example. We wish to make a web page that display the first few factorial numbers:

1!, 2!, 3!, ...,

Please take a look at the fac-1 page (click on the red name to see the program) which corresponds directly to the fac-1 document in the 'getting started' examples. You should also take a look at the page which is generated by the program.

The first line loads "laml.scm" from your LAML installation. The value of laml-dir is transferred from the context (by a shell script, by Emacs, or by other means) and it is bound at LAML installation time. The value of laml-dir is the directory path to your LAML installation.

The second line uses a LAML style simple-xhtml1.0-transitional-validating. In this case we are using a style that allows us to use the XHTML1.0 transitional mirror functions. The use of this style takes responsibility to load the relevant libraries. As we will see later, other LAML styles are more substantial, in the sense that they offer more than just function that supports HTML counterparts. Such more substantial document styles typically generate HTML contents based on high level specifications at the level of XML.

Following that we activate begin-laml which marks ending of the loading part, and the beginning of the real document part of the program.

Next follows the recursive factorial function fac. This is the usual factorial function, which you probably have seen many times before.

The middle big part of the example shows a call to write-xml. (You can click on the name to see its SchemeDoc interface documentation). The write-xml procedure comes from the laml.scm library, and it renders and writes a HTML fragment onto some file. If no file name is passed, it writes the file on fac-1.html, because the proper source name of the example is fac-1. This is a common behavior of many LAML programs/documents. The first parameter '(raw prolog) is a list of properties; Here we ask for raw writing of the HTML fragment. We also ask for the generation of a document prolog (document type definition). Alternatively we could, for instance, pass '(pp prolog epilog) which asks for HTML pretty printing, document prolog (as given by the function standard-prolog ), and document epilog (as given by the function standard-epilog ).

The expression

    (title "Factorials"))
    (h1 "Factorials")

    (table 'border "1"
        (td "1") (td (fac 1)))
        (td "2") (td (fac 2)))
        (td "3") (td (fac 3)))
        (td "4") (td (fac 4)))))

uses quite a few of the so-called mirror functions: html, head, body, h1, table, tbody, tr, and td. These functions correspond directly to the elements (tags) of HTML. The expression is evaluated to some internal structure (not shown in full)

(ast "html" 
  ((ast "head" 
    ((ast "title" ("Factorials") () double)) () double) ... )
  () double)
which can be rendered (linearized to normal HTML text) as
    <table border = "1">
      <tr><td>1</td> <td>1</td></tr> 
      <tr><td>2</td> <td>2</td></tr> 
      <tr><td>3</td> <td>6</td></tr>
      <tr><td>4</td> <td>24</td></tr>

In the context of our example, fac-1, the function write-xml will do the rendering.

Notice that the numbers returned by (fac 1), (fac 2), (fac 3), and (fac 4) are automatically converted to text strings by the mirror functions.

Finally, the expression (end-laml) says goodbye in a gentle way, and it reports about time consumption. The procedure end-laml initiates the final part of the document validation, and it is able to check the links in the document).

We process the LAML document by simply running it as a Scheme program. As mentioned, a tiny bit of contextual information is passed to the Scheme interpreter: laml-dir, the name of the source file (source-filename-without-extension), and the directory in which the file is located (startup-directory).

The best and the easiest way to process the document is from Emacs, where the Emacs LAML mode binds the C-c C-o (Control c followed by Control o) key to an Emacs command laml-process-current-buffer that activates your Scheme interpreter. LAML mode in Emacs also offers a menu with a number of useful commands. Try it out on the files in the example directory!

You can also, from Emacs, activate M-x run-laml-interactively and then issue the command

(laml "fac-1")

You also find run-laml-interactively in the Tools > Laml menu of Emacs.

You can also just start your Scheme system, define laml-dir and the contextual information, and then load the laml program by (load "fac-1.laml"). See additional details on this mode of activation.

Finally, you can activate LAML from a command prompt (shell) in both Windows and Unix. Locate yourself in the appropriate directory and say

> laml fac-1.laml

The prefix > denotes the prompt, so do not write that part yourself! Consult the laml (or laml.exe ) command in the bin directory of your local LAML directory, and make sure that it is executable (in Unix), and that the bin directory of your LAML system is in your path.

2.2     Another factorial page

We will make another version of the program which is more general. Instead of asking for (fib 1) .. (fib 4) we will iterate over the first 50 numbers using the idea of mapping a function on the elements of a list. Please take a look at the fac-2 page, which also is part of the 'getting started' examples. The generated HTML page can be seen here. Notice that we ask for pretty printed HTML rendering via the first parameter of write-xml. You may wish to take a look at the source of the generated HTML page.

The innermost mapping in fac-2 applies the function

(lambda (n) 
  (list (td (as-string n))
        (td (as-string (fac n)))))

on the list (1 .. 50). The function number-interval is one of functions from the LAML general library. The innermost mapping produces a list like

 ("<td>1</td>" "<td>1</td>")
 ("<td>2</td>" "<td>2</td>")
 ("<td>3</td>" "<td>6</td>")
 ("<td>4</td>" "<td>24</td>")

Actually, the elements are not HTML strings, but instances of internal abstract syntax trees which can be rendered as the HTML strings shown above.

The outermost mapping applies the tr function on the rows. In this situation it is crucial that the contents of an HTML mirror function can be a list of contents elements. (This aspect of the mirror functions is discussed thoroughly in another section of the LAML tutorial ). The outermost mapping produces

 "<tr><td>1</td> <td>1</td></tr>"
 "<tr><td>2</td> <td>2</td></tr>"
 "<tr><td>3</td> <td>6</td></tr>"
 "<tr><td>4</td> <td>24</td></tr>"

Again - the elements of the list are actually syntax trees. This list serves as contents to tbody, which is a constituent of the table function.

3     LAML processing
In this section we will discuss a few important LAML processing topics, including the concept LAML load safeness .
3.1     LAML processing

3.1     LAML processing

As discussed on the LAML software home page, LAML processing can be activated in different ways.

Personally, I almost always initiate LAML processing from Emacs, using the Emacs command laml-process-current-buffer (which is bound to C-c C-o in the Emacs setup of the LAML distribution if you use the hygienic key binding mode. If you use the original key binding mode, you just type C-o).

Sometimes, it is attractive to initiate LAML processing by means of the function laml. We have, for instance, arranged that all LAML examples in all tutorial example directories can be re-processed automatically. The LAML examples related to 'getting started', are processed by the script process-all.laml. The function process-list processes a list of laml files. The actual processing is done at the location of the laml procedure ().

In general, we want to be able to write

(laml-cd "dir")
(laml "file")
(laml-cd "..")

in order to process 'file.laml' in 'dir'. For that to succeed it is important that all kinds of file access in 'file.laml' is relative to the LAML startup-directory. In the example above, laml-cd sets the startup-directory. When the 'outer level' of LAML processing start, we always transfer knowledge about the current directory (the startup directory) to the LAML processor.

We have decided, at an early point in time, that all file access is handled explicitly. In principle we always access files via use of full file path information. This stands as a contrast to using load path (ala Emacs load paths, or java class paths).

We say that a LAML program is LAML load safe, if each and every file access (inclusive file loading) is either absolute, or relative to the startup directory. The function in-startup-directory is handy in this respect. When we write something like

(read-text-file (in-startup-directory "file.txt"))


(load (in-startup-directory  "file.scm"))

we ensure that 'file.txt' and 'file.scm' are accessed relative to the LAML startup directory.

There are more LAML processing examples in examples/processing/ in the LAML distribution.


We now approach a much more realistic problem, namely the handling of bookmarks. If you like to see LAML in play with some Scheme programs, you are invited to read through the details. If not, you may safely proceed to the chapter on mirror function, which is the next part of the tutorial.

4.1     Bookmark administration
4.2     The frameset section
4.3     Bookmark selectors and constructor
4.4     The left frame
4.5     The right frame

4.1     Bookmark administration

In this section we will develop a LAML program that produce a set of interrelated web pages. The program manages a collection of bookmarks. The program is called bookmark .

The example is rather typical, because it is based on data that are represented in a simple data structure. The data represent some facts and relationships which we want to display on a web page.

As concrete motivation we can observe that it is not practical to bind bookmarks to a single machine, nor to a single browser. I use several different machines, on both the Unix and the Windows platform, and I use different browsers too. Therefore I maintain my bookmarks as a data structure, from which I generate a HTML presentation of the bookmarks.

Let us assume that each bookmark is of the form

(bookmark "title" "category" "comment")

Thus, a bookmark is a four tuple of a symbol, a bookmark category string, and a comment. The collection of bookmarks, which we represent in a file, is a list of bookmark entries with no particular ordering imposed. Given such a list, we will here make a bookmark browser, such as this one.

The sample bookmark data file is available as bookmark-data.

In the start of the program, in loading-section we load the XML related libraries. Notice that we do not load simple-xhtml1.0-transitional-validating as we did in the factorial examples discussed above. The reason is that we need more control over the exact set of libraries to load for this example. We both need XHTML transitional and XHTML frameset. The file xhtml1.0-convenience.scm holds a bunch of auxiliary stuff that I have developed through the years.

There is a huge overlap among the HTML mirror functions in XHTML transitional and XHTML frameset. In frameset-alias-section we provide direct access to a few HTML mirror functions from the XHTML frameset library, via use of the so-called xhtml10-frameset language map. We also assign xml-check-language-overlap? to #f. If we do not do so, LAML will discover the massive overlap between the frameset and the transitional mirror functions, and give a lot of (non-fatal) warnings. The transitional library 'wins' and 'dominates', because it is loaded after the frameset library in loading-section.

4.2     The frameset section

The bookmark browser will be constructed as a frameset of two frames. Thus, there are three programming tasks involved: The construction of frameset, the left hand frame, and the right hand frame. In this section we describe the frameset task, which is the more simple one. In the two next sections we describe the programming of the two frames.

We first define the variable frame-width-list which defines the widths of the browsers 'column' frames. Next in the section frameset-page we generate the frameset page. This is a page with head (), title (), and two frames (). Both the head, title, and the frame forms are applications of the aliased XHTML mirror functions. Frames are named (for mutual reference purposes) and sourced (in order to bring up other web pages in the frames). Notice the use of frame-width-list (at ): we use the function list-to-string to create a comma separated value of the cols attribute. The resulting frameset is written to a HTML file of the same name as the source LAML file. This is the default behavior of the write-xml procedure. Thus, if the source document is located in index.laml, the frameset will be written to index.html .

We see that we make empty frame pages using make-empty-page!. These empty pages will later in the program be overwritten by more useful frame pages.

4.3     Bookmark selectors and constructor

It is always a good idea to access data in an abstract fashion. In this way we do not bind our program to a concrete data representation. As a consequence, we define four bookmark selector functions:

In addition, we construct a bookmark by means of the constructor function make-bookmark.

4.4     The left frame

The left frame is produced by the function present-categories which is called in the program section left-frame-page. The font-1 function is a convenience function which requires a font size and a color. Alternatively we could have used the XHTML font mirror function directly with appropriate attributes. In LAML, colors can be represented as RGB values in various ways. As an example, '(255 0 0) represents red. Please consult the function rgb-color-encoding in the LAML color library for details on the handling of colors in LAML.

The function present-categories takes a bookmark list, bml, as parameter. The value of the variable bookmarks is passed as actual parameter to this function. The bookmarks as such come from a file, such as bookmark-data, as it appears in the definition of the variable bookmarks.

Let us now take a closer look at the function present-categories, which is called in left-frame-page. It first extracts all bookmark categories by mapping the selector function bookmark-category-of on the bookmark list bml (). This will typically create a list with a lot of duplicates, because each category of bookmarks, in general, will contain many individual bookmarks. Therefore we remove the duplicates from the category list ().

In the body of present-categories we render anchors to the bookmark file (as they are presented in the right frame). This is done my mapping () a specially made, simple lambda function on the sorted and downcased category list. Notice that the downcasing is done in the comparator function (the lambda expression) in order not to change the letter case of the anchor names as such. (Doing so causes a result, which will not work in Netscape and Mozilla, but it is OK in MS explorer). The lambda function returns targeted hyper references. Notices that we rely on anchor names (in terms of the categories), which we assume will be found in the bookmarks.html file. As such it will probably be a requirement that the category names of the bookmarks are without spaces or other kinds of white space.

4.5     The right frame

The right frame contains the bookmarks as such. We decide that it will hold the bookmarks as subitems to their categories. Thus, we go for a bookmark page which looks like:


The first task is to order the bookmarks according to their categories. This is done in the 'top level function' present-bookmarks. The ordering is done by the Scheme/LAML sort-list function () which is one of the so-called compatibility functions of LAML (non-standard functions which LAML assumes can be defined on all platforms on which LAML is used. Collections of these functions for various Scheme systems and platforms are found in lib/compatibility in your LAML distribution). The crucial parameter in the call of sort-list is the comparison function (), which is given two bookmarks bm1 and bm2 to compare. Bookmark bm1 is less than bm2 if bm1 's downcased category is less than bm2 's downcased category.

Now present-bookmarks-1 takes over the rest of the presentation work. The main challenge in present-bookmarks-1 is to present the bookmark category in front of only the first bookmark in a given category. This is somewhat tricky. Our functional solution is the following. In the last two lines of present-bookmarks we use two list: the sorted bookmarks () and a right shifted copy of it (). If we assume that (b1 b2 b3 b4 b5) is the list of sorted bookmarks, the right shifted copy is (x b1 b2 b3 b4). x is a special sentinel-bookmark .

Now given these two lists

      (b1 b2 b3 b4 b5)
      (x  b1 b2 b3 b4)

as bml and prev-bml in present-bookmarks-1, we emit a category header if the category of bm is not equal to the category of bm-pre (). This can be done by mapping a function () over the two lists. As it should be clear, the sentinel bookmark is of a different category than b1, thus causing the first category to appear in front of the very first bookmark.

The presentation of a single bookmark is simple; It is done by present-a-bookmark. Notice how the bookmark comment is used as the title attribute of the anchor tag ().

The right frame is written in the program section right-frame-page.

5     Epilogue
We will summarize what we have learned here, and what we will be concerned with next.
5.1     Epilogue

5.1     Epilogue

In this chapter of the tutorial we have studied some applications of LAML, in which some data, in various data structures, are presented on web pages. First we showed how a function, fac, can be mapped on a list of numbers, and not least how to present the results on a web page. Next we showed a real-life task, namely how to generate a frame-based web page for a collection of bookmarks.

We have encountered various HTML mirror functions already. In the next chapter of the tutorial we will more systematically study the mirror functions of HTML. This insight can be used generally for all XML languages that are supported by LAML.

In general, you can navigate between neighbor chapters of the tutorial by using the right arrow icon in the top banner of this window.