Merge lp:~rotty/scheme-libraries/srfi into lp:~scheme-libraries-team/scheme-libraries/srfi

Proposed by Andreas Rottmann
Status: Merged
Approved by: Derick Eddington
Approved revision: 94
Merged at revision: 94
Proposed branch: lp:~rotty/scheme-libraries/srfi
Merge into: lp:~scheme-libraries-team/scheme-libraries/srfi
Diff against target: 386 lines (+337/-1)
6 files modified
%3a45.sls (+10/-0)
%3a45/lazy.sls (+71/-0)
README (+1/-0)
compile-all.ikarus.sps (+2/-0)
private/registry.sls (+1/-1)
tests/lazy.sps (+252/-0)
To merge this branch: bzr merge lp:~rotty/scheme-libraries/srfi
Reviewer Review Type Date Requested Status
Derick Eddington Approve
Review via email: mp+25709@code.launchpad.net

Description of the change

Added SRFI 45 (Primitives for Expressing Iterative Lazy Algorithms)

I think I've now addressed all the issues pointed out by Derick in my first attempt.

To post a comment you must log in.
Revision history for this message
Derick Eddington (derick-eddington) wrote :

Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '%3a45'
2=== added file '%3a45.sls'
3--- %3a45.sls 1970-01-01 00:00:00 +0000
4+++ %3a45.sls 2010-05-20 18:00:56 +0000
5@@ -0,0 +1,10 @@
6+#!r6rs
7+;; Automatically generated by private/make-aliased-libraries.sps
8+(library (srfi :45)
9+ (export
10+ delay
11+ eager
12+ force
13+ lazy)
14+ (import (srfi :45 lazy))
15+)
16
17=== added file '%3a45/lazy.sls'
18--- %3a45/lazy.sls 1970-01-01 00:00:00 +0000
19+++ %3a45/lazy.sls 2010-05-20 18:00:56 +0000
20@@ -0,0 +1,71 @@
21+#!r6rs
22+;; lazy.sls -- SRFI 45 (Primitives for Expressing Iterative Lazy Algorithms)
23+
24+;; Copyright (C) André van Tonder (2003). All Rights Reserved.
25+
26+;; Permission is hereby granted, free of charge, to any person
27+;; obtaining a copy of this software and associated documentation
28+;; files (the "Software"), to deal in the Software without
29+;; restriction, including without limitation the rights to use, copy,
30+;; modify, merge, publish, distribute, sublicense, and/or sell copies
31+;; of the Software, and to permit persons to whom the Software is
32+;; furnished to do so, subject to the following conditions:
33+
34+;; The above copyright notice and this permission notice shall be
35+;; included in all copies or substantial portions of the Software.
36+
37+;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38+;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39+;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
40+;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
41+;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
42+;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
43+;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
44+;; SOFTWARE.
45+
46+(library (srfi :45 lazy)
47+ (export delay
48+ lazy
49+ force
50+ eager)
51+ (import (rnrs base)
52+ (rnrs records syntactic))
53+
54+ (define-record-type promise
55+ (fields (mutable val)))
56+
57+ (define-record-type value
58+ (fields (mutable tag)
59+ (mutable proc)))
60+
61+ (define-syntax lazy
62+ (syntax-rules ()
63+ ((lazy exp)
64+ (make-promise (make-value 'lazy (lambda () exp))))))
65+
66+ (define (eager x)
67+ (make-promise (make-value 'eager x)))
68+
69+ (define-syntax delay
70+ (syntax-rules ()
71+ ((delay exp) (lazy (eager exp)))))
72+
73+ (define (force promise)
74+ (let ((content (promise-val promise)))
75+ (case (value-tag content)
76+ ((eager) (value-proc content))
77+ ((lazy) (let* ((promise* ((value-proc content)))
78+ (content (promise-val promise))) ; *
79+ (if (not (eqv? (value-tag content) 'eager)) ; *
80+ (begin (value-tag-set! content
81+ (value-tag (promise-val promise*)))
82+ (value-proc-set! content
83+ (value-proc (promise-val promise*)))
84+ (promise-val-set! promise* content)))
85+ (force promise))))))
86+
87+ ;; (*) These two lines re-fetch and check the original promise in case
88+ ;; the first line of the let* caused it to be forced. For an example
89+ ;; where this happens, see reentrancy test 3 below.
90+
91+ )
92
93=== modified file 'README'
94--- README 2010-03-05 08:11:29 +0000
95+++ README 2010-05-20 18:00:56 +0000
96@@ -32,6 +32,7 @@
97 (srfi :41 streams)
98 (srfi :42 eager-comprehensions)
99 (srfi :43 vectors)
100+ (srfi :45 lazy)
101 (srfi :48 intermediate-format-strings)
102 (srfi :61 cond)
103 (srfi :64 testing)
104
105=== modified file 'compile-all.ikarus.sps'
106--- compile-all.ikarus.sps 2010-03-05 08:11:29 +0000
107+++ compile-all.ikarus.sps 2010-05-20 18:00:56 +0000
108@@ -47,6 +47,8 @@
109 (only (srfi :42 eager-comprehensions))
110 (only (srfi :43))
111 (only (srfi :43 vectors))
112+ (only (srfi :45))
113+ (only (srfi :43 lazy))
114 (only (srfi :48))
115 (only (srfi :48 intermediate-format-strings))
116 (only (srfi :48 intermediate-format-strings compat))
117
118=== modified file 'private/registry.sls'
119--- private/registry.sls 2010-03-05 08:11:29 +0000
120+++ private/registry.sls 2010-05-20 18:00:56 +0000
121@@ -44,7 +44,7 @@
122 (42 eager-comprehensions)
123 (43 vectors)
124 #;(44 collections)
125- #;(45 lazy)
126+ (45 lazy)
127 #;(46 syntax-rules)
128 #;(47 arrays)
129 (48 intermediate-format-strings)
130
131=== added file 'tests/lazy.sps'
132--- tests/lazy.sps 1970-01-01 00:00:00 +0000
133+++ tests/lazy.sps 2010-05-20 18:00:56 +0000
134@@ -0,0 +1,252 @@
135+#!r6rs
136+;; Copyright (C) André van Tonder (2003, 2010). All Rights Reserved.
137+
138+;; Permission is hereby granted, free of charge, to any person
139+;; obtaining a copy of this software and associated documentation
140+;; files (the "Software"), to deal in the Software without
141+;; restriction, including without limitation the rights to use, copy,
142+;; modify, merge, publish, distribute, sublicense, and/or sell copies
143+;; of the Software, and to permit persons to whom the Software is
144+;; furnished to do so, subject to the following conditions:
145+
146+;; The above copyright notice and this permission notice shall be
147+;; included in all copies or substantial portions of the Software.
148+
149+;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
150+;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
151+;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
152+;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
153+;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
154+;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
155+;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
156+;; SOFTWARE.
157+
158+;=========================================================================
159+; TESTS AND BENCHMARKS:
160+;=========================================================================
161+
162+(import (rnrs)
163+ (only (rnrs r5rs) modulo)
164+ (srfi :64 testing)
165+ (srfi :45 lazy))
166+
167+(define-syntax test-output
168+ (syntax-rules ()
169+ ((_ expected proc)
170+ (test-equal expected
171+ (call-with-string-output-port proc)))))
172+
173+(define (test-leak thunk)
174+ (display
175+ "Leak test, please watch memory consumption; press C-c when satisfied\n")
176+ (guard (c (#t 'aborted))
177+ (thunk)))
178+
179+(test-begin "lazy-tests")
180+
181+;=========================================================================
182+; Memoization test 1:
183+
184+(test-output "hello"
185+ (lambda (port)
186+ (define s (delay (begin (display 'hello port) 1)))
187+ (test-equal 1 (force s))
188+ (force s)))
189+
190+;=========================================================================
191+; Memoization test 2:
192+
193+(test-output "bonjour"
194+ (lambda (port)
195+ (let ((s (delay (begin (display 'bonjour port) 2))))
196+ (test-equal 4 (+ (force s) (force s))))))
197+
198+;=========================================================================
199+; Memoization test 3: (pointed out by Alejandro Forero Cuervo)
200+
201+(test-output "hi"
202+ (lambda (port)
203+ (define r (delay (begin (display 'hi port) 1)))
204+ (define s (lazy r))
205+ (define t (lazy s))
206+ (test-equal 1 (force t))
207+ (test-equal 1 (force r))))
208+
209+;=========================================================================
210+; Memoization test 4: Stream memoization
211+
212+(define (stream-drop s index)
213+ (lazy
214+ (if (zero? index)
215+ s
216+ (stream-drop (cdr (force s)) (- index 1)))))
217+
218+(define (ones port)
219+ (delay (begin
220+ (display 'ho port)
221+ (cons 1 (ones port)))))
222+
223+(test-output "hohohohoho"
224+ (lambda (port)
225+ (define s (ones port))
226+ (test-equal 1
227+ (car (force (stream-drop s 4))))
228+ (test-equal 1
229+ (car (force (stream-drop s 4))))))
230+
231+;=========================================================================
232+; Reentrancy test 1: from R5RS
233+
234+(letrec ((count 0)
235+ (p (delay (begin (set! count (+ count 1))
236+ (if (> count x)
237+ count
238+ (force p)))))
239+ (x 5))
240+ (test-equal 6 (force p))
241+ (set! x 10)
242+ (test-equal 6 (force p)))
243+
244+;=========================================================================
245+; Reentrancy test 2: from SRFI 40
246+
247+(letrec ((f (let ((first? #t))
248+ (delay
249+ (if first?
250+ (begin
251+ (set! first? #f)
252+ (force f))
253+ 'second)))))
254+ (test-equal 'second (force f)))
255+
256+;=========================================================================
257+; Reentrancy test 3: due to John Shutt
258+
259+(let* ((q (let ((count 5))
260+ (define (get-count) count)
261+ (define p (delay (if (<= count 0)
262+ count
263+ (begin (set! count (- count 1))
264+ (force p)
265+ (set! count (+ count 2))
266+ count))))
267+ (list get-count p)))
268+ (get-count (car q))
269+ (p (cadr q)))
270+
271+ (test-equal 5 (get-count))
272+ (test-equal 0 (force p))
273+ (test-equal 10 (get-count)))
274+
275+;=========================================================================
276+; Test leaks: All the leak tests should run in bounded space.
277+
278+;=========================================================================
279+; Leak test 1: Infinite loop in bounded space.
280+
281+(define (loop) (lazy (loop)))
282+(test-leak (lambda () (force (loop)))) ;==> bounded space
283+
284+;=========================================================================
285+; Leak test 2: Pending memos should not accumulate
286+; in shared structures.
287+
288+(let ()
289+ (define s (loop))
290+ (test-leak (lambda () (force s)))) ;==> bounded space
291+
292+;=========================================================================
293+; Leak test 3: Safely traversing infinite stream.
294+
295+(define (from n)
296+ (delay (cons n (from (+ n 1)))))
297+
298+(define (traverse s)
299+ (lazy (traverse (cdr (force s)))))
300+
301+(test-leak (lambda () (force (traverse (from 0))))) ;==> bounded space
302+
303+;=========================================================================
304+; Leak test 4: Safely traversing infinite stream
305+; while pointer to head of result exists.
306+
307+(let ()
308+ (define s (traverse (from 0)))
309+ (test-leak (lambda () (force s)))) ;==> bounded space
310+
311+;=========================================================================
312+; Convenient list deconstructor used below.
313+
314+(define-syntax match
315+ (syntax-rules ()
316+ ((match exp
317+ (() exp1)
318+ ((h . t) exp2))
319+ (let ((lst exp))
320+ (cond ((null? lst) exp1)
321+ ((pair? lst) (let ((h (car lst))
322+ (t (cdr lst)))
323+ exp2))
324+ (else 'match-error))))))
325+
326+;========================================================================
327+; Leak test 5: Naive stream-filter should run in bounded space.
328+; Simplest case.
329+
330+(define (stream-filter p? s)
331+ (lazy (match (force s)
332+ (() (delay '()))
333+ ((h . t) (if (p? h)
334+ (delay (cons h (stream-filter p? t)))
335+ (stream-filter p? t))))))
336+
337+(test-leak
338+ (lambda ()
339+ (force (stream-filter (lambda (n) (= n 10000000000))
340+ (from 0)))))
341+ ;==> bounded space
342+
343+;========================================================================
344+; Leak test 6: Another long traversal should run in bounded space.
345+
346+; The stream-ref procedure below does not strictly need to be lazy.
347+; It is defined lazy for the purpose of testing safe compostion of
348+; lazy procedures in the times3 benchmark below (previous
349+; candidate solutions had failed this).
350+
351+(define (stream-ref s index)
352+ (lazy
353+ (match (force s)
354+ (() 'error)
355+ ((h . t) (if (zero? index)
356+ (delay h)
357+ (stream-ref t (- index 1)))))))
358+
359+; Check that evenness is correctly implemented - should terminate:
360+
361+(test-equal 0
362+ (force (stream-ref (stream-filter zero? (from 0))
363+ 0)))
364+
365+(let ()
366+ (define s (stream-ref (from 0) 100000000))
367+ (test-leak (lambda () (force s)))) ;==> bounded space
368+
369+;======================================================================
370+; Leak test 7: Infamous example from SRFI 40.
371+
372+(define (times3 n)
373+ (stream-ref (stream-filter
374+ (lambda (x) (zero? (modulo x n)))
375+ (from 0))
376+ 3))
377+
378+(force (times3 7))
379+(test-leak (lambda ()
380+ (force (times3 100000000)))) ;==> bounded space
381+
382+(test-end "lazy-tests")
383+
384+;; Local Variables:
385+;; scheme-indent-styles: ((test-output 1) (test-equal 1))
386+;; End:

Subscribers

People subscribed via source and target branches

to status/vote changes: