New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

explicit case class companion does not extend Function / override toString #3664

Open
scabug opened this Issue Jul 12, 2010 · 12 comments

Comments

Projects
None yet
5 participants
@scabug
Copy link

scabug commented Jul 12, 2010

When explicitly defining the companion object of a case class, the compiler does not add a FunctionN parent and does not override toString.

working as expected:

scala> object u { case class C(x: Int) }
defined module u

scala> u.C
res1: u.C.type = C

scala> val f: Int => u.C = u.C
f: (Int) => u.C = C

scala> 

unexpected:

scala> object t { object C; case class C(x: Int) }
defined module t

scala> t.C
res0: t.C.type = t$$C$$@5e58a983

scala> val f: Int => t.C = t.C
<console>:6: error: type mismatch;
 found   : t.C.type (with underlying type object t.C)
 required: (Int) => t.C
       val f: Int => t.C = t.C
                             ^
@scabug

This comment has been minimized.

Copy link
Author

scabug commented Jul 12, 2010

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Jan 31, 2012

@milessabin said:
Is this really "Not a Bug"? Can someone point to specage which justifies that resolution?

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Jan 31, 2012

@lrytz said:
inconsistent with the spec - it does not say that generated companions extend from Function

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Dec 3, 2012

Fred (fred) said:
What's the status on this?

This one is annoying for me when trying to use the new "JSON Inception" coming in playframework 2.1 (http://mandubian.com/2012/11/11/JSON-inception/).

Consider this using play 2.1-RC1 and scala 2.10.0-RC1:

import play.api.libs.json._
import play.api.libs.functional.syntax._

case class ThisIsOk(id: String)

case class ThisIsNotOk(id: String)

object ThisIsNotOk {
  val something = 2
}

case class ThisBecomesOk(id: String)

object ThisBecomesOk extends (String => ThisBecomesOk) {
  val something = 2
}

object Test {
  val compilesFine = Json.format[ThisIsOk]

/*
Compilation error:
[error]  found   : ThisIsNotOk.type
[error]  required: String => ThisIsNotOk
[error]     val doesNotCompile = Json.format[ThisIsNotOk]
*/
  val doesNotCompile = Json.format[ThisIsNotOk]

  val compilesFineButIsAPain = Json.format[ThisBecomesOk]
}
@scabug

This comment has been minimized.

Copy link
Author

scabug commented Sep 9, 2015

@hseeberger said:
So, what's the status here? It's very disappointing that one can't use tupled for case classes which have an explicit companion.

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Sep 9, 2015

Jeff May (jeffmay) said:
Yea, this is one of those "odd Scala bugs" that I have to explain to newcomers all the time. We have a lot of code that was written before switching to Play Json, and a lot of places just passed the case class instead of the lambda'd apply method. The newcomer will add a companion object to create the Json Format and all of a sudden get compile errors about companion object singleton types not extending blah blah blah. It's definitely unexpected.

I notice that a lot of people I work with don't really know the difference between f(X(_, _)) or f(X *) or f(X.apply *) or f(X.apply) or f(X) and certainly don't understand singleton types. Its a very subtle and confusing area already and this bug just adds to the exceptional cases. It would be nice to see some consistency here. Either case class companion objects should be functions or they shouldn't. The fact that the true behavior is a surprise seems un Scala-like considering much of the language just does the right thing when mixing a lot of orthogonal features.

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Sep 9, 2015

Stephen Compall (s11001001) said:
Heiko, I agree, but you can work around it by adding extends function type to the object.

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Sep 9, 2015

Jeff May (jeffmay) said:
The other work around is to f(X.apply.tupled) or f((X.apply _).tupled) I am not sure which right now.

My issue with this is that it is inconsistent. I wouldn't mind typing the work around of using the apply method every time. The fact that the rules change for what seems like an unrelated change is what bothers me.

@scabug

This comment has been minimized.

Copy link
Author

scabug commented Dec 16, 2016

Sven Ludwig (sourcekick) said:
I just stumbled over this starting with Slick, where this is also referenced in the docs: http://slick.lightbend.com/doc/3.2.0-M2/schemas.html#mapped-tables together with #4808

It would be cool to see this fixed for a better "just works out-of-the-box" feeling.

@HuStmpHrrr

This comment has been minimized.

Copy link

HuStmpHrrr commented Jun 19, 2017

hi all, yet another case. this is a fundamental mistake, no? we either shouldn't inherit FunctionX or inherit it automatically, shouldn't we? this looks entirely inconsistent.

@S11001001

This comment has been minimized.

Copy link

S11001001 commented Aug 6, 2017

@HuStmpHrrr It's hard to say. It's inconsistent, and a frequent gotcha, but the obvious solutions have problems.

Never extend function type: Well, this is just piling on the inconvenience. Thanks but no thanks.

Extend function type even when explicitly defining object: This is the most intuitive behavior, and probably what you want 95% of the time. But it's not all sunshine and roses: what if you don't want to extend the function type? What if you want to extend the function type, but at different type parameters?

case class Foo(b: Bar)

object Foo extends (Baz => Foo) {
  def apply(bz: Baz): Foo = apply(bazToBar(bz))
}

This is a direct example, but you might also be extending the function type indirectly, via extending some other trait you wish your object to extend. This would not compile if the case class machinery insisted on a Bar => Foo superclass (depending on Bar/Baz's subtyping relationship, of course).

It just so happens that, in the absence of any syntax saying you don't want the function extend, the syntax for saying so is to declare the object explicitly without the function extend. So the 5% case is the default, the 95% case is more verbose, but at least you can do everything you might need to.

@som-snytt

This comment has been minimized.

Copy link

som-snytt commented Dec 26, 2017

One proposal to signal don't extend FunctionN is to supply a private apply with the matching signature. A linter for unused members would probably want to ignore that use case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment