Ключевое слово X/exception



В 11l присутствует два [[вида/]типа/]рода исключений:
  1. Нефатальные/дешёвые исключения. Обязательны к перехвату в исходном коде, компилятор должен проверить, что все такие исключения перехватываются.
    Их необходимо указывать в ‘спецификаторах исключений’\‘exception specifications’. Поведение аналогично checked исключениям в Java, а реализация — аналогична ошибкам в Rust и Swift — т.е. просто как дополнительное [скрытое] возвращаемое значение у функции.
  2. Фатальные/дорогие исключения, отсутствие которых гарантировать невозможно и которые нельзя проверить на этапе компиляции. Поведение аналогично тому, как реализованы исключения в самых популярных языках программирования (C++, Python, C#, unchecked исключения в Java).

Исключения первого рода используют синтаксис
X <объект_исключения>
для порождения и
X.handle <тип_исключения>
для обработки.
Исключения второго рода используют синтаксис
X.throw <объект_исключения>
для порождения и
X.try
/
X.catch
для обработки.

Почему `X.handle`?

Пример исключения первого рода:
T StopIteration {}

T RangeIterator
   Int cur, end

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

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

V it = RangeIterator(1, 10)
L
   V i = it.__next__()
   X.handle StopIteration
      L.break
   print(i)

Обратите внимание, что код, порождающий исключения первого рода, не обязательно заключать в блок
X.try
{}, т.к. такие исключения гарантированно должны быть обработаны.
Но при необходимости его можно заключить в блок
X.try
:
F circles_from_p1p2r(p1, p2, r) X(Error)
   I r == 0.0
      X Error(‘radius of zero’)
   I p1 == p2
      X Error(‘coincident points gives infinite number of Circles’)
   ...
   R (c1, c2)

L(p1, p2, r) [((0.1234, 0.9876), (0.8765, 0.2345), 2.0),
              ...
              ((0.1234, 0.9876), (0.1234, 0.9876), 0.0)]
   print("Through points:\n  #.,\n  #.\n  and radius #.6\nYou can construct the following circles:".format(p1, p2, r))
   X.try
      V (c1, c2) = circles_from_p1p2r(p1, p2, r)
      print("  #.\n  #.\n".format(c1, c2))
   X.handle Error v
      print("  ERROR: #.\n".format(v.msg))
(Полный код.)

Исключения первого рода хорошо подходят для возврата из рекурсивных функций:
F walk_maze(m, n, &cell, indx) X(PercolatedException)
   cell[n][m] = indx
   I n < :nn - 1 & cell[n + 1][m] == :NOT_VISITED
      walk_maze(m, n + 1, &cell, indx)
   E I n == :nn - 1
      X PercolatedException((m, indx))
   I m & cell[n][m - 1] == :NOT_VISITED
      walk_maze(m - 1, n, &cell, indx)
   I m < :M - 1 & cell[n][m + 1] == :NOT_VISITED
      walk_maze(m + 1, n, &cell, indx)
   I n & cell[n - 1][m] == :NOT_VISITED
      walk_maze(m, n - 1, &cell, indx)

F check_from_top(&cell) -> (Int, Int)?
   V (n, walk_index) = (0, 1)
   L(m) 0 .< :M
      I cell[n][m] == :NOT_VISITED
         walk_index++
         walk_maze(m, n, &cell, walk_index)
         X.handle PercolatedException ex
            R ex.t
   R N
(Полный код.)

Пример исключения второго рода:
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)
Этот код выведет:
1
Error: error message

Обоснование разделения исключений в 11l на два рода