44

I am in the process of migrating from Slick to Slick 2, and in Slick 2 you are meant to use the tupled method when projecting onto a case class (as shown here http://slick.typesafe.com/doc/2.0.0-RC1/migration.html)

The problem is when the case class has a companion object, i.e. if you have something like this

case class Person(firstName:String, lastName:String) {

}

Along with a companion object

object Person {
  def something = "rawr"
}

In the same scope, the tupled method no longer works, because its trying to run tupled on the object, instead of the case class.

Is there a way to retrieve the case class of Person rather than the object, so you can call tupled properly?

Andrii Abramov
  • 10,019
  • 9
  • 74
  • 96
mdedetrich
  • 1,899
  • 1
  • 18
  • 29
  • do you absolutely need the companion object? also, I think this is a general Scala question not really Slick related. – Erik Kaplun Mar 13 '14 at 01:11
  • Yes I do, unless I want to refactor like half of my project (the companion object has a lot of helper methods for the case class in question) And yes you are right, its not directly related to slick, just mentioned it because Slick may have its own workaround – mdedetrich Mar 13 '14 at 01:13
  • You can't just rename the companion object and import the renamed object's contents directly to the case class? Alternatively, you can just make your custom companion object look like an autogenerated one manually. – Erik Kaplun Mar 13 '14 at 01:31
  • Is what I am asking something impossible to do? I mean I can do that (amongst various other options), but that object is used in hundreds of places, so it would be a pretty painful refactor – mdedetrich Mar 13 '14 at 01:40
  • I've proposed a workaround, so no, it's not impossible, but it's ugly... overriding case class companion objects isn't really intended, otherwise what's the point of using the case class in the first place? case classes are afaik just syntactic sugar over regular classes + autogenerated companions with `apply` and `unapply`... if you override the autogen. companion, you're by definition defeating the purpose of case classes. – Erik Kaplun Mar 13 '14 at 01:41
  • Well it was a design choice, you override the case class with an object for the same reason you use static final in java, that is, you want to attach helper methods onto a namespace In this case I want to attach helper methods to the `Person` namespace, things like apply and unapply are just for creating an instance of Person (which case classes generate), however it doesn't have anything to do with the other methods that are attached to the `Person` type – mdedetrich Mar 13 '14 at 01:58
  • As I said, you can put the helper functions in **any** object and import them to `Person`; it doesn't have to be a companion object. – Erik Kaplun Mar 13 '14 at 02:59
  • 1
    Yes, but idiomatically speaking, if you have global functions that deal with `Person`, they should work on the `Person` namespace, thats the whole purpose of the Singleton Pattern There is a reason when have global methods with `BigDecimal` (as an example), the methods are attached to `BigDecimal`, and not `BigDecimalHelpers` or something like that – mdedetrich Mar 13 '14 at 03:18
  • It's hardly idiomatic to override and then re-implement the gist of case classes. – Erik Kaplun Mar 13 '14 at 03:20
  • The intention to use an object with the same namespace as its `case class` is not to override the `case class`, the intention is to add methods/values from the `object` into the same namespace of the `case class`. This practice is done in almost every mainstream OOP language (including Java,Scala and others). You are not overriding or reimplementing the `case classes` (unless you are defining `apply`, `unapply` in your `object`), you are adding ontop of the namespace. Whether a `class` is a `case class` or a normal `class` is irrelevant, `case classes` just add an apply/unapply methods. – mdedetrich Mar 13 '14 at 03:52
  • OK, I see, but when why did the `tupled` method disappear? – Erik Kaplun Mar 13 '14 at 03:56
  • 3
    Actually they don't disappear, have a look at this answer http://stackoverflow.com/a/22368413/1519631. As to why the `tupled` method "disappears", I have no clue. After some quick reading, it may be down to how its brought into scopes by implicits (this is done internally). Remember that you can still use `case classes` fine, even if there is a companion `object`. This issue only cropped up because I specifically needed to use the tupled method – mdedetrich Mar 13 '14 at 04:10

4 Answers4

87

You can also write

(Person.apply _).tupled

to avoid repeating the types.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
25

This is very similar to what Alexey Romanov said, but in order to avoid lifting apply whenever you need tupled, we just add it to our companion objects.

object Person {
  def something = "rawr"
  def tupled = (Person.apply _).tupled
}

Now you can call Person.tupled just like you would have if it didn't have a companion object.

Tim Gautier
  • 29,150
  • 5
  • 46
  • 53
7

One workaround is define a companion object as follows:

object Person extends((String,String) => Person) {
    ...
}

See. https://groups.google.com/d/msg/scala-user/jyWBMz5Qslw/Bryv4ftzRLgJ

kawty
  • 1,656
  • 15
  • 22
  • Thanks, that actually works (I assume there isn't any workaround for the boilerplate that is (String,String) ) – mdedetrich Mar 13 '14 at 01:55
4

To build on some of the other comments you could do the following as well since tuple is calling the generated default apply method for the case class.

object Person {
  ...
  def tupled = (this.apply _).tupled
}
ekrich
  • 357
  • 2
  • 10