Reputation: 27
I was working on this bit of code
(letrec ([f (lambda (x)
(if (= (remainder x 5) 0)
(cons (- x) (lambda () (f (+ x 1))))
(cons x (lambda () (f (+ x 1))))))])
(lambda () (f 1))))
And I thought, why use a letrec and an anoymous function? Could I not use a local and a define - something like this?
(local [(define (f x) (if....]))
Is there any particular reason to use anymous functions instead of local-defines?
Upvotes: 0
Views: 1055
Reputation: 48745
Everytime you see (define (something ...) ..)
it is rewritten (define something (lambda (...) ...))
by the language. It is just syntax sugar to make it easier to write procedures.
local
is not inherited by Scheme. In fact it is more specific syntax to do the same as just placing define
in the procedure body:
;;; standard Scheme letrec
(define (reverse lst)
(letrec ([helper (lamda (lst acc)
(if (null lst)
acc
(helper (cdr lst) (cons (car lst) acc))))])
(helper lst '()))
;;; Standard scheme local define
(define (reverse lst)
(define (helper (lst acc)
(if (null lst)
acc
(helper (cdr lst) (cons (car lst) acc))))
(helper lst '()))
;;; Implementation specific Racket way
(define (reverse lst)
(local [(define (helper (lst acc)
(if (null lst)
acc
(helper (cdr lst) (cons (car lst) acc))))]
(helper lst '()))
All these are equal. In fact the standard Scheme report explains that the local define
can be implemented by rewriting it to a letrec
or vice versa.
The local
version is unique to Racket language and is not a part of Scheme. Eg. if you are writing code that should work across implementations this cannot be used. As for your question since standard Scheme has both define
and letrec
in its base language they are the same thing.
That let
and friends doesn't have a sugar for procedures are just unluck. By the time Scheme was made they probably didn't have the short form yet and when they made it there was no way to mend let
and friends. If you don't like lambda
use define
(or local
)
Upvotes: 3
Reputation: 6502
The full answer to your question is going to be somewhat complicated, because it involves multiple languages and their histories.
But in short, you can use local
instead, nothing is wrong with that. You can also use letrec
. Arguably, it's just a stylistic choice which one you prefer to use.
In the beginning, there's a language Scheme. Racket was a Scheme implementation, before it becomes its own language. Then, there are student languages for HtDP in Racket. These three languages are similar, but subtly different in many ways.
I assume that you are using the student languages from HtDP, which have the restriction that the body of functions must consist of exactly one "thing". For example, the following is invalid, because the body has two "things" in it.
(define (f x)
1
2)
This is why local
is useful: it allows you to define local definitions, while itself counts as only one "thing".
(define (compute-fact-x-plus-one x)
(local [(define (fact n)
(if (zero? n)
1
(* n (fact (sub1 n)))))]
(add1 (fact x))))
In the real Racket language, there is no such restriction. You are able to write:
(define (compute-fact-x-plus-one x)
(define (fact n)
(if (zero? n)
1
(* n (fact (sub1 n)))))
(add1 (fact x)))
Therefore, local
is not as useful in the real Racket language. In fact, it's not provided by the language by default. You need to specifically import it from an extra library, and I think most people don't even know that it exists!
local
doesn't exist in Scheme. People would prefer to use letrec
instead. So in old Racket code (when it was just a Scheme implementation), you will see that people use letrec
a lot too. In modern Racket, letrec
is less frequently used, because it's more verbose than a series of define
s.
But that's not all. If we look at the implementation of languages, all series of define
s (in modern Racket) and local
(in student languages) are eventually converted to letrec
internally.
In summary:
local
is in practice only popular in HtDP student languagesdefine
directly in modern Racket code.letrec
is traditionally used a lot, but it's less frequently used directly in modern Racket. It's still used all the time as the internal target construct that local
and a series of define
s are converted to.I could actually write more on this topic.
define
across versions are different. And even in the same version, the behaviors of define
across implementations are also different.letrec
can be seen as "superior" to a series of define
because you can control the scope of variables it introduces.But I think this answer is long enough already.
Upvotes: 3