Bob McIsaac wrote:
The puzzling comment to my humble form said : "This code assumes that arguments to functions are evaluated left-to-right." But what of it? There is a stream of text to be evaluated. Choices are 1. left-right evaluation in one pass; 2. parsing the stream into tokens and sorting the tokens into a tree according to the rules of the grammar ... not Lispy. Choice 1 means typing parentheses so that the evaluator can be simple and recursive. ( I assume that evaluation results in a linked list)
Hm, we're not talking about nested expressions here. We're talking about multiple expressions given to one function. And at that point, regardless of whether your internal representation is lists or something else, the evaluator could be programmed to evaluate function arguments in different orders.
The problematic code is:
(bitwise-ior (bitwise-and (arithmetic-shift (read-u8 INP) 24) #xff000000) (bitwise-and (arithmetic-shift (read-u8 INP) 16) #xff0000) (bitwise-and (arithmetic-shift (read-u8 INP) 8) #xff00) (bitwise-and (read-u8 INP) #xff)))
It contains four (read-u8 INP) forms; the order in which they are evaluated matters (because reading from a port advances the position in the underlying stream as a side effect).
The problem is that those forms are indirectly positioned as arguments to bitwise-ior. bitwise-ior is a function. Scheme dictates that the arguments to functions are evaluated before the function is called (meaning before the body of the function definition is evaluated). So you're guaranteed that all four (read-u8 INP) forms have been evaluated before the bitwise-ior call. *But* Scheme does not specify in which order the four arguments of bitwise-ior are evaluated. So it's very well possible that some Scheme implementation first evaluates (bitwise-and (read-u8 INP) #xff)), then (bitwise-and (arithmetic-shift (read-u8 INP) 8) #xff00), then (bitwise-and (arithmetic-shift (read-u8 INP) 16) #xff0000), and at last (bitwise-and (arithmetic-shift (read-u8 INP) 24) #xff000000). Or the other way round. Or even in some other order or even in parallel. So the bytes coming from INP will end up at unspecified and possibly random places.
OTOH, special forms like let and let* do specify the order of evaluation (they are not functions). So writing
(let* ((a (bitwise-and (arithmetic-shift (read-u8 INP) 24) #xff000000)) (b (bitwise-and (arithmetic-shift (read-u8 INP) 16) #xff0000)) (c (bitwise-and (arithmetic-shift (read-u8 INP) 8) #xff00)) (d (bitwise-and (read-u8 INP) #xff))) (bitwise-ior a b c d))
makes the order of evaluation explicit (first a, then b, then c, then d) and thus portable (and deterministic).
Christian.