3
votes

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
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
the standard and correct way to rewrite nested defines (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
(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

4 Answers

2
votes

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)))
4
votes

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.

4
votes

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

3
votes

foo1 is also equivalent to the following:

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

Is that more acceptable-looking to you?