type CloseableLikeNoParams = { def close: Unit }
type CloseableLikeEmptyParams = { def close(): Unit }
def doCloseNoParams(x: CloseableLikeNoParams) = x.close
def doCloseEmptyParams(x: CloseableLikeEmptyParams) = x.close()
trait CloseNoParams { def close: Unit }
trait CloseEmptyParams { def close(): Unit }
def test(na: CloseNoParams, ea: CloseEmptyParams, jc: java.io.Closeable) {
na.close
na.close() // compile error: can't invoke with empty params
doCloseNoArgs(na)
doCloseEmptyArgs(na) // compile error: type mismatch
ea.close
ea.close()
doCloseNoArgs(ea) // compile error: type mismatch [*]
doCloseEmptyArgs(ea)
jc.close
jc.close()
doCloseNoArgs(jc) // compile error: type mismatch [*]
doCloseEmptyArgs(jc)
}
}
(Here, java.io.Closeable is identical in semantics to the trait CloseEmptyArgs, but is included to show interoperability.)
The incongruity I reported in 4506 is that both cases, a def with no param-list and a def with an empty param-list, can be called without a param list (xx.close), but only defs declared with an empty param-list can be called with it (xx.close()).
However, using structural types, the with/without empty param-list state of a def currently must match exactly, meaning that code intended to work with both CloseNoParams and CloseEmptyParams must have two separate code paths to handle them. No single structural type can match both, even though a single direct calling convention works with both.
I stated in the issue that #2810 was a symptom of this larger problem (in that a library function was originally "def close" and changed to "def close()" to resolve that specific issue).
It feels to me like this situation goes against the grain of the uniform access principle, since defs with or without an empty param-list can be called in the same way -- but structural types don't currently agree. Thoughts?