Comp210 Lecture # 22    Fall 2002

Visitors, Continued...

Yeah, it was a little fast last time, so let's go back and review about visitors first.

Let's try another example of a recursive visitor:

;; ordIns does an ordered insert of param into 
;; an ascending list.
;; list-of-num num --> list-of-num
(define ordIns
  (make-Visitor
   (lambda (a-list param)
     (cons param a-list))   ;; nowhere to put it except here
   (lambda (a-list param)
     (cond
       [(< param (first a-list)) (cons param a-list)]   ;; put it here
       [else (cons (first a-list) (execute (rest a-list) ordIns param))]))))   ;; keep looking

"ordIns test case:"
(equal? (list 1 2 3 4 5 6 7) (execute (list 1 2 3 5 6 7) ordIns 4)
)

Notice how the recursive call uses the whole visitor, without worrying about whether the empty case or the inductive case is needed next.

Here's a recursive visitor that uses a recursive visitor:

;; listSort sorts a list in ascending order
;; param is ignored.
(define listSort
  (make-Visitor
   (lambda (a-list param)
     empty)
   (lambda (a-list param)
     (execute (execute (rest a-list) listSort param) ordIns (first a-list)))))

"listSort test case:"
(execute (list 4 2 7 3 5 6 1) listSort 0)

See how an insertion sorting algorithm has been pared down (abstracted) to its very essences?

Got visitors down now? Sure you do--no problem.

Since this is Templates-R-Us, we are compelled to create some templates for visitors:

;; Visitor template using local
(define listVisitor
(local
[(define (fBase a-list param)

(...a-list...param...)
)
(define (fInduct a-list param)
(...(first a-list)...param...(execute (rest a-list) listVisitor ...)))]
(make-Visitor fBase fInduct)))
;; Visitor template, using lambda
(define listVisitor
(make-Visitor
(lambda (a-list param)

(...a-list...param...))
(lambda (a-list param)
(...(first a-list) (execute (rest a-list) listVisitor ...)... param...))
))

But you say, isn't this at least the same amount of code as our original design template? What have we gained here?

(Reply in a deep Continental accent) "Ahh, let us compare and contrast!..."

;; Original design template with just one parameter for comparison
(define (listFunc a-list param)
   (cond
       [(empty? a-list) (...a-list...param...)]
       [(cons? a-list) (...(first a-list)...param...(listFunc (rest a-list)...))]))

The blue highlighted code above identifies the invariant syntax in the templates. The red highlighed code above identifies the variant syntax.

Notice these things about the visitor vs. original design templates:

The last point is crucial because it represents a fundamental change in the way that we view what programs are. So far, we have thought of programs as collections of functions that call each other. A sort of giant (f(g(h(l(k(x)))))) as it were, which has a well-defined execution path through it.

Visitors, on the other hand, lack this well-defined execution path. Instead they simply provide "service" functions for whomever wants to use them, with no knowledge of who, how, or why they are being used.

The Visitor structure combined with the execute function and the list create what is called a "component-framework" system.

Note the new syntax above to indicate that the Visitor structure holds two functions with the following contracts:

fBase: Empty Any1 --> Any2
fInduct: Cons Any1 --> Any2

All large, modern software systems are built using component-framework technologies.

Examples of component-framework systems:

  1. Plug-ins and browsers such as Netscape, IE and Mozilla (incompatible frameworks --- arghh!!)
  2. Add-ins and Microsoft Office.
  3. Modular database systems like that used at Rice.
  4. Audio, video, mouse and other drivers and operating systems such as Windows, Mac, Linux, Unix, etc.
  5. Simulations systems such as electronics simulators with modules for different electrical components.
  6. etc, etc, etc.

This is what we have wrought with just a few lines of code.

Higher Order Functions in Terms of Higher Order Functions

Let's see what these things can really do. How about we take our fancy higher order functions, map, fold-right and fold-left and see if they can be re-written using visitors?

Let's start with map:

;; mapVisitor implements map as a visitor
;; the parameter is a function of one variable
(define mapVisitor
  (make-Visitor
   (lambda (a-list func)
     empty)
   (lambda (a-list func)
     (cons (func (first a-list)) (execute (rest a-list) mapVisitor func)))))

"mapVisitor test cases:"
(equal?  (map sqrt (list 1 4 9 16 25))  (execute (list 1 4 9 16 25) mapVisitor sqrt)) 
(equal? (map (lambda (x) 
               (string-append (number->string x) " banana"))
             (list 1 2 3 4 5))
        (execute (list 1 2 3 4 5) mapVisitor (lambda (x) 
                                            (string-append (number->string x) " banana"))))

How about fold-right? We'll need a special function to hold the base case value and the inductive case function. Note that the abstract contract for the inductive case function is the same for fold-right and fold-left -- they're the same thing after all, they just push information in different directions through the list:

;; Structure used for both foldrList and foldlList
;; f is the inductive case function
;; base is the base case value.
;; If the input list is a list-of-any1
;; and the base case value is an any2, then
;; the inductive case function has the abstract contract:
;; f: any1 any2 --> any2

(define-struct FoldInp (f base))

;;foldrVisitor implements foldr as a visitor
(define foldrVisitor
  (make-Visitor
   (lambda (a-list foldInp)
     (FoldInp-base foldInp))
   (lambda (a-list foldInp)
     ((FoldInp-f foldInp) (first a-list) (execute (rest a-list) foldrVisitor foldInp)))))

"foldrVisitor test cases:"
(equal? (foldr + 0 empty) (execute empty foldrVisitor (make-FoldInp + 0)))
(equal? (foldr + 0 (list 1 2 3 4 5)) (execute (list 1 2 3 4 5) foldrVisitor (make-FoldInp + 0)))
(equal? (foldr * 1 (list 1 2 3 4 5)) (execute (list 1 2 3 4 5) foldrVisitor (make-FoldInp * 1)))

Piece of cake.

What about writing map in terms of foldrVisitor and foldl in terms of a Visitor? ...Not today....    

The code for this lecture can be downloaded here: visitors2.scm

 

 

©2002 Stephen Wong