Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Procedure definition style?

6 views
Skip to first unread message

Erik Max Francis

unread,
Feb 28, 2002, 9:40:01 PM2/28/02
to
There are two equivalent ways to define a procedure in scheme:

(define f (lambda (args...) body...)

and, as a shortcut,

(define (f args...) body....)

_The Scheme programming language_ suggests that the former style is
preferred, since the latter is only syntactic sugar. But there's
nothing inherently wrong with syntactic sugar (after all, most language
constructs are "only syntactic sugar," but they make things a great deal
more clear). Is there a general consensus on the choice of style? Is
the former style _really_ commonly preferred over the latter, or was the
preference indicated in the book a minority?

Thanks.

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, US / 37 20 N 121 53 W / ICQ16063900 / &tSftDotIotE
/ \ Laws are silent in time of war.
\__/ Cicero
Esperanto reference / http://www.alcyone.com/max/lang/esperanto/
An Esperanto reference for English speakers.

David Rush

unread,
Mar 1, 2002, 3:55:49 AM3/1/02
to
Erik Max Francis <m...@alcyone.com> writes:
> There are two equivalent ways to define a procedure in scheme:
> (define f (lambda (args...) body...)
> (define (f args...) body....)
>
> _The Scheme programming language_ suggests that the former style is
> preferred, since the latter is only syntactic sugar. ... Is there a

> general consensus on the choice of style? Is
> the former style _really_ commonly preferred over the latter, or was the
> preference indicated in the book a minority?

I originally preferred (define x (lambda ...)) until I realized that
Bigloo gave me better type-checking (arity-checking really) with
(define (x) ...). It also seems that most of the code I see on the net
uses the 'shortcut' style.

david rush
--
To have no errors
Would be life without meaning
No struggle, no joy
-- Joe McCabe (Techy Fella at Netscape)

Dorai Sitaram

unread,
Mar 1, 2002, 10:32:13 AM3/1/02
to
In article <3C7EEA01...@alcyone.com>,

Erik Max Francis <m...@alcyone.com> wrote:
>There are two equivalent ways to define a procedure in scheme:
>
> (define f (lambda (args...) body...)
>
>and, as a shortcut,
>
> (define (f args...) body....)
>
>_The Scheme programming language_ suggests that the former style is
>preferred, since the latter is only syntactic sugar. But there's
>nothing inherently wrong with syntactic sugar (after all, most language
>constructs are "only syntactic sugar," but they make things a great deal
>more clear). Is there a general consensus on the choice of style? Is
>the former style _really_ commonly preferred over the latter, or was the
>preference indicated in the book a minority?
>
>Thanks.

I prefer the first syntax as it is uniform, ie, it
doesn't care if the value is a procedure or not.
Procedures are different from other values, but only in
that they can be applied, and emphasizing this
difference in contexts where it is not material
is something I don't subjectively like.

Another reason I prefer the first syntax is that the
second syntax isn't usable for all procedure
definitions anyway! The moment you introduce a
lexical contour around the defined procedure you have
to scurry back to the first syntax, eg,

(define foo
(let ((baz 0))
(lambda (y)
etc...)))

All subjective of course. Some of my best friends use
the second syntax. But I wish they'd stop. :-)

--d

Kevin Holmes

unread,
Mar 2, 2002, 1:05:08 AM3/2/02
to
I have come across a situation that follows that pattern. What is the
difference between

(define foo
(let ((baz 0))
(lambda (y)
etc...)))

and

(define foo
(lambda (y)
(let ((baz 0))
etc...)))

?

Best regards,
Kevin Holmes

Erik Max Francis

unread,
Mar 2, 2002, 1:27:57 AM3/2/02
to
Kevin Holmes wrote:

> I have come across a situation that follows that pattern. What is the
> difference between
>
> (define foo
> (let ((baz 0))
> (lambda (y)
> etc...)))
>
> and
>
> (define foo
> (lambda (y)
> (let ((baz 0))
> etc...)))
>
> ?

I'm pretty sure there's no difference, especially considering that let
is itself syntactic sugar for a lambda in the first place.

Jeffrey M. Vinocur

unread,
Mar 2, 2002, 2:03:18 AM3/2/02
to
In article <3C8070ED...@alcyone.com>,

Erik Max Francis <m...@alcyone.com> wrote:
>>
>> (define foo
>> (let ((baz 0))
>> (lambda (y)
>> etc...)))
>>
>> (define foo
>> (lambda (y)
>> (let ((baz 0))
>> etc...)))
>
>I'm pretty sure there's no difference,

Mmm, consider if there is a (set! baz ...) in that etc someplace.
For example:

(define foo
(let ((baz 0))
(lambda (y)

(set! baz (+ baz 1))
baz)))

(define foo
(lambda (y)
(let ((baz 0))

(set! baz (+ baz 1))
baz)))


>especially considering that let
>is itself syntactic sugar for a lambda in the first place.

Let is syntactic sugar for a lambda being applied to an
expression, not for a lambda abstraction.


--
Jeffrey M. Vinocur * jm...@cornell.edu
http://www.people.cornell.edu/pages/jmv16/

Jeffrey Siegal

unread,
Mar 2, 2002, 2:06:35 AM3/2/02
to
Kevin Holmes wrote:
> (define foo
> (let ((baz 0))
> (lambda (y)
> etc...)))
>
> and
>
> (define foo
> (lambda (y)
> (let ((baz 0))
> etc...)))

In the first version, there is a single binding of baz which is
referenced every time the foo is invoked. In the second version, there
is a new binding of baz established at each invocation. So, with the
first version, if foo set baz to 1 and then returned, then baz would be
1 on the next invocation, but with the second version, if foo set baz to
1 and then returned, baz would be 0 on the next invocation.

Jean-François Trevien

unread,
Mar 2, 2002, 4:55:50 AM3/2/02
to
>
> (define foo
> (let ((baz 0))
> (lambda (y)
> etc...)))
>
You can also write it:

(define foo
(let ((baz 0))
(define (foo-internal y) etc ...)
foo-internal))

Personally, I prefer the second form, because the definition look the same
that the use of it. And also it's a little shorter to write.

Matthias Blume

unread,
Mar 2, 2002, 7:25:02 AM3/2/02
to
On Sat, 02 Mar 2002 01:27:57 -0500, Erik Max Francis wrote:

> Kevin Holmes wrote:
>
>> I have come across a situation that follows that pattern. What is the
>> difference between
>>
>> (define foo
>> (let ((baz 0))
>> (lambda (y)
>> etc...)))
>>
>> and
>>
>> (define foo
>> (lambda (y)
>> (let ((baz 0))
>> etc...)))
>>
>> ?
>
> I'm pretty sure there's no difference, especially considering that let
> is itself syntactic sugar for a lambda in the first place.

You are wrong. There is a significant difference. Whether or not it
can be observerd depends on what the "..." is.

Erik Max Francis

unread,
Mar 2, 2002, 2:59:38 PM3/2/02
to
Matthias Blume wrote:

> You are wrong. There is a significant difference. Whether or not it
> can be observerd depends on what the "..." is.

Yes, I realized and cancelled the original article.

Dorai Sitaram

unread,
Mar 4, 2002, 6:04:50 PM3/4/02
to
In article <okfr8n4...@bellsouth.net>,

David Rush <ku...@bellsouth.net> wrote:
>
>I originally preferred (define x (lambda ...)) until I realized that
>Bigloo gave me better type-checking (arity-checking really) with
>(define (x) ...).

Isn't it a purely syntactic check for Bigloo to
determine that (define x (lambda (arg ...) ...))
is equivalent to (define (x arg ...) ...) and can
therefore be checked the same way?

--d

David Rush

unread,
Mar 4, 2002, 9:48:16 PM3/4/02
to

One would think so, but this also concerned interactions with bigloo's
module system (around version 2.1). I simply observed that the
checking at module boundaries was more robust with the (define (foo
...) ...) shorthand.

david rush
--
but we mus' move by Jah love, each one
-- Maelcum, in _Neuromancer_

Manuel Serrano

unread,
Mar 5, 2002, 3:04:06 AM3/5/02
to
> > >I originally preferred (define x (lambda ...)) until I realized that
> > >Bigloo gave me better type-checking (arity-checking really) with
> > >(define (x) ...).
> >
> > Isn't it a purely syntactic check for Bigloo to
> > determine that (define x (lambda (arg ...) ...))
> > is equivalent to (define (x arg ...) ...) and can
> > therefore be checked the same way?
>
> One would think so, but this also concerned interactions with bigloo's
> module system (around version 2.1). I simply observed that the
> checking at module boundaries was more robust with the (define (foo
> ...) ...) shorthand.
For Bigloo

(define f (lambda (arg ...) ...))

is strictly equivalent to

(define (f arg ...) ...)

In particular both forms are type checked similarly (actually a early
stage of the compile rewrites the first form into the second).

The only concern is about module exportation. If F is exported using a
clause such as:

(export (f arg ...))

Then F is *constant*, bound to a function. Thus the compiler can inline it,
type checked it easily, etc.

If F is exported as:

(export f)

Then F is a *variable*, that can be modified from somewhere else (i.e. another
module) and then is compiler will not be able to do a good job.

--
Manuel

Bob

unread,
Mar 2, 2002, 9:41:05 PM3/2/02
to
The "Big" difference has to do with the order of operations.

"Matthias Blume" <matt...@shimizu-blume.com> wrote in message
news:pan.2002.03.02.07...@shimizu-blume.com...

Matthias Blume

unread,
Mar 6, 2002, 10:00:00 AM3/6/02
to
On Sat, 02 Mar 2002 21:41:05 -0500, Bob wrote:

> The "Big" difference has to do with the order of operations.

Wrong.

Matthias

Bob

unread,
Mar 6, 2002, 12:35:26 PM3/6/02
to
Nope.

Bob


"Matthias Blume" <matt...@shimizu-blume.com> wrote in message

news:pan.2002.03.06.09....@shimizu-blume.com...

Bruce Lewis

unread,
Mar 7, 2002, 9:23:03 AM3/7/02
to
Matthias Blume <matt...@shimizu-blume.com> writes:

> On Sat, 02 Mar 2002 21:41:05 -0500, Bob wrote:
>
> > The "Big" difference has to do with the order of operations.
>
> Wrong.

Kevin Holmes is either not doing homework, or doing a reasonable job of
impersonating someone not taking a course involving Scheme, based on
recent posts. I don't think we need to use cryptic hints and one-word
answers in discussing his question.

Bob is somewhat right in that the difference has to do with whether baz
is bound before or after the function is defined. However, he's wrong
in that the important difference isn't before/after, it's once/many. In
one form, baz is initialized to 0 once. In the other form, baz is
initialized to 0 every time the procedure is applied. With no mutation,
the two forms yield the same results. Introduce mutation and you get a
difference:

(define p1
(let ((baz 0))
(lambda ()


(set! baz (+ baz 1))
baz)))

(define p2
(lambda ()


(let ((baz 0))
(set! baz (+ baz 1))
baz)))

Apply each several times and note the different behavior.

Michael Sperber [Mr. Preprocessor]

unread,
Mar 7, 2002, 12:57:02 PM3/7/02
to
>>>>> "brl" == Bruce Lewis <brl...@yahoo.com> writes:

brl> Bob is somewhat right in that the difference has to do with whether baz
brl> is bound before or after the function is defined. However, he's wrong
brl> in that the important difference isn't before/after, it's
brl> once/many.

At the risk of being anal-retentive, the difference is "one/many", not
"once/many":

brl> In one form, baz is initialized to 0 once. In the other form,
brl> baz is initialized to 0 every time the procedure is applied.

In one form, BAZ is *bound* once, to 0. In the other form, BAZ is
bound to 0 every time the procedure is applied. Therefore, in the
other form, there are many bindings for BAZ, whereas there's only one
in the first example.

brl> With no mutation, the two forms yield the same results.
brl> Introduce mutation and you get a difference:

brl> (define p1
brl> (let ((baz 0))
brl> (lambda ()
brl> (set! baz (+ baz 1))
brl> baz)))

brl> (define p2
brl> (lambda ()
brl> (let ((baz 0))
brl> (set! baz (+ baz 1))
brl> baz)))

You can't observe the difference in this specific example, but you can
observe it here:

(define p3


(lambda ()
(let ((baz 0))

(lambda ()
(set! baz (+ baz 1))

baz))))

--
Cheers =8-} Mike
Friede, Völkerverständigung und überhaupt blabla

Bruce Lewis

unread,
Mar 7, 2002, 2:17:00 PM3/7/02
to
spe...@informatik.uni-tuebingen.de (Michael Sperber [Mr. Preprocessor]) writes:
> >>>>> "brl" == Bruce Lewis <brl...@yahoo.com> writes:
>
> brl> (define p1
> brl> (let ((baz 0))
> brl> (lambda ()
> brl> (set! baz (+ baz 1))
> brl> baz)))
>
> brl> (define p2
> brl> (lambda ()
> brl> (let ((baz 0))
> brl> (set! baz (+ baz 1))
> brl> baz)))
>
> You can't observe the difference in this specific example, but you can
> observe it here:

Why doesn't this illustrate the difference?

Welcome to MzScheme version 103, Copyright (c) 1995-2000 PLT (Matthew Flatt)
> (load "/home/blewis/2002/bind.scm")
> (p1)
1
> (p1)
2
> (p1)
3
> (p2)
1
> (p2)
1
> (p2)
1

Michael Sperber [Mr. Preprocessor]

unread,
Mar 8, 2002, 3:05:02 AM3/8/02
to
>>>>> "brl" == Bruce Lewis <brl...@yahoo.com> writes:

brl> spe...@informatik.uni-tuebingen.de (Michael Sperber [Mr. Preprocessor]) writes:
>> >>>>> "brl" == Bruce Lewis <brl...@yahoo.com> writes:
>>
brl> (define p1
brl> (let ((baz 0))
brl> (lambda ()
brl> (set! baz (+ baz 1))
brl> baz)))

brl> (define p2
brl> (lambda ()
brl> (let ((baz 0))
brl> (set! baz (+ baz 1))
brl> baz)))
>>
>> You can't observe the difference in this specific example, but you can
>> observe it here:

>> (define p3


>> (lambda ()
>> (let ((baz 0))
>> (lambda ()

>> (set! baz (+ baz 1))

>> baz))))

brl> Why doesn't this illustrate the difference?

brl> Welcome to MzScheme version 103, Copyright (c) 1995-2000 PLT (Matthew Flatt)
>> (load "/home/blewis/2002/bind.scm")
>> (p1)
brl> 1
>> (p1)
brl> 2
>> (p1)
brl> 3
>> (p2)
brl> 1
>> (p2)
brl> 1
>> (p2)
brl> 1

Because there's only one live binding of BAZ at any one time, even in
P2. Therefore, there's not really an observable difference between
re-initialization (i.e. assignment) and binding. Here's an
alternative version of P2 with identical behavior:

(define p2


(let ((baz 0))
(lambda ()

(set! baz 0)


(set! baz (+ baz 1))

baz)))

Matthias Blume

unread,
Mar 8, 2002, 7:14:15 AM3/8/02
to
On Fri, 08 Mar 2002 03:05:02 -0500, Michael Sperber [Mr. Preprocessor]
wrote:

I don't understand what you are trying to get at here. The original
poster showed two Scheme programs, each with a hole. The question was
whether these two programs with holes are equivalent (which usually means
that they exhibit equivalent behavior no matter how you fill the hole and
no matter in which context you put them as long as you do the same to both).

So we had:

(define p1 (let ((baz 0)) (lambda (y) <HOLE>)))

and

(define p2 (lambda (y) (let ((baz 0)) <HOLE>)))

With this, if you fill the hole using

<HOLE> ::= (set! baz (+ baz 1)) baz

and put them into a context that repeatedly calls p1 or p2, respectively,
then you *do* observe a difference. Therefore, the two are not
equivalent. QED.

In other words, it does not matter whether one can find two different
things to put into <HOLE>, one for p1 and one for p2, that make the two
equivalent.

Matthias

Michael Sperber [Mr. Preprocessor]

unread,
Mar 8, 2002, 8:52:48 AM3/8/02
to
>>>>> "Matthias" == Matthias Blume <matt...@shimizu-blume.com> writes:

Matthias> I don't understand what you are trying to get at here. The original
Matthias> poster showed two Scheme programs, each with a hole. The question was
Matthias> whether these two programs with holes are equivalent (which usually means
Matthias> that they exhibit equivalent behavior no matter how you fill the hole and
Matthias> no matter in which context you put them as long as you do the same to both).

I was replying to brl's explanation of the phenomenon (which was not
exactly correct), not the original poster. You can see the difference
between his explanation and my explanation (not between p1 and p2)
with the p3 I provided. I probably expressed myself poorly.

Per Bothner

unread,
Mar 9, 2002, 12:59:45 PM3/9/02
to
Erik Max Francis wrote:
> There are two equivalent ways to define a procedure in scheme:
>
> (define f (lambda (args...) body...)
>
> and, as a shortcut,
>
> (define (f args...) body....)

I suggest, as a matter of style, using the latter when 'f' is viewed
as constant - i.e. you're not expected to re-assign it. Use the
former when is really a variable - e.g. a "hook" variable in the
Emacs sense.

Kawa uses this distinction as a hint as to whether 'f' can be
inlined. It can also be used as a hint whether a recursive
call to 'f' should be viewed as self-tail-recursion, which is
useful when compiling in a mode or compiler that does not
support full general tail-call-elimination.

Yes, this isn't pedantically correct, but it works pretyy well.
You don't need this convention when doing whole-program
compilation of course, but it is useful when doing separate
compilation. (One of these days I'll implement a --pedantic flag
to turn off not-100%-safe inlining and other optimizations.)
--
--Per Bothner
p...@bothner.com http://www.bothner.com/per/

0 new messages