summaryrefslogtreecommitdiff
path: root/doc/limbo/addendum.ms
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 20:52:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 20:52:35 +0000
commit46439007cf417cbd9ac8049bb4122c890097a0fa (patch)
tree6fdb25e5f3a2b6d5657eb23b35774b631d4d97e4 /doc/limbo/addendum.ms
parent37da2899f40661e3e9631e497da8dc59b971cbd0 (diff)
20060303-partial
Diffstat (limited to 'doc/limbo/addendum.ms')
-rw-r--r--doc/limbo/addendum.ms495
1 files changed, 495 insertions, 0 deletions
diff --git a/doc/limbo/addendum.ms b/doc/limbo/addendum.ms
new file mode 100644
index 00000000..557115fa
--- /dev/null
+++ b/doc/limbo/addendum.ms
@@ -0,0 +1,495 @@
+.TL
+Addendum to
+.I "The Limbo Programming Language"
+.AU
+Vita Nuova
+.br
+30 March 2005
+.NH 1
+Introduction
+.LP
+This addendum provides a brief summary of several language changes to
+Limbo since
+.I "The Limbo Programming Language"
+was last revised:
+.RS
+.IP •
+buffered channels
+.IP •
+unrestricted \f5alt\f1
+.IP •
+function references
+.IP •
+exceptions
+.IP •
+exponentiation
+.IP •
+fixed-point types
+.RE
+.NH 1
+Buffered channels
+.LP
+A buffered channel can now be declared:
+.P1
+c := chan[1] of int;
+.P2
+Here the buffer size is 1. A send on this channel will succeed immediately
+if there is a receiver waiting or if the buffer is empty. A receive on this
+channel will succeed immediately if there is a data item in the buffer. This allows us to
+write a very simple locking mechanism:
+.P1
+acquire(c: chan of int)
+{
+ c <-= 0;
+}
+
+release(c: chan of int)
+{
+ <-c;
+}
+
+new(): chan of int
+{
+ return chan[1] of int;
+}
+.P2
+The declaration
+.P1
+c := chan[0] of int;
+.P2
+is equivalent to
+.P1
+ c := chan of int;
+.P2
+An attempt to create a channel with a negative buffer size will raise
+an exception. An attempt to create a channel with a very large buffer
+may result in an immediate memory exception if there is not enough
+room for the buffer.
+.NH 1
+Unrestricted
+.B alt
+.LP
+The implementation has changed to remove the restriction that only one process can be
+waiting in an
+.CW alt
+to send or receive on a particular channel.
+The busy exception never occurs now. Thus you can do
+.P1
+i()
+{
+ c := chan of int;
+ c1 := chan of int;
+ spawn p(c, c1);
+ <-c;
+ spawn p(c, c1);
+ <-c;
+ for(i := 0; i < 20; i++)
+ c1 <-= i;
+}
+
+p(c: chan of int, c1: chan of int)
+{
+ c <-= 0;
+ for(;;)
+ alt{
+ i := <-c1 =>
+ ;
+ }
+}
+.P2
+The two spawned processes can both wait on
+.CW c1
+without fuss.
+Processes are queued on a strict FIFO basis so, in
+the example above, the two processes receive on
+.CW c1
+alternately.
+.NH 1
+Function references
+.LP
+Function references may be declared as follows:
+.P1
+fp: ref fn(s1: string, s2: string): int;
+.P2
+Given the function
+.P1
+cmp(s1: string, s2: string): int
+{
+ if(s1 < s2)
+ return -1;
+ if(s1 > s2)
+ return 1;
+ return 0;
+}
+.P2
+a reference to it can be created by assignment:
+.P1
+fp = cmp;
+.P2
+where the name can be qualified by an explicit module reference as usual:
+.P1
+fp = mod->cmp;
+.P2
+or it can be returned from a function:
+.P1
+Cmp: type ref fn(s1: string, s2: string): int;
+
+rcmp(s1: string, s2: string): int
+{
+ return -cmp(s1, s2);
+}
+
+choose(i: int): Cmp
+{
+ if(i)
+ return rcmp;
+ return cmp;
+}
+.P2
+(the declaration of the synonym
+.CW Cmp
+was done only for clarity).
+They may be declared and passed as parameters:
+.P1
+sort(a: array of string, f: ref fn(s1, s2: string): int): array of string
+{
+ # ...
+}
+ # ...
+b := sort(a, cmp);
+c := sort(a, rcmp);
+.P2
+The function is called via the reference by
+.P1
+ r := fp("fred", "bloggs");
+.P2
+Otherwise function references behave just like any other reference type.
+.NH 1
+Exceptions
+.LP
+Both string exceptions and user defined exceptions are now provided.
+The
+.CW Sys
+module interface to exceptions
+has been removed and replaced by new language constructs in limbo.
+.NH 2
+String exceptions
+.LP
+Simple string exceptions can be raised as follows
+.P1
+raise \fIs\fP;
+.P2
+where
+.I s
+is any value of type string (it need not be constant).
+.LP
+Exception handlers may be attached to a block (or sequence of statements) :-
+.P1
+{
+ foo();
+ bar();
+} exception e {
+"a" or "b" =>
+ sys->print("caught %s\en", e);
+ raise;
+"ab*" =>
+ sys->print("caught %s\en", e);
+ exit;
+"abcd*" =>
+ sys->print("caught %s\en", e);
+ raise e;
+"a*" =>
+ sys->print("caught %s\en", e);
+ raise "c";
+"*" =>
+ sys->print("caught %s\en", e);
+}
+LL:
+.P2
+.LP
+Any exception occurring within the block (and in nested function calls within the block) can
+potentially be caught by the exception handler. An exception is caught by a guard exactly
+maching the exception string or by a guard
+\f5\&"\fP\fIs\fP\f5*"\fP
+where
+.I s
+is a prefix of the exception string.
+The most specific match is used. Thus a raise of "a" will be caught by the first
+guard and not by the fourth guard. A raise of "abcde" is caught by the third and not the second
+or fourth. If a match is found, the sequence of statements following the guard are executed.
+If not, the system searches for a handler at a higher level.
+.LP
+As shown above, the exception is available through the exception identifier (e in this case) if given following the exception keyword.
+.LP
+The exception is reraised using
+.P1
+raise;
+.P2
+or
+.P1
+raise e;
+.P2
+.LP
+Both the block and the exception code will fall through to the statement labelled
+LL unless, of course, they do an explicit exit, return or raise first.
+.NH 2
+User-defined exceptions
+.LP
+You can declare your own exceptions:
+.P1
+implement Fibonacci;
+
+include "sys.m";
+include "draw.m";
+
+Fibonacci: module
+{
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+.P3
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+ sys := load Sys Sys->PATH;
+ for(i := 0; ; i++){
+ f := fibonacci(i);
+ if(f < 0)
+ break;
+ sys->print("F(%d) = %d\en", i, f);
+ }
+}
+.P3
+
+FIB: exception(int, int);
+.P3
+
+fibonacci(n: int): int
+{
+ {
+ fib(1, n, 1, 1);
+ }exception e{
+ FIB =>
+ (x, nil) := e;
+ return x;
+ "*" =>
+ sys->print("unexpected string exception %s raised\en", e);
+ * =>
+ sys->print("unexpected exception raised\en");
+ }
+ return 0;
+}
+.P3
+
+fib(n: int, m: int, x: int, y: int) raises (FIB)
+{
+ if(n >= m)
+ raise FIB(x, y);
+
+ {
+ fib(n+1, m, x, y);
+ }exception e{
+ FIB =>
+ (x, y) = e;
+ x = x+y;
+ y = x-y;
+ raise FIB(x, y);
+ }
+}
+.P2
+.LP
+.CW FIB
+is a declared exception that returns two integers. The values are supplied when raising the exception:
+.P1
+raise FIB(3, 4);
+.P2
+When caught the values can be recovered by treating the declared exception identifier
+as if it were a tuple of 2 integers:
+.P1
+(x, y) = e;
+.P2
+In general each exception alternative treats the exception identifier appropriately : as a string
+when the exception qualifier is a string, as the relevant tuple when the exception is declared.
+.LP
+If you do
+.P1
+"abcde" or FIB =>
+ (x, y) = e;
+ sys->print("%s\en", e);
+.P2
+you will get a compiler error as
+.CW e 's
+type is indeterminate within this alternative.
+.LP
+Reraising is the same as in the case of string exceptions.
+.LP
+Note also the difference between the string guard
+\&\f5"*"\fP and the guard
+.CW *
+in
+the function fibonacci.
+The former will match any string exception, the latter any exception. If a
+string exception does occur it matches the former as it is the most specific.
+If an unexpected user defined
+exception occurs it matches the latter.
+.LP
+The main difference between declared exceptions and string exceptions is
+that the former must be caught by the immediate caller of a function that
+raises them, otherwise they turn into a string exception whose name is derived
+from that of the exception declaration.
+.NH 2
+The
+.CW raises
+clause
+.LP
+The definition of the function fib in the above example also lists the user defined exceptions it can raise via the use of a
+.CW raises
+clause. In this case there is just the one exception (\f5FIB\f1). These
+clauses (if given) must be compatible between any declaration and definition of the function.
+.LP
+The compiler reports instances of functions which either raise some exception which
+is not mentioned in their raises clause or does not raise some exception which is
+mentioned in their raises clause. Currently the report is a warning.
+.NH 1
+Exponentiation
+.LP
+The exponentiation operator (written as
+.CW ** )
+is now part of the Limbo language.
+Its precedence is above that of multiplication, division and modulus but below
+that of the unary operators. It is right associative. Thus
+.P1
+3**4*2 = (3**4)*2 = 81*2 = 162
+-3**4 = (-3)**4 = 81
+2**3**2 = 2**(3**2) = 2**9 = 512
+.P2
+The type of the left operand must be
+.CW int ,
+.CW big
+or
+.CW real .
+The type of the right operand must be
+.CW int .
+The type of the result is the type of the left operand.
+.NH 1
+Fixed point types
+.LP
+A declaration of the form
+.P1
+x: fixed(0.2, 12345.0);
+.P2
+declares
+.CW x
+to be a variable of a fixed point type. The scale of the type is
+1/5 and the maximum absolute value of the type is 12345.0.
+.LP
+Similarly
+.P1
+x: fixed(0.125, 4096.0)
+.P2
+specifies a scale of 0.125 and a maximum absolute value of 4096.
+This requires only 17 bits so the underlying type will be
+.CW int
+and the compiler
+is free to allocate the remaining 15 bits to greater range or greater
+accuracy. In fact the compiler always chooses the latter.
+.LP
+The maximum absolute value is optional :-
+.P1
+x: fixed(0.125);
+.P2
+is equivalent to
+.P1
+x: fixed(0.125, 2147483647.0 * 0.125);
+.P2
+and ensures the underlying type is exactly an int ie the compiler has
+no scope to add any extra bits for more accuracy.
+.LP
+A binary fixed point type with 8 bits before the binary point and 24 after
+might therefore be declared as
+.P1
+x: fixed(2.0**-24);
+.P2
+.LP
+The scale must be static: its value known at compile time and
+it must be positive and real; similarly for the maximum absolute
+value when specified.
+.LP
+Currently the only underlying base type supported is
+.CW int .
+.LP
+A shorthand for fixed point types is available through the use of
+.CW type
+declarations:
+.P1
+fpt: type fixed(2.0**-16);
+.P2
+We can then do
+.P1
+x, y, z: fpt;
+zero: con fpt(0);
+
+x = fpt(3.21);
+y = fpt(4.678);
+z = fpt(16r1234.5678);
+z = -x;
+z = x+y;
+z = x-y;
+z = x*y;
+z = x/y;
+sys->print("z=%f", real z);
+.P2
+There is no implicit numerical casting in Limbo so we have to use explicit
+casts to initialize fixed point variables. Note the use of a base to
+initialize
+.CW z
+using a new literal representation.
+.LP
+Given
+.P1
+fpt1: type fixed(0.12345);
+x: fpt1;
+fpt2: type fixed(0.1234);
+y: fpt2;
+fpt3: type fixed(0.123);
+z: fpt3;
+.P2
+then
+.P1
+z = x*y;
+.P2
+is illegal. We must add casts and do
+.P1
+z = fpt3(x)*fpt3(y);
+.P2
+ie type equivalence between fixed point types requires equivalence of scale
+(and of maximum absolute value when specified).
+.LP
+Fixed 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.
+.LP
+You cannot use complement(~), not(!), and(&), or(|), xor(^) or modulus(%) on them as fixed point types are basically a form of real type.
+.NH 2
+Accuracy
+.LP
+A fixed point value is a multiple of its scale. Given fixed point values X, Y and
+Z of scale s, t and u respectively, we can write
+.P1
+X = sx
+Y = ty
+Z = uz
+.P2
+where x, y and z are integers.
+.LP
+For the multiplication Z = X*Y the accuracy achieved is given by
+.P1
+| z - (st/u)xy | < 1
+.P2
+and for the division Z = X/Y
+.P1
+| z - (s/(tu))x/y | < 1
+.P2
+That is, the result is always within the result scale of the correct real value.
+.LP
+This also applies when casting a fixed point type to another, casting an
+integer to a fixed point type and casting a fixed point type to an integer. These
+are all examples of the multiplication law with t = y = 1 since an
+integer may be thought of as a fixed point type with a scale of 1.