问题
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:
- I think this kind of nested definition makes sense, but why it is considered a syntax error?
- is there any decent way to write the definition of
foo
?
回答1:
Answering your questions:
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.- 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 ifbar
were a recursive definition, you could useletrec
.
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)))
回答2:
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 forfoo.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.
回答3:
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...
回答4:
foo1
is also equivalent to the following:
(define foo1
(let ()
(define (bar n)
(+ n n))
(bar 1)))
Is that more acceptable-looking to you?
来源:https://stackoverflow.com/questions/20896471/decent-way-of-nested-definition-in-scheme