Skip to content

case class optimisation #10659

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

Closed
fommil opened this issue Dec 16, 2017 · 7 comments
Closed

case class optimisation #10659

fommil opened this issue Dec 16, 2017 · 7 comments

Comments

@fommil
Copy link

fommil commented Dec 16, 2017

it is quite common when writing invariant ADTs that have an "empty" element to use something along the lines of the following encoding

sealed abstract class Maybe[A]
final case class Empty[A]() extends Maybe[A]
final case class Just[A](a: A) extends Maybe[A]

but this incurs an object allocation overhead that must be mitigated with the following (ugly) pattern

  final case object Empty extends Maybe[Nothing] {
    def apply[A](): Maybe[A] = this.asInstanceOf[Maybe[A]]
    def unapply[A](e: Maybe[A]): Boolean = this == e
  }

it would be fantastic if the compiler automatically added the apply() and unapply() under these circumstances.

@sjrd
Copy link
Member

sjrd commented Dec 17, 2017

Under what circumstances?

What code do you expect to write, exactly, and what do you expect would be generated?

@fommil
Copy link
Author

fommil commented Dec 17, 2017

The ticket has the expected input and output, what am I missing?

@fommil
Copy link
Author

fommil commented Dec 17, 2017

Damn, we found that this optimisation breaks exhaustivity, see scalaz/scalaz#1579 (comment)

@fommil fommil closed this as completed Dec 17, 2017
@jvican
Copy link
Member

jvican commented Dec 17, 2017

I think @sjrd is looking for a more formalized version of the codegen (?). I agree this would be useful, I’ve seen this pattern in many places - it’s quite common when you define tree-like data structures and it’s not clear for beginners what they should do to have a singleton empty.

@sjrd
Copy link
Member

sjrd commented Dec 17, 2017

The ticket has the expected input and output, what am I missing?

Then it's not sound in general:

final case class Empty[A]() extends Maybe[A] {
  var oops: A = _
}

val e1 = Empty[Int]()
e1.oops = 5
val e2 = Empty[String]()
e2.oops = "foo"
println(e1.oops) // prints "foo"
val s = e1.oops // throws ClassCastException
println(s)

And anyway, people could rely on the identity of the Empty()'s.

@fommil
Copy link
Author

fommil commented Dec 17, 2017

We may be willing to accept the loss of exhaustiveness checking in scalaz, which might make this a reasonable optimisation still to make.

I feel like there should be some form of epic to capture optimisations that can be made when code is pure. Your var breaks that.

I feel like I could code this up, but I don't understand what you're asking in terms of a formal spec... so anybody who feels they know how to write formal specs can comment here. I shan't be adding one.

@fommil
Copy link
Author

fommil commented Dec 17, 2017

the pattern is flawed.

@fommil fommil closed this as completed Dec 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants