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