decent way of nested definition in scheme

6.6k views Asked by At

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?
4

There are 4 answers

0
Óscar López On BEST ANSWER

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)))
0
C. K. Young On

foo1 is also equivalent to the following:

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

Is that more acceptable-looking to you?

0
Greg Hendershott On

If I understand your question correctly, another idiomatic way to do this in Racket would be to use a module.

This module could be defined using a separate file:

;; foo.rkt
#lang racket
(define (bar n)
  (+ n n))
(define foo (bar 1))
(provide foo)

;; use-foo.rkt
#lang racket
(require "foo.rkt")
foo

Or via a module form within one file:

#lang racket
(module 'foo-mod racket
  (define (bar n)
    (+ n n))
  (define foo (bar 1))
  (provide foo))

(require 'foo-mod)
foo

Is this concise compared to your examples? Of course not. But in practice this sort of encapsulation usually works fine at a module granularity.

  • For instance a private helper like bar might be useful in defining multiple other functions or constants.

  • Often the file form is more convenient: If a helper like bar is not nested, but instead at the module top level for foo.rkt, it's easier to debug it in a REPL.

p.s. Racket provides a define-package form for compatibility with Chez Scheme, but it's not idiomatic in Racket; instead you'd use a submodule.

1
GoZoner On

Your original code has a syntax error because the required syntax for define of an identifier is

(define <identifier> <expression>)

but your syntax is

(define <identifier> <definition> <expression>)

You need some way to group the <definition> and the <expression>. What you are looking for is something that allows lexical definitions - in Scheme this is a syntactic form with a <body>. The syntactic forms for this are a lambda or any let (and variants) or a 'programmatic' begin.

But, this is easily done in Scheme w/o needing Racket extensions or extra, empty lexical environments or a <body> syntactic form. Just use what you considered 'unsatisfactory'

(define foo
  (let ((bar (lambda (x) (+ x x))))
    (bar 1)))

or even

(define foo
  ((lambda (x) (+ x x)) 1))

Too much sugar, even syntactic sugar, my have adverse health consequences...