This page lists all known errors and their corrections for the first printing of the book. The errors in the most recent, fourth printing, are given here and the errors in the second and third printings are given here. If you find any new errors, please notify one of the authors. We use the following notation for the corrections: positive line numbers start counting from the top of the text body (including section titles); negative line numbers start counting from the bottom (including section titles and footnote text).
local Max in
local C in
Max=proc {$ ...} ... end
local A in
local B in
A=3
B=5
{Max ...}
end
end
end
end
The translation given in the book
does an additional source transformation that
lifts all the local statements to be outermost.
fun {Sequence NonTerm Sep S1 Sn}
fun {SequenceLoop Prefix S2 Sn}
case S2 of T|S3 andthen {Sep T} then Next S4 in
Next={NonTerm S3 S4}
{SequenceLoop T(Prefix Next) S4 Sn}
else
Sn=S2 Prefix
end
end
First S2
in
First={NonTerm S1 S2}
{SequenceLoop First S2 Sn}
end
local Leak in
{Unwrap fun {$ K} K=Leak unit end}
end
will bind Leak to the key, which compromises the encapsulation.
Data abstractions that use Unwrap are insecure
because the internal calls to Unwrap can be exploited
to leak the key.
A correct definition of NewWrapper is:
proc {NewWrapper ?Wrap ?Unwrap}
Key={NewName}
in
fun {Wrap X}
{Chunk.new w(Key:X)}
end
fun {Unwrap W}
try W.Key catch _ then raise error(unwrap(W)) end end
end
end
This uses a chunk, which is defined in Oz as a limited form of record
that only supports the selection operator ".".
This definition is secure because the W.Key operation cannot leak
the value of Key, no matter what W is.
Unlike what the book says on page 517 and in appendix B.4 (page 828),
chunks cannot be defined with procedure values and names.
Chunks are an additional concept that can be added to
the kernel language.
(Found by Tony Tanami Vågenes and Mark Miller.)
First, a read-only variable is created. Second, when the value of the read-only variable is needed (i.e., the program attempts to invoke a module operation), then the functor value is loaded from memory and called in a new thread with as arguments the modules it imports. This is called dynamic linking, as opposed to static linking in which the functor is installed when execution starts. Third, the functor call returns the new module and binds it to the variable.(Found by Gary T. Leavens.)
fun {Spawn P}
Id Ok in
thread
Id={Thread.this}
{Wait Ok}
{P}
end
{Thread.suspend Id}
Ok=unit
Id
end
proc {Resume Id}
Ok Me={Thread.this} in
thread
{Thread.suspend Me}
Ok=unit
{Thread.resume Id}
end
{Wait Ok}
end
(Found by Jeff Jackson.)
T(receive ... end Sin Sout) ≡
local
fun {Loop S T#E Sout}
if {IsDet S} then
case S of M|S1 then
case M
of T(Pattern1) then E=S1 T(Body1 T Sout)
...
[] T(PatternN) then E=S1 T(BodyN T Sout)
else E1 in E=M|E1 {Loop S1 T#E1 Sout} end
end
else E=S T(BodyT T Sout) end
end T
in
{Loop Sin T#T Sout}
end
The loop is needed since the message removed from the mailbox
is not necessarily the first.
fun {NewCollection}
S={NewStack}
proc {Put X} {S.push X} end
fun {Get} {S.pop} end
fun {IsEmpty} {S.isEmpty} end
in
collection(put:Put get:Get isEmpty:IsEmpty)
end
The definition in the book has several small errors.
(Found by Fred Spiessens.)
procedure sqr(a:integer);
begin
a:=a*a;
end;
var c:integer;
c:=25;
sqr(c);
browse(c); /* value of c unchanged */
with Oz translation:
proc {Sqr D}
A={NewCell D}
in
A:=@A*@A
end
local
C={NewCell 0}
in
C:=25
{Sqr @C}
{Browse @C} /* content of C unchanged */
end
This gives a more reasonable definition of sqr
and shows how to pass variables by value.
(Found by Robert Godfroid.)
meth kill(X S)
if @alive then
if S==1 then @last=@ident
elseif X mod @step==0 then
alive:=false
{@pred setSucc(@succ)}
{@succ setPred(@pred)}
{@succ kill(X+1 S-1)}
else
{@succ kill(X+1 S)}
end
else {@succ kill(X S)} end
end
This works because the FIFO property guarantees
that the setSucc and setPred
messages arrive at their destinations before the kill messages.
fun {GetLock}
if {Thread.this}\=@CurThr then Old New in
{Exchange Token1 Old New}
{Wait Old}
Token2:=New
CurThr:={Thread.this}
true
else false end
end
proc {LockM P}
if {L.get} then
try {P} finally {L.release} end
else {P} end
end
proc {WaitM}
X in
{Q.insert X} {L.release} {Wait X}
if {L.get} then skip end
end
The new definition of GetLock will return true
if it got the lock, and false otherwise.
(Found by Jeff Jackson.)
proc {Palindrome ?A}
B C X Y
in
A::0#9999 B::0#99 C::0#99
A=:B*C
X::1#9 Y::0#9
A=:X*1000+Y*100+Y*10+X
{FD.distribute ff [X Y B C]}
end
The new definition solves exactly the same problem as the relational
version in chapter 9 (page 629).
The old definition solves a slightly different problem.
(Found by Chris Rathman and Raphaël Collet.)
The arity of a record is the set of features of the record. The call {Arity R} executes as soon as R is bound to a record and returns the arity represented as a list of the features of the record sorted lexicographically.(Found by Waclaw Kusnierczyk.)