xref: /inferno-os/doc/limbo/addendum.ms (revision 46439007cf417cbd9ac8049bb4122c890097a0fa)
1.TL
2Addendum to
3.I "The Limbo Programming Language"
4.AU
5Vita Nuova
6.br
730 March 2005
8.NH 1
9Introduction
10.LP
11This addendum provides a brief summary of several language changes to
12Limbo since
13.I "The Limbo Programming Language"
14was last revised:
15.RS
16.IP •
17buffered channels
18.IP •
19unrestricted \f5alt\f1
20.IP •
21function references
22.IP •
23exceptions
24.IP •
25exponentiation
26.IP •
27fixed-point types
28.RE
29.NH 1
30Buffered channels
31.LP
32A buffered channel can now be declared:
33.P1
34c := chan[1] of int;
35.P2
36Here the buffer size is 1. A send on this channel will succeed immediately
37if there is a receiver waiting or if the buffer is empty. A receive on this
38channel will succeed immediately if there is a data item in the buffer. This allows us to
39write a very simple locking mechanism:
40.P1
41acquire(c: chan of int)
42{
43	c <-= 0;
44}
45
46release(c: chan of int)
47{
48	<-c;
49}
50
51new(): chan of int
52{
53	return chan[1] of int;
54}
55.P2
56The declaration
57.P1
58c := chan[0] of int;
59.P2
60is equivalent to
61.P1
62	c := chan of int;
63.P2
64An attempt to create a channel with a negative buffer size will raise
65an exception. An attempt to create a channel with a very large buffer
66may result in an immediate memory exception if there is not enough
67room for the buffer.
68.NH 1
69Unrestricted
70.B alt
71.LP
72The implementation has changed to remove the restriction that only one process can be
73waiting in an
74.CW alt
75to send or receive on a particular channel.
76The busy exception never occurs now. Thus you can do
77.P1
78i()
79{
80	c := chan of int;
81	c1 := chan of int;
82	spawn p(c, c1);
83	<-c;
84	spawn p(c, c1);
85	<-c;
86	for(i := 0; i < 20; i++)
87		c1 <-= i;
88}
89
90p(c: chan of int, c1: chan of int)
91{
92	c <-= 0;
93	for(;;)
94		alt{
95			i := <-c1 =>
96				;
97		}
98}
99.P2
100The two spawned processes can both wait on
101.CW c1
102without fuss.
103Processes are queued on a strict FIFO basis so, in
104the example above, the two processes receive on
105.CW c1
106alternately.
107.NH 1
108Function references
109.LP
110Function references may be declared as follows:
111.P1
112fp: ref fn(s1: string, s2: string): int;
113.P2
114Given the function
115.P1
116cmp(s1: string, s2: string): int
117{
118	if(s1 < s2)
119		return -1;
120	if(s1 > s2)
121		return 1;
122	return 0;
123}
124.P2
125a reference to it can be created by assignment:
126.P1
127fp = cmp;
128.P2
129where the name can be qualified by an explicit module reference as usual:
130.P1
131fp = mod->cmp;
132.P2
133or it can be returned from a function:
134.P1
135Cmp: type ref fn(s1: string, s2: string): int;
136
137rcmp(s1: string, s2: string): int
138{
139	return -cmp(s1, s2);
140}
141
142choose(i: int): Cmp
143{
144	if(i)
145		return rcmp;
146	return cmp;
147}
148.P2
149(the declaration of the synonym
150.CW Cmp
151was done only for clarity).
152They may be declared and passed as parameters:
153.P1
154sort(a: array of string, f: ref fn(s1, s2: string): int): array of string
155{
156	# ...
157}
158	# ...
159b := sort(a, cmp);
160c := sort(a, rcmp);
161.P2
162The function is called via the reference by
163.P1
164	r := fp("fred", "bloggs");
165.P2
166Otherwise function references behave just like any other reference type.
167.NH 1
168Exceptions
169.LP
170Both string exceptions and user defined exceptions are now provided.
171The
172.CW Sys
173module interface to exceptions
174has been removed and replaced by new language constructs in limbo.
175.NH 2
176String exceptions
177.LP
178Simple string exceptions can be raised as follows
179.P1
180raise \fIs\fP;
181.P2
182where
183.I s
184is any value of type string (it need not be constant).
185.LP
186Exception handlers may be attached to a block (or sequence of statements) :-
187.P1
188{
189	foo();
190	bar();
191} exception e {
192"a" or "b" =>
193	sys->print("caught %s\en", e);
194	raise;
195"ab*" =>
196	sys->print("caught %s\en", e);
197	exit;
198"abcd*" =>
199	sys->print("caught %s\en", e);
200	raise e;
201"a*" =>
202	sys->print("caught %s\en", e);
203	raise "c";
204"*" =>
205	sys->print("caught %s\en", e);
206}
207LL:
208.P2
209.LP
210Any exception occurring within the block (and in nested function calls within the block) can
211potentially be caught by the exception handler. An exception is caught by a guard exactly
212maching the exception string or by a guard
213\f5\&"\fP\fIs\fP\f5*"\fP
214where
215.I s
216is a prefix of the exception string.
217The most specific match is used. Thus a raise of "a" will be caught by the first
218guard and not by the fourth guard. A raise of "abcde" is caught by the third and not the second
219or fourth. If a match is found, the sequence of statements following the guard are executed.
220If not, the system searches for a handler at a higher level.
221.LP
222As shown above, the exception is available through the exception identifier (e in this case) if given following the exception keyword.
223.LP
224The exception is reraised using
225.P1
226raise;
227.P2
228or
229.P1
230raise e;
231.P2
232.LP
233Both the block and the exception code will fall through to the statement labelled
234LL unless, of course, they do an explicit exit, return or raise first.
235.NH 2
236User-defined exceptions
237.LP
238You can declare your own exceptions:
239.P1
240implement Fibonacci;
241
242include "sys.m";
243include "draw.m";
244
245Fibonacci: module
246{
247	init: fn(nil: ref Draw->Context, argv: list of string);
248};
249.P3
250
251init(nil: ref Draw->Context, nil: list of string)
252{
253	sys := load Sys Sys->PATH;
254	for(i := 0; ; i++){
255		f := fibonacci(i);
256		if(f < 0)
257			break;
258		sys->print("F(%d) = %d\en", i, f);
259	}
260}
261.P3
262
263FIB: exception(int, int);
264.P3
265
266fibonacci(n: int): int
267{
268	{
269		fib(1, n, 1, 1);
270	}exception e{
271	FIB =>
272		(x, nil) := e;
273		return x;
274	"*" =>
275		sys->print("unexpected string exception %s raised\en", e);
276	* =>
277		sys->print("unexpected exception raised\en");
278	}
279	return 0;
280}
281.P3
282
283fib(n: int, m: int, x: int, y: int) raises (FIB)
284{
285	if(n >= m)
286		raise FIB(x, y);
287
288	{
289		fib(n+1, m, x, y);
290	}exception e{
291	FIB =>
292		(x, y) = e;
293		x = x+y;
294		y = x-y;
295		raise FIB(x, y);
296	}
297}
298.P2
299.LP
300.CW FIB
301is a declared exception that returns two integers. The values are supplied when raising the exception:
302.P1
303raise FIB(3, 4);
304.P2
305When caught the values can be recovered by treating the declared exception identifier
306as if it were a tuple of 2 integers:
307.P1
308(x, y) = e;
309.P2
310In general each exception alternative treats the exception identifier appropriately : as a string
311when the exception qualifier is a string, as the relevant tuple when the exception is declared.
312.LP
313If you do
314.P1
315"abcde" or FIB =>
316	(x, y) = e;
317	sys->print("%s\en", e);
318.P2
319you will get a compiler error as
320.CW e 's
321type is indeterminate within this alternative.
322.LP
323Reraising is the same as in the case of string exceptions.
324.LP
325Note also the difference between the string guard
326\&\f5"*"\fP and the guard
327.CW *
328in
329the function fibonacci.
330The former will match any string exception, the latter any exception. If a
331string exception does occur it matches the former as it is the most specific.
332If an unexpected user defined
333exception occurs it matches the latter.
334.LP
335The main difference between declared exceptions and string exceptions is
336that the former must be caught by the immediate caller of a function that
337raises them, otherwise they turn into a string exception whose name is derived
338from that of the exception declaration.
339.NH 2
340The
341.CW raises
342clause
343.LP
344The definition of the function fib in the above example also lists the user defined exceptions it can raise via the use of a
345.CW raises
346clause. In this case there is just the one exception (\f5FIB\f1). These
347clauses (if given) must be compatible between any declaration and definition of the function.
348.LP
349The compiler reports instances of functions which either raise some exception which
350is not mentioned in their raises clause or does not raise some exception which is
351mentioned in their raises clause. Currently the report is a warning.
352.NH 1
353Exponentiation
354.LP
355The exponentiation operator (written as
356.CW ** )
357is now part of the Limbo language.
358Its precedence is above that of multiplication, division and modulus but below
359that of the unary operators. It is right associative. Thus
360.P1
3613**4*2 = (3**4)*2 = 81*2 = 162
362-3**4 = (-3)**4 = 81
3632**3**2 = 2**(3**2) = 2**9 = 512
364.P2
365The type of the left operand must be
366.CW int ,
367.CW big
368or
369.CW real .
370The type of the right operand must be
371.CW int .
372The type of the result is the type of the left operand.
373.NH 1
374Fixed point types
375.LP
376A declaration of the form
377.P1
378x: fixed(0.2, 12345.0);
379.P2
380declares
381.CW x
382to be a variable of a fixed point type. The scale of the type is
3831/5 and the maximum absolute value of the type is 12345.0.
384.LP
385Similarly
386.P1
387x: fixed(0.125, 4096.0)
388.P2
389specifies a scale of 0.125 and a maximum absolute value of 4096.
390This requires only 17 bits so the underlying type will be
391.CW int
392and the compiler
393is free to allocate the remaining 15 bits to greater range or greater
394accuracy. In fact the compiler always chooses the latter.
395.LP
396The maximum absolute value is optional :-
397.P1
398x: fixed(0.125);
399.P2
400is equivalent to
401.P1
402x: fixed(0.125, 2147483647.0 * 0.125);
403.P2
404and ensures the underlying type is exactly an int ie the compiler has
405no scope to add any extra bits for more accuracy.
406.LP
407A binary fixed point type with 8 bits before the binary point and 24 after
408might therefore be declared as
409.P1
410x: fixed(2.0**-24);
411.P2
412.LP
413The scale must be static: its value known at compile time and
414it must be positive and real; similarly for the maximum absolute
415value when specified.
416.LP
417Currently the only underlying base type supported is
418.CW int .
419.LP
420A shorthand for fixed point types is available through the use of
421.CW type
422declarations:
423.P1
424fpt: type fixed(2.0**-16);
425.P2
426We can then do
427.P1
428x, y, z: fpt;
429zero: con fpt(0);
430
431x = fpt(3.21);
432y = fpt(4.678);
433z = fpt(16r1234.5678);
434z = -x;
435z = x+y;
436z = x-y;
437z = x*y;
438z = x/y;
439sys->print("z=%f", real z);
440.P2
441There is no implicit numerical casting in Limbo so we have to use explicit
442casts to initialize fixed point variables. Note the use of a base to
443initialize
444.CW z
445using a new literal representation.
446.LP
447Given
448.P1
449fpt1: type fixed(0.12345);
450x: fpt1;
451fpt2: type fixed(0.1234);
452y: fpt2;
453fpt3: type fixed(0.123);
454z: fpt3;
455.P2
456then
457.P1
458z = x*y;
459.P2
460is illegal. We must add casts and do
461.P1
462z = fpt3(x)*fpt3(y);
463.P2
464ie type equivalence between fixed point types requires equivalence of scale
465(and of maximum absolute value when specified).
466.LP
467Fixed point types may be used where any other numerical type (byte, int, big, real) can be used. So you can compare them, have a list of them, have a channel of them, cast them to or from string and so on.
468.LP
469You cannot use complement(~), not(!), and(&), or(|), xor(^) or modulus(%) on them as fixed point types are basically a form of real type.
470.NH 2
471Accuracy
472.LP
473A fixed point value is a multiple of its scale. Given fixed point values X, Y and
474Z of scale s, t and u respectively, we can write
475.P1
476X = sx
477Y = ty
478Z = uz
479.P2
480where x, y and z are integers.
481.LP
482For the multiplication Z = X*Y the accuracy achieved is given by
483.P1
484| z - (st/u)xy | < 1
485.P2
486and for the division Z = X/Y
487.P1
488| z - (s/(tu))x/y | < 1
489.P2
490That is, the result is always within the result scale of the correct real value.
491.LP
492This also applies when casting a fixed point type to another, casting an
493integer to a fixed point type and casting a fixed point type to an integer. These
494are all examples of the multiplication law with t = y = 1 since an
495integer may be thought of as a fixed point type with a scale of 1.
496