Why can't a closeable have an open() method that returns this - how do I acquire a resource?
← Error Handling and Exceptions · Ref: Q1720
A type that has an 'operator close' acquires its resource in its CONSTRUCTOR, and a try-with-resources header (or the statement that creates it) owns it from the moment it exists. There is no separate 'open' step, so there is nothing to forget and no half-open object. The two-step 'construct, then fluent open()' idiom is rejected because 'open() <- rtn as Res: this' hands back the object that the receiver expression already created: both the try and the temporary then own the same object, which is closed twice (E81057 - a closeable return must be a NEW instance).
Name it in a try when you need it across several statements:
try -> named <- Tracker("named") stdout.println(named.describe())
Leave it unnamed for a one-shot read - the statement closes it:
lines <- List() of String cat TextFile("data.txt").input() > lines
The program below acquires in the constructor, uses one tracker named in a try and one unnamed, and has a method that hands back a NEW tracker, which the caller's try then closes.
Example
defines module qa.errorhandling.acquireinconstructor defines class Tracker name as String? default private Tracker() Tracker() -> label as String stdout <- Stdout() name: label stdout.println(`opened ${name}`) describe() as pure <- rtn as String: `tracker ${name}` again() <- rtn as Tracker: Tracker(name) operator close as pure stdout <- Stdout() stdout.println(`closed ${name}`) default operator ? defines program AcquireInConstructorDemo() stdout <- Stdout() try -> named <- Tracker("named") stdout.println(named.describe()) stdout.println(Tracker("unnamed").describe()) try -> second <- Tracker("first").again() stdout.println(second.describe())
Common mistakes
E81057 — A method with a closeable return type must return a NEW instance; returning 'this' would give the caller the object that the receiver expression already owns, so it would be closed twice. Acquire in the constructor instead of a fluent open(). See ek9 -h E81057 for details.
Incorrect:
again()
<- rtn as Tracker: this
Correct:
again()
<- rtn as Tracker: Tracker(name)
Other ways to ask this
- How do I write the Resource().open() pattern in EK9?
- Why is a fluent open() on my closeable class error E81057?
- Should a resource be acquired in its constructor or in a separate open() call?
- What replaces 'file = File(path); file.open()' in EK9?
Coming from another language?
Java: new Resource().open() or a builder returning this leaves the construct-then-open window where an exception leaks the object. Python: __init__ then .open() has the same window; 'with' is optional. C++: RAII - acquire in the constructor, release in the destructor - is exactly this model. Rust: a constructor such as File::open returns the owned value. EK9: acquire in the constructor; a method returning a closeable must return a new instance (E81057).
Keywords: closeable, fluent, try, E81057, resource, close, constructor, RAII, acquire, ownership, open