X/exception keyword



In 11l, there are two [types/]kinds of exceptions:
  1. Non-fatal/cheap exceptions. Mandatory to be caught in source code, the compiler must check that all such exceptions are caught.
    They must be specified in exception specifications. The behavior is similar to checked exceptions in Java, and the implementation is similar to errors in Rust and Swift - i.e. just as an extra [hidden] return value in a function.
  2. Fatal/expensive exceptions whose absence cannot be guaranteed and cannot be checked at compile time. The behavior is similar to how exceptions are implemented in the most popular programming languages (C++, Python, C#, unchecked exceptions in Java).

Exceptions of the first kind use the syntax
X <exception_object>
for throwing and
X.handle <exception_type>
for handling.
Exceptions of the second kind use the syntax
X.throw <exception_object>
for throwing and
X.try
/
X.catch
for handling.

Why `X.handle`?

An example of an exception of the first kind:
T StopIteration {}

T RangeIterator
   Int cur, end

   F (start, end)
      .cur = start - 1
      .end = end

   F __next__() X(StopIteration)
      .cur++
      I .cur == .end
         X StopIteration()
      R .cur

V it = RangeIterator(1, 10)
L
   V i = it.__next__()
   X.handle StopIteration
      L.break
   print(i)
(Note that code generating exceptions of the first kind need not be enclosed in the
X.try
block {}, since such exceptions are guaranteed to be handled.)

An example of an exception of the second kind:
T Error
   String message
   F (message)
      .message = message

X.try
   print(‘1’)
   X.throw Error(‘error message’)
   print(‘never printed string’)
X.catch Error e
   print(‘Error: ’e.message)
This code will print:
1
Error: error message