-
Notifications
You must be signed in to change notification settings - Fork 31
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
Support @newtype case classes if not inside object #11
Comments
@oleg-py Maybe you can use a trait OwnIdType {
@newtype case class ID(private val toInt: Int)
object ID {
// Circe things
implicit val toJson: Encoder[ID] = deriving
implicit val fromJson: Decoder[ID] = deriving
}
} What I would then do is just use If you really want an extension method on it, you could maybe just write a |
@oleg-py I went ahead and tested out the idea, here's a basic example of using this strategy with the package example
import io.estatico.newtype.macros.newtype
import io.estatico.newtype.ops._
object Example {
def main(args: Array[String]): Unit = {
val zebra = Zebra(Zebra.ID(1), "zeb")
// Using coerce, no need for the ToInt type class
val zebraIntId1 = zebra.id.coerce[Int]
// Using the ToInt type class' ops
import ToInt.ops._
val zebraIntId2 = zebra.id.toInt
}
}
trait OwnIdType {
@newtype case class ID(private val toInt: Int)
object ID {
implicit val toInt: ToInt[ID] = deriving
}
}
case class Zebra(id: Zebra.ID, name: String)
object Zebra extends OwnIdType
// ToInt type class example
trait ToInt[A] {
def toInt(a: A): Int
}
object ToInt {
implicit val intToInt: ToInt[Int] = new ToInt[Int] {
override def toInt(a: Int): Int = a
}
final class Ops[A](val repr: A) extends AnyVal {
def toInt(implicit ev: ToInt[A]): Int = ev.toInt(repr)
}
trait ToOps {
implicit def toToIntOps[A](x: A): Ops[A] = new Ops(x)
}
val ops = new ToOps {}
} |
@carymrobbins not sure what problem My point is that it is possible to have extension methods on |
@oleg-py The problem is that it becomes less efficient than just using a case class in place of the newtype. If our Ops does not extend AnyVal, we'll get a new allocation of the Ops class every time an extension method is invoked. The ToInt type class avoids that by still defining the Ops in an object, permitting the use of AnyVal. |
To clarify, the reason I'm not comfortable with that feature is that it seems to undermine the point of using newtype in the first place. |
@carymrobbins sorry for the long reply. I use newtypes to avoid extra boxing, giving me easy support for H2 and Postgres arrays. As far as extension methods go, I expect performance overhead of not having |
@oleg-py While working on What about something like this? @newtype(boxedExtensions = true) case class ID(toInt: Int) Naming things is hard, I'm open to suggestions. Also considering - @newtype(anyValOps = false) case class ID(toInt: Int) I think I can include this change in the next release, along with |
@carymrobbins sounds amazing 👍 I would name it something along the lines of |
Yeah that seems slightly better, I'd change the @newtype(optimizeOps = false) case class ID(toInt: Int) I like this also because it doesn't really guarantee that things are optimized at any particular level, just that they will be to the extent that we can (and I can even disallow |
This has been merged to master and will be available in the 0.4.0 release. |
I'm making a web app and using newtypes to for type-safe IDs for different model types.
As there are quite a few model types, I tried to move type code into a trait that can be mixed into companion object:
That didn't work, however, telling me that fields can only be used if the newtype is defined inside an object
Ultimately I wrote case class boilerplate myself:
It compiles and works, and the only significant difference is that
Ops
class cannot extendAnyVal
.Is it possible to support
@newtype case class
inside traits by droppingextends AnyVal
on ops class?The text was updated successfully, but these errors were encountered: