[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [plt-scheme] syntax rules -- solution

> try to make a toy type system

I'm pretty sure that syntax-case is the way to go but since,
originally, you wanted to do this using syntax-rules, I wondered
whether this is possible and this is what I came up with.

Maybe, that shows that the syntax-pattern matching mechanism can be
quite useful.

I've packed it into a module for convenience. You may copy-paste the
three definitions into top-level, of course.

Funny enough, it's not neccessary to provide 'intermediate-lambda'.
Why, I wonder?


PS: Please tell me if the code is not understandably enough. I did not
comment, because it's all about pattern-matching.


(module type-lambda mzscheme

  (provide assert-type

  (define assert-type
    (lambda (typep value)
      (or (typep value)
          (error 'bad-type))))

  (define-syntax type-lambda
    (syntax-rules ()
      ((_ () expr0 expr1 ...)
       (lambda () expr0 expr1 ...))
      ((_ (arg0 arg1 ...) expr0 expr1 ...)
       (intermediate-lambda (arg0 arg1 ...) ()
                            expr0 expr1 ...))))

  (define-syntax intermediate-lambda
    (syntax-rules ()
      ((_ () (y0 ...) assn expr0 expr1 ...)
       (lambda (y0 ...) assn expr0 expr1 ...))
      ((_ ((p? v) arg1 ...) (y0 ...) assn expr0 expr1 ...)
       (intermediate-lambda (arg1 ...) (y0 ... v)
                            (and assn (assert-type p? v)) 
                            expr0 expr1 ...))
      ((_ (arg0 arg1 ...) (y0 ...) assn expr0 expr1 ...)
       (intermediate-lambda (arg1 ...) (y0 ... arg0) 
                            expr0 expr1 ...))))

(require type-lambda)

(define test-function
  (type-lambda ((number? x) (string? y) z)
               (list x y z)))

(test-function 1 "funny little" 'macro-riddle)
;; => (1 "funny little" macro-riddle)

(test-function "one" "funny little" 'macro-riddle)
;; => error: bad-type


PPS: When experimentating with macros, I found it extremely useful to
visualize the forms they expand to. Let's do that here:

;; Expand only once:
  '(type-lambda ((number? x) (string? y) z)
		(list x y z))))
;; =>
;; (intermediate-lambda ((number? x) (string? y) z) () 
;; 		        #t 
;; 		        (list x y z))

;; Expand till no macro-identifiers are left in the expression:
  '(type-lambda ((number? x) (string? y) z)
		(list x y z))))
;; =>
;; (lambda (x y z) 
;;   (if (if (#%datum . #t) 
;;        (#%app assert-type (#%top . number?) x) 
;;        (#%datum . #f)) 
;;       (#%app assert-type (#%top . string?) y) 
;;       (#%datum . #f)) 
;;   (#%app (#%top . list) x y z))

After stripping away the context-information, 
this looks like this:

(lambda (x y z) 
  (if (if #t
	  (assert-type number? x)
      (assert-type string? y)
  (list x y z))