Subject: Re: bug#17825: broken set! in iteration

From: Mark H Weaver <[email-masked]>
Date: Sat, 21 Jun 2014 17:19:52 -0400

tags 17825 notabug
close 17825
thanks

Alírio Eyng <[email-masked]> writes:

> I can't see why the second code doesn't work like the first:
>
> (use-modules (srfi srfi-1))
> (define D '(((3 4))))
> (let ((r 1))
>        (set! D (append D '(())))
>        (display D)(newline)
>        (set-car! (drop D r) (car (drop D (- r 1)))))
> (let ((r 2))
>        (set! D (append D '(())))
>        (display D)(newline)
>        (set-car! (drop D r) (car (drop D (- r 1)))))
> output:
> (((3 4)) ())
> (((3 4)) ((3 4)) ())
>
> (use-modules (srfi srfi-1))
> (define D '(((3 4))))
> (map (lambda (r)
>        (set! D (append D '(())))
>        (display D)(newline)
>        (set-car! (drop D r) (car (drop D (- r 1)))))
>      '(1 2))
> output:
> (((3 4)) ())
> (((3 4)) ((3 4)) ((3 4)))

This code mutates literal lists, which is not allowed.  Specifically,
this code calls 'set-car!' on pairs that come from a literal list,
namely '(()).  The optimizer assumes that this will never happen, and
generates code based on that assumption.

So, you need to change '(()) to (list '()).  Also, if you will ever
mutate the pairs in '(((3 4))), you'll need to change that as well.

Another problem is that 'map' does not specify the order in which the
procedure is applied to the elements of the input list.  We have
'map-in-order', but both of those also build a list of the results,
which is not needed here.

Here, you should be using 'for-each', which guarantees the order of
iteration and does not build a list of the results.

The following code works as expected:

--8<---------------cut here---------------start------------->8---
(use-modules (srfi srfi-1))
(define D '(((3 4))))
(for-each (lambda (r)
            (set! D (append D (list '())))
            (display D)(newline)
            (set-car! (drop D r) (car (drop D (- r 1)))))
          '(1 2))
--8<---------------cut here---------------end--------------->8---

and outputs:

--8<---------------cut here---------------start------------->8---
(((3 4)) ())
(((3 4)) ((3 4)) ())
--8<---------------cut here---------------end--------------->8---

I'm closing this bug, but if you think there's still a bug here, feel
free to reopen it.

    Thanks!
      Mark