On this page:
6.1 Multiple App Elements
6.2 In-Place Replacements
6.3 Pre-processing and Post-processing
7.6

6 The Functional Workflow

In the functional workflow, each application element can provide a replace-page procedure to replace the entire page without side-effects.

This example uses tx-replace to replace the <head> element with a more usable starting point.

Notice that the replacement is a list containing a head element. This is because you can replace one element with several.

"<script type="application/racket">...</script>"

#lang racket/base
(require polyglot)
(provide replace-page)
 
(define (replace-page page-tx)
  (tx-replace page-tx
              (λ (x) (tag-equal? 'head x))
              (λ (x) `((head ((title "My Page"))
                             (link ((rel "stylesheet") ("styles.css"))))))))

So what’s page-tx? Given a Markdown file my-page.md, the page starts life as this value:

`(html (head (title "Untitled")) (body . ,(parse-markdown "my-page.md")))

The replace-page procedure provided by the first app element of my-page.md will get this value bound to page-tx. This implies that the app element can "see itself" inside the page.

If an app element does not provide a replace-page procedure, then it does not change the page at all.

All app and lib elements are removed from the page they produced once they are used, but the workflow will always look for new app elements that appear in new versions of a page. The page is only considered "done" when there are no more app elements left to replace the page. You can take advantage of this to do all sorts of neat things.

6.1 Multiple App Elements

You may write other application elements in the page, and they will be evaluated from top-to-bottom. The output of the first app element’s replace-page procedure will be the input to the second app element’s replace-page procedure, and so on.

This page counts to 2.

  Here is a number: <output>0</output>

  

  <script type="text/racket" id="nums">

  #lang racket/base

  (require polyglot)

  

  (define (increment page-tx)

    (tx-replace page-tx

                (λ (x) (tag-equal? 'output x))

                (λ (x) (number->string (add1 (string->number (car (get-elements x))))))))

  </script>

  

  <script type="application/racket">

  #lang racket/base

  (require "nums.rkt")

  (provide (rename-out [increment replace-page]))

  </script>

  

  <script type="application/racket">

  #lang racket/base

  (require "nums.rkt")

  (provide (rename-out [increment replace-page]))

  </script>

If there is one more app element left in a page, and that element create a new page with even more app elements, then the new app elements are processed without interruption.

6.2 In-Place Replacements

Use tx-replace-me to replace the app element in which the procedure appears.

"<script type="application/racket">...</script>"

#lang racket/base
 
(require polyglot)
(provide replace-page)
 
(define (replace-page page-tx)
  (tx-replace-me page-tx
                 (λ (x) `((p "I'm where the app element was.")))))

6.3 Pre-processing and Post-processing

Here’s an app element that sets a layout for a page.

"<script type="application/racket">...</script>"

#lang racket/base
 
(require polyglot
         "project/layouts.rkt")
(provide replace-page)
(define (replace-page page-tx)
  (one-column "My Page" page-tx))

If you didn’t read Addendum: Avoid Leaving XML in HTML5, do so before you go wild with what I’m suggesting here.

Wouldn’t it be better to just write this?

  <page title="My Page" layout="one-column" />

To do this, go to the functional workflow in your .polyglotrc.rkt and override preprocess-page:

".polyglotrc.rkt"

#lang racket/base
(provide polyglot+%)
(require polyglot)
 
(define polyglot+%
  (class* polyglot/functional%
    (define/override (preprocess-page page-tx)
      (tx-replace-tagged page-tx
                         'page
                         (λ (x)
                           `((script ((type "application/racket")
                                      (id ,(genid page-tx)))
                                     "#lang racket/base"
                                     "(require \"project/layouts.rkt\")"
                                     "(provide replace-page)"
                                     "(define (replace-page x)"
                                     ,(format "  (~a ~e x))"
                                              (attr-ref x 'layout "one-column")
                                              (attr-ref x 'title "Untitled")))))))))

Now for every page, the non-standard <page> element is replaced by an app element you no longer have to write yourself.

To post-process each page, override postprocess-page. It behaves like every other page replacement call.