[racket] Visitor Pattern and Racket

From: Harry Spier (vasishtha.spier at gmail.com)
Date: Sat Jan 19 21:43:19 EST 2013

"A Little Java a Few Patterns" gives a simple example of the Visitor
Pattern which collects the variations of a method over different
sub-class variations into its own class.  The classes recursively
define a shish-kabob and the method says if it only contains onions?

Case 1 below is my version in Racket, but I can just as easily in
Racket (I don't know about other languages) collect the variations in
the main class as in case 2 below which seems much simpler, or even
collect the variations of the method in a mixin as in case 3.

Does the Visitor pattern give something that collecting the methods in
the main class or using a mixin doesn't?  Or do the class features of
Racket, particularly mixins make the Visitor Pattern superfluous?

Thanks,
Harry Spier

Case 1: Visitor Pattern
#lang racket

(define shish-D%
  (class object% (super-new)
     (define/public (only-onions-fn)
       (new only-onions-visitor%))
    (abstract only-onions?)))

(define shish-D%/c (is-a?/c shish-D%))

(define skewer%
  (class shish-D% (super-new)
    (inherit only-onions-fn)
    (define/override (only-onions?)
      (send (only-onions-fn) for-skewer))))

(define/contract onion%
  (class/c [init (sh shish-D%/c)])
  (class shish-D% (super-new)
    (init-field sh)
    (inherit only-onions-fn)
    (define/override (only-onions?)
      (send sh only-onions?))))

(define/contract lamb%
  (class/c [init-field (sh shish-D%/c)])
  (class shish-D% (super-new)
    (init-field sh)
    (inherit only-onions-fn)
    (define/override (only-onions?)
      (send (only-onions-fn) for-lamb))))

(define/contract tomato%
  (class/c [init-field (sh shish-D%/c)])
  (class shish-D% (super-new)
    (init-field sh)
    (inherit only-onions-fn)
    (define/override (only-onions?)
      (send (only-onions-fn) for-tomato))))

(define only-onions-visitor%
  (class object% (super-new)
    (define/public (for-skewer) #t)
    (define/public (for-lamb)   #f)
    (define/public (for-tomato) #f)
    (define/public (for-onion shish)
      ((send shish only-onions? )))))

(define shish-a (new onion% [sh (new onion% [sh (new skewer%)])]))
(define shish-b (new onion% [sh (new lamb%  [sh (new skewer%)])]))
(define shish-c (new onion% [sh (new onion% [sh (new lamb%  [sh (new
skewer%)])])]))
(send shish-a only-onions? )
(send shish-b only-onions? )
(send shish-c only-onions? )



Case 2: Collecting the method variations in the main class
#lang racket

(define shish-D%
  (class object% (super-new)
    (init-field sh)
     (define/public (only-onions?)
       (cond
         [(is-a? this skewer%) #t]
         [(is-a? this   lamb%) #f]
         [(is-a? this tomato%) #f]
         [(is-a? this  onion%)
          (send sh only-onions? )]))))

(define shish-D%/c (is-a?/c shish-D%))

(define/contract skewer%
  (class/c [field (sh #f)])
  (class shish-D% (super-new)
    (inherit only-onions?)))

(define/contract onion%
  (class/c [init-field (sh shish-D%/c)])
  (class shish-D% (super-new)
    (inherit only-onions?)))

(define/contract lamb%
  (class/c [init-field (sh shish-D%/c)])
  (class shish-D% (super-new)
    (inherit only-onions?)))

(define/contract tomato%
  (class/c [init-field (sh shish-D%/c)])
  (class shish-D% (super-new)
    (inherit only-onions?)))


(define shish-a (new onion% [sh (new onion% [sh (new skewer% [sh #t])])]))
(define shish-b (new onion% [sh (new lamb%  [sh (new skewer% [sh #f])])]))
(define shish-c (new onion% [sh (new onion% [sh (new lamb%  [sh (new
skewer% [sh #f])])])]))
(send shish-a only-onions?)
(send shish-b only-onions?)
(send shish-c only-onions?)



Case 3: Collecting the method variations in a mixin
#lang racket

(define (only-onions-mixin %)
  (class % (super-new)
    (init-field sh)
    (define/public (only-onions?)
       (cond
         [(is-a? this skewer%) #t]
         [(is-a? this   lamb%) #f]
         [(is-a? this tomato%) #f]
         [(is-a? this  onion%)
          (send sh only-onions? )]))))

(define shish-D%
  (class object% (super-new)))

(define shish-D%/c (is-a?/c shish-D%))

(define/contract skewer%
  (class/c [field (sh #f)])
  (class (only-onions-mixin shish-D%)(super-new)
    (inherit only-onions?)))

(define/contract onion%
  (class/c [init-field (sh shish-D%/c)])
  (class (only-onions-mixin shish-D%)(super-new)
    (inherit only-onions?)))

(define/contract lamb%
  (class/c [init-field (sh shish-D%/c)])
  (class (only-onions-mixin shish-D%)(super-new)
    (inherit only-onions?)))

(define/contract tomato%
  (class/c [init-field (sh shish-D%/c)])
  (class (only-onions-mixin shish-D%)(super-new)
    (inherit only-onions?)))


(define shish-a (new onion% [sh (new onion% [sh (new skewer% [sh #t])])]))
(define shish-b (new onion% [sh (new lamb%  [sh (new skewer% [sh #f])])]))
(define shish-c (new onion% [sh (new onion% [sh (new lamb%  [sh (new
skewer% [sh #f])])])]))
(send shish-a only-onions?)
(send shish-b only-onions?)
(send shish-c only-onions?)

Posted on the users mailing list.