How do I reconnect or replace a resource my object holds?
← Error Handling and Exceptions · Ref: Q1841
You do not re-point it: a closeable property is set ONCE, in its own initialiser or its own type's constructor, while it holds nothing yet, and its type's operator close releases it when the object is closed. Any other assignment - ':=' in a reconnect() method, an assignment through another object, a constructor setting it a second time - is error E81063, because it drops the resource the property held and nothing would ever close it. The guarded ':=?' is no escape: a connection that failed or was closed is present but UNSET, so ':=?' replaces it and still drops it.
When code needs a new resource for each attempt, acquire it in a try where it is used - a fresh connection per try. Each attempt's connection is closed when its try ends, whether the attempt worked or not. When an object needs a different resource for its whole life, make a new object: its constructor acquires the resource and its operator close releases it.
The program below retries a send three times with a fresh connection per try; the first two attempts fail, and every connection is closed exactly once.
Example
defines module qa.errorhandling.replaceaheldresource defines class Connection name as String? healthy as Boolean? default private Connection() Connection() -> text as String works as Boolean name: text healthy: works label() as pure <- rtn as String: name send() -> message as String <- rtn as Boolean: healthy stdout <- Stdout() if healthy stdout.println(`sent ${message} on ${name}`) else stdout.println(`${name} failed`) operator close as pure stdout <- Stdout() stdout.println(`closed ${name}`) default operator ? <?- Its connection is set once, in the constructor, and closed with the session -?> Session conn as Connection? default private Session() Session() -> text as String conn: Connection(text, true) describe() stdout <- Stdout() stdout.println(`session on ${conn.label()}`) default operator close default operator ? defines program RetryWithFreshConnection() stdout <- Stdout() try -> session <- Session("primary") session.describe() maximumAttempts <- 3 sent <- false attempt <- 1 while not sent and attempt <= maximumAttempts try -> conn <- Connection(`attempt ${attempt}`, attempt == maximumAttempts) sent := conn.send("hello") attempt++ stdout.println(`sent: ${sent}`)
Common mistakes
E81063 — A closeable property is set once, while its object is constructed. Re-pointing it in a method drops the connection it held and nothing closes it; acquire a fresh connection in a try instead. See ek9 -h E81063 for details.
Incorrect:
describe()
conn := Connection("replacement", true)
stdout <- Stdout()
stdout.println(`session on ${conn.label()}`)
Correct:
describe()
stdout <- Stdout()
stdout.println(`session on ${conn.label()}`)
Other ways to ask this
- Why is 'conn := Connection(..)' in a reconnect method an error?
- Can I use ':=?' to open a connection only if it is not open?
- How do I retry with a new connection in EK9?
- What does E81063 mean?
Coming from another language?
Java: 'this.conn = connect()' in a reconnect method silently leaks the old socket unless you remember to close it first. C#: the same, and a disposable field re-assigned is a classic CA2213 miss. Python: 'self.conn = connect()' leaks until the garbage collector finalises it. EK9: re-pointing a closeable property is a compile error (E81063); acquire a fresh resource in a try per attempt.
Keywords: re-point, operator close, retry, close, E81063, property, reconnect, assign if unset, try-with-resources, closeable