Search code examples
schemeracketmit-schemeguile

decent way of nested definition in scheme


I want to define a constant foo using an auxiliary function, say, bar. And I want to hide bar inside the definition of foo, so I come with this code:

(define foo
  (define (bar n)
    (+ n n))
  (bar 1))

However, this definition causes syntax errors in many scheme implementations(mit-scheme, racket, guile, etc.).

I have three workarounds but none of them seems satisfactory:

(define foo1
  ((lambda ()
     (define (bar n)
       (+ n n))
     (bar 1))))

(define foo2
  (let ((bar (lambda (n) (+ n n))))
    (bar 1)))

(define (foo3)
  (define (bar n)
    (+ n n))
  (bar 1))

foo1 uses lambda to create an environment of writing auxiliary definitions and the parentheses seem somehow confusing.

foo2 uses let expression but I can no longer use the syntactic sugar (define (f n) ...) => (define f (lambda (n) ...))

foo3 requires less modification comparing with the original one, but every time I want this value, I have to call (foo3) and do the computation all over again.

My questions are:

  1. I think this kind of nested definition makes sense, but why it is considered a syntax error?
  2. is there any decent way to write the definition of foo?

Solution

  • Answering your questions:

    1. define can only be used in certain ways, as mandated by the specification. What you want to do isn't covered by the specification, hence the error. As you know, define assigns a name to the value of an expression, it's just that you can't directly create internal definitions in its context.
    2. But there are other expressions that allow creating new bindings in this context. IMHO foo2 is the best option here, and it's idiomatic, too. And if bar were a recursive definition, you could use letrec.

    But if loosing a bit of syntactic sugar bothers you (because of the way procedures are defined inside a let expression), then try using local, it'll work in Racket:

    (define foo
      (local [(define (bar n) (+ n n))]
        (bar 1)))