qt4cg / qtspecs

QT4 specifications
https://qt4cg.org/
Other
27 stars 15 forks source link

Error handling: Rethrow errors; finally block #501

Open ChristianGruen opened 1 year ago

ChristianGruen commented 1 year ago

Re-throw errors

In https://github.com/qt4cg/qtspecs/pull/493, a function/expression was suggested to re-throw errors:

try {
  (: wild stuff :)
} catch * {
  module:log($err:description),
  rethrow($err:map)
}

Alternatives

In principle, the error information can also be constructed by the user. If we extend fn:error to also accept a map, it could be used to both throw and re-throw errors:

try {
  1 + <empty/>
} catch * {
  (: ... :)
  fn:error($err:map)
}

Missing information in the map could be added as if fn:error is raised.

let $map := map { 'column-number': 12, 'line-number': 3 }
return fn:error(xs:QName('oob'), 'Out of bounds', map := $map)

Finally clause

It can be helpful to have a code block that is always executed, even if errors occur:

let $tmp := file:create-temp-file()
return try {
  (: I/O stuff :)
} finally {
  file:delete($tmp)
}
michaelhkay commented 1 year ago

I'm not sure how useful "finally" is in a declarative language with no state and no open resources that need to be closed. Your example isn't very convincing if it's only needed when handling file updates with side effects.

I'm inclined to go for a function throw(map). Not rethrow(), because the map might be constructed for the purpose.

ndw commented 1 year ago

We ended up with a finally block in XProc even though I concur that it's a bit odd in a declarative language. It's not impossible, however, to imagine interacting with a stateful database or web service where it's important to signal completion.

Rather than throw() does it make sense to adjust the semantics of error() to handle a map? Having two different ways to raise an exception doesn't seem ideal.

ChristianGruen commented 1 year ago

I'm not sure how useful "finally" is in a declarative language with no state and no open resources that need to be closed. Your example isn't very convincing if it's only needed when handling file updates with side effects.

I agree that finally would be a concession that XQuery is also used for side-effecting use cases. If your code is deterministic, there’s no need for it.

I'm inclined to go for a function throw(map). Not rethrow(), because the map might be constructed for the purpose.

I also believe we should better extend the existing fn:error function; otherwise, it might get difficult to explain what’s the difference between these functions (most users will stick with fn:error anyway). I have slightly enhanced my initial comment in this issue to indicate how this could be done.

michaelhkay commented 1 year ago

I also believe we should better extend the existing fn:error function

I just worry a little about how the different parameters interact with each other; when you have different ways of providing the same information, the specification becomes a bit complicated.