Reputation: 3381
Racket provides a language extension for lazy evaluation that simply requires a #lang lazy
header in the beginning of some file to change the evaluation strategy throughout the file.
However, this standard approach allows only a course level of granularity; A finer approach is needed to confine lazy evaluation to some defined scope.
Does Racket provide some standard approach to confining a lazy evaluation strategy to some scope? I'm looking for something like the following:
(eval-lazy (+ 2 2)) ;; only available by explicit or implicit call-by-need
Upvotes: 0
Views: 1892
Reputation: 48745
#lang lazy
is a completely different language than #lang racket
. Yes, you can mix and match some parts, but it can get messy dealing with lazy values from #lang lazy
in a #lang racket
module.
Every scheme, thus also racket, has delay
and force
:
(define promise (delay (+ 2 2)))
(force promise) ; ==> 4
However that is just syntax sugar anyway since you can do it like this:
(define promise (thunk (+ 2 2)))
(promise) ; ==> 4
And of course thunk
is just syntax sugar for anonymous function with no arguments:
(define promise (lambda () (+ 2 2)))
(promise) ; ==> 4
Now delay
actually is slightly more complex since if you call these functions it will run the expression every time. To prevent that we memoize the result. Here I'll implement your eval-lazy
as a macro:
(define ^not-calculated (list #f)) ; unique value
(define-syntax eval-lazy
(syntax-rules ()
((_ expression)
(let ((value ^not-calculated))
(lambda ()
(when (eq? ^not-calculated value)
(set! value expression))
value)))))
As with the thunk
and lambda
just calling the result will force it.
Streams
Racket provides a stream library which can be used to do 90% of all algorithms in a way where you do abstract each step as it's own process where you can compose them together without getting the penalty of lists. Streams are just delayed values in cons
popularized in SICP
Upvotes: 3