3

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?
Javran
  • 3,394
  • 2
  • 23
  • 40
  • 1
    Both `foo1` and `foo2` are equivalent (`let` uses `lambda` under the hood). On the other hand, `foo3` is defining a new function, not a constant value as the other options, so it's not equivalent. – Óscar López Jan 03 '14 at 05:22
  • the standard and correct way to rewrite nested `define`s (which *are* recursive) is with `letrec`: `(define foo (letrec ((bar (lambda(n) (+ n n)))) (bar 1)))`. which is equivalent to the (more popular) *"named `let`"* construct, `(define foo (let bar ((n 1)) (+ n n)))` (even though `bar` isn't called recursively here at all). – Will Ness Aug 11 '15 at 07:37
  • (define foo1 ((lambda () (define (bar n) (+ n n)) (bar 1)))), this has excess of parenthesis, remove one pair of parenthesis around the lambda; like this (define foo1 (lambda () (define (bar n) (+ n n)) (bar 1))). It works. – Alfgaar Dec 29 '17 at 10:50

4 Answers4

4

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.

Greg Hendershott
  • 16,100
  • 6
  • 36
  • 53
4

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...

GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • 2
    "Syntactic sugar causes cancer of the semicolon." Alan J. Perlis, epigram 3. http://www.cs.yale.edu/quotes.html – ben rudgers Jan 03 '14 at 22:51
3

foo1 is also equivalent to the following:

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

Is that more acceptable-looking to you?

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
2

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)))
Óscar López
  • 232,561
  • 37
  • 312
  • 386