UP | HOME

[racket macro] define/where

In Racket, we know using form(... represents ignore)

(define (helper)
  ...)
(define (procedure)
  (helper)
  ...)

is better than form

(define (procedure)
  (define (helper)
    ...)
  (helper)
  ...)

However, the benefit of defining helper in target procedure can be see. An example is the where syntax from Haskell:

procedure :: IO ()
procedure = do
  helper
  ...
  where helper :: IO ()
        helper = ...

Can we support this in Racket? Yes! And it won't take a lot of time to make one.

I named the macro define/where, which supporting two kinds of form

(define/where x y
  (where ([y 1])))

and

(define/where (procedure)
  (helper)
  ...
  (where ([helper (λ () ...)])))

1. Macro

To define a macro, we can use define-syntax since in this case, we have a leading name for macro(define/where). Two forms can be handle like this:

(define-syntax define/where
  (syntax-rules ()
    [(_ name:id e) #f]
    [(_ (name:id p* ...) stmt* ...) #f]))

This is enough to handle: (define/where x y) and (define/where (x a b c) (+ a b c)). Notice that we follow the syntax limitation from define, an identifier binding can have only one expression. Now we need to handle where clause:

(define-syntax define/where
  (syntax-rules ()
    [(_ name:id e
        (where
         ([x*:id e*] ...))) #f]
    [(_ (name:id p* ...) stmt* ...
        (where
         ([x*:id e*] ...))) #f]))

This is whole syntax wrapper, but we need to generate code then can say macro would work:

(define-syntax define/where
  (syntax-rules ()
    [(_ name:id e
        (where
         ([x*:id e*] ...)))
     (define name:id
       (letrec ([x*:id e*] ...)
         e))]
    [(_ (name:id p* ...) stmt* ...
        (where
         ([x*:id e*] ...)))
     (define (name:id p* ...)
       (letrec ([x*:id e*] ...)
         stmt* ...))]))

letrec is very nice in this task, then define/where is just a define with letrec but reverse the declaration order! Thanks for your read and have a nice day!

Date: 2020-08-23 Sun 00:00
Author: Lîm Tsú-thuàn