Trac #4506: structural types vs. no-arg defs

43 views
Skip to first unread message

Todd Vierling

unread,
Apr 27, 2011, 10:23:44 AM4/27/11
to scala-l...@googlegroups.com
https://lampsvn.epfl.ch/trac/scala/ticket/4506#comment:2

I'm not sure why the close comment on this issue directed me to http://lampsvn.epfl.ch/trac/scala/changeset/24029, and I'm thinking that perhaps what I was asking wasn't really clear. In any case, here's a code example of the issue, and I've changed it to be more like the case reported in https://lampsvn.epfl.ch/trac/scala/ticket/2810 so that a concrete use case can be understood:

object Test {
  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?

Todd Vierling

unread,
Apr 27, 2011, 10:26:06 AM4/27/11
to scala-l...@googlegroups.com
On Wednesday, April 27, 2011 10:23:44 AM UTC-4, Todd Vierling wrote:
  def doCloseNoParams(x: CloseableLikeNoParams) = x.close
  def doCloseEmptyParams(x: CloseableLikeEmptyParams) = x.close()
 
      doCloseNoArgs(na)
      doCloseEmptyArgs(na) // compile error: type mismatch

Bah. In case it wasn't obvious, the original test case used the word "Args", but I changed it, and I didn't fully edit all those instances to say "Params" before posting. All text using "Args" should actually read "Params". :-/

Rex Kerr

unread,
Apr 28, 2011, 10:50:06 AM4/28/11
to scala-l...@googlegroups.com
I agree that this is a silly state of affairs, but note that if it is not fixed, you can build your way around it using implicits:

  implicit def noP2withP(c: { def close: Unit }) = new { def close() { c.close } }
  implicit def withP2noP(c: { def close(): Unit }) = new { def close { c.close } }

Add these two lines and now everything will compile.  If you want to only go in one direction, use only one of the implicits.

  --Rex
Reply all
Reply to author
Forward
0 new messages