Reputation: 3434
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:
foo
?Upvotes: 3
Views: 6920
Reputation: 70215
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...
Upvotes: 4
Reputation: 16260
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.
Upvotes: 4
Reputation: 223153
foo1
is also equivalent to the following:
(define foo1
(let ()
(define (bar n)
(+ n n))
(bar 1)))
Is that more acceptable-looking to you?
Upvotes: 3
Reputation: 236112
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.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)))
Upvotes: 2