Search code examples
schemelazy-evaluationguile

How to use Lazy Evaluation in Guile scheme?


I have written a code which uses lazy evaluation to produce infinite data structure but there is an error.

Here is the code:

#!/usr/bin/guile \
-e main -s
!#
(define ints-f 
    (lambda (n) 
        (let ((n1 (delay (ints-f (+ n 1)))))
        (cons n (force n1)))))
(define (main args)
    (display (car (ints-f 3) ))
    (newline)
    )

This gives an error which says stack overflow. Which means that force is being executed even if not called. How to rectify this?

#!/usr/bin/guile \
-e main -s
!#
(define ints-f 
    (lambda (n) 
        (let ((n1 (delay (ints-f (+ n 1)))))
        (cons n n1))))
(define (main args)
    (display (car (ints-f 3) ))
    (newline)
    )

The above code is giving the expected output of 3 but if I use cdr as in the code below

#!/usr/bin/guile \
-e main -s
!#
(define ints-f 
    (lambda (n) 
        (let ((n1 (delay (ints-f (+ n 1)))))
        (cons n n1))))
(define (main args)
    (display (cdr (ints-f 3) ))
    (newline)
    )

It prints a promise object.

How to use lazy evaluation in guile scheme?


Solution

  • The first snippet is incorrect, you're forcing the value when building the sequence, hence defeating the whole purpose of lazy evaluation. On the other hand, the second snippet looks right - although it can be simplified a bit:

    (define (ints-f n)
      (cons n (delay (ints-f (+ n 1)))))
    

    It's normal to get a promise when calling cdr, it's a thunk built with delay that will only yield its value when forced. In fact, if you want to peek into the elements of an infinite sequence, you'll have to use a procedure for traversing the part you're interested in:

    (define (take seq n)
      (if (= n 0)
          '()
          (cons (car seq)
                (take (force (cdr seq)) (- n 1)))))
    

    Similarly:

    (define (get seq idx)
      (if (= idx 0)
          (car seq)
          (get (force (cdr seq)) (- idx 1))))
    

    For example:

    (take (ints-f 5) 5)
    => '(5 6 7 8 9)
    
    (get (ints-f 5) 5)
    => 10