E81061: a closeable type is never a type argument - hold it as a property of a type you own (closed in its 'operator close'), acquire it in a try, or lend it as a parameter
What went wrong
A closeable type (its type has an 'operator close') is used as a type argument of a generic type or a generic function: List of TCP, Optional of Res, Dict of (String, HTTP), a Box of Res of your own, Acceptor of TCP. It is refused whether the type argument is written or inferred - [res], Optional(res), {"key": res} and a generic function called with a closeable are all the same parameterisation. A generic's 'T constrain by Res', where Res is closeable, is refused at its declaration for the same reason.
A closeable value has exactly one owner, and the compiler must see that owner close it: a try-with-resources resource, a property closed in its type's 'operator close', or the caller of a callable that returns a new one. A generic type or function has no such owner. Nothing would ever close what it holds, or it would close a value somebody else still owns.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 1780 (Can I put a closeable resource in a List, an Optional or my own generic type?)
- ek9 -q 1740 (How do I let other code use a resource my object owns without letting it close or keep it?)
- ek9 -q 1520 (When does EK9 close a closeable value, and where may I keep one?)
- ek9 -q 1901 (How do I broadcast a message to every open connection, like a chat server, when I cannot keep a list of connections?)
How to fix it
A closeable can be a PARAMETER type - a loan, and the lender keeps ownership - but never a type argument. Pick the shape that matches what the code needs:
- Hold it as a property of a type you own, and let that type's close close it - a 'Pair' holding two Res properties, closed by its generated close, rather than a List of Res (the [EXAMPLE] section below)
- Acquire it in a try where it is used, rather than wrapping it in an Optional
- Lend it as a parameter instead of collecting it - 'use(res)' from inside the owning try, rather than '[res]' (the same [EXAMPLE])
Common causes
- Collecting several connections or files in a List or Dict to close them later in a loop.
- Wrapping a resource in an Optional to say "may not be open".
- A generic holder or cache class parameterised with a resource type.
- A callback typed as a generic function type such as Acceptor of TCP; declare a non-generic function type that takes the resource as its parameter instead (as TCPReadyHandler does).
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E81061: a closeable type is never a type argument - hold it as a property of a type you own (closed in its 'operator close'), acquire it in a try, or lend it as a parameter
#!ek9 defines module error.e81061.bad defines class Res name as String? default private Res() Res() -> initialName as String name: initialName default operator ? operator close as pure require true Box of type T default Box() as pure Box() as pure -> seed as T require seed? default operator ? <?- BAD: a property holding a list of closeables -?> ListHolder held as List of Res? default operator ? <?- A type closeable only through its super, declared before that super -?> SubRes extends BaseRes default SubRes() default operator ? BaseRes as open default BaseRes() default operator ? operator close as pure require true <?- VALID: a closeable held as a property of a type you own, closed in its operator close -?> Owner held as Res? default private Owner() Owner() -> name as String held: Res(name) default operator ? default operator close defines function <?- BAD: a parameter typed as a list of closeables -?> listParameter() -> resources as List of Res require resources? <?- BAD: a nested type argument -?> nestedListParameter() -> resources as List of List of Res require resources? <?- BAD: an Optional of a built-in closeable as the returning variable -?> optionalReturn() as abstract <- rtn as Optional of TCP? <?- BAD: a closeable as a Dict value type -?> dictParameter() -> servers as Dict of (String, HTTP) require servers? <?- BAD: a user generic parameterised with a closeable -?> boxConstruction() boxes <- Box() of Res require boxes? <?- BAD: a generic function type parameterised with a closeable -?> acceptorParameter() -> handler as Acceptor of TCP require handler? <?- BAD: an empty list of a built-in closeable input -?> listConstruction() inputs <- List() of CloseableStringInput require inputs? <?- BAD: the Closeable trait itself, and a type closeable only through its super -?> closeableTraitParameter() -> resources as List of Closeable require resources? inheritedCloseParameter() -> resources as Optional of SubRes require resources? <?- VALID: the standard streams have a close operator but are never closed, so they are not closeable here -?> standardOutputs() -> outputs as List of Stdout require outputs? <?- VALID: a closeable lent as a parameter -?> lentParameter() -> res as Res require res? <?- VALID: a generic of a non-closeable type -?> nonCloseableList() -> names as List of String require names?
Fixed: Hold the closeable in a property of a type you own, or acquire it in a try and lend it
#!ek9 defines module error.e81061.good defines class Res name as String? default private Res() Res() -> label as String name: label read() as pure <- rtn as String: `read ${name}` default operator ? operator close as pure stdout <- Stdout() stdout.println(`closed ${name}`) Pair first as Res? second as Res? default private Pair() Pair() -> label as String first: Res(`${label} one`) second: Res(`${label} two`) default operator ? default operator close defines function use() -> res as Res <- rtn as String: res.read() lend() <- rtn as String? try -> res <- Res("lent") rtn: use(res)
Questions and answers
These Q&A entries show E81061 and its fix: