Skip to content
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

Add typed versions of Mirror.ProductOf#fromProduct #14114

Merged
merged 5 commits into from Feb 28, 2022

Conversation

mpilquist
Copy link
Contributor

@mpilquist mpilquist commented Dec 15, 2021

The existing Mirror.ProductOf#fromProduct method is defined for any scala.Product which can lead to some surprising runtime errors:

Welcome to Scala 3.1.2-RC1-bin-SNAPSHOT-nonbootstrapped-git-b636aa9 (17.0.1, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

scala> case class Point(x: Int, y: Int, z: Int)
// defined case class Point

scala> summon[deriving.Mirror.ProductOf[Point]].fromProduct((1, 2))
java.lang.IndexOutOfBoundsException: 2 is out of bounds (min 0, max 1)
  at scala.Product2.productElement(Product2.scala:43)
  at scala.Product2.productElement$(Product2.scala:40)
  at scala.Tuple2.productElement(Tuple2.scala:24)
  at rs$line$1$Point$.fromProduct(rs$line$1:1)
  at rs$line$1$Point$.fromProduct(rs$line$1:1)
  ... 38 elided

scala> summon[deriving.Mirror.ProductOf[Point]].fromProduct((1, 2, 3.0))
java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.Integer (java.lang.Double and java.lang.Integer are in module java.base of loader 'bootstrap')
  at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:99)
  at rs$line$1$Point$.fromProduct(rs$line$1:1)
  at rs$line$1$Point$.fromProduct(rs$line$1:1)
  ... 38 elided

This PR adds fromTuple and fromProductTyped extension methods to Mirror.ProductOf[T], both which ensure the argument is well typed:

scala> summon[deriving.Mirror.ProductOf[Point]].fromTuple((1, 2))
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |summon[deriving.Mirror.ProductOf[Point]].fromTuple((1, 2))
  |                                                   ^^^^^^
  |                                                 Found:    (Int, Int)
  |                                                 Required: (Int, Int, Int)

longer explanation available when compiling with `-explain`
1 error found

scala> summon[deriving.Mirror.ProductOf[Point]].fromTuple((1, 2, 3.0))
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |summon[deriving.Mirror.ProductOf[Point]].fromTuple((1, 2, 3.0))
  |                                                          ^^^
  |                                                 Found:    (3.0d : Double)
  |                                                 Required: Int

longer explanation available when compiling with `-explain`
1 error found

scala> summon[deriving.Mirror.ProductOf[Point]].fromTuple((1, 2, 3))
val res2: Point = Point(1,2,3)

@mpilquist mpilquist force-pushed the topic/mirror-from-product-typed branch from b636aa9 to 8fefb76 Compare December 15, 2021 00:16
tests/neg/deriving.scala Outdated Show resolved Hide resolved
@dwijnand
Copy link
Member

Add as experimental?

@mpilquist
Copy link
Contributor Author

Add as experimental?

Done. Curious when @experimental gets removed from API additions? Any link to docs on that?

@nicolasstucki
Copy link
Contributor

Curious when @experimental gets removed from API additions?

We can only remove experimental when going to a new minor release (3.2.0, 3.3.0, 3.4.0, ...). Before each minor release we need to decide which experimental method will become non-experimental.

@julienrf
Copy link
Collaborator

It looks to me that the existing fromProduct method should be fixed instead of adding a new method fromProductTyped. Is this something doable before Scala 4.x?

tests/neg/deriving.scala Outdated Show resolved Hide resolved
tests/neg/deriving.scala Outdated Show resolved Hide resolved
library/src/scala/deriving/Mirror.scala Outdated Show resolved Hide resolved
@OlivierBlanvillain
Copy link
Contributor

What are the benefits of adding these API to the standard library as opposed to defining then in user space? IIUC the Mirror API was not designed to provide a type safe experience out of the box, but rather to be a low-level building block for people to build type-safe frameworks on top...

@mpilquist
Copy link
Contributor Author

Discoverability & avoidance of dependencies in foundational libraries, which lead to instability & churn in overall library ecosystem. The more type safety we can provide in these foundational APIs, the easier it will be for folks to build their higher level derivations, frameworks, etc.

@OlivierBlanvillain
Copy link
Contributor

That makes sense, although if it's just an extension method away I think we could also do without. But I suppose such argument could be made for any addition to the standard library...

Regardless, I think that PR should also update docs/docs/reference/contextual/derivation.md since it's the main entry point to the Mirror APIs.

Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that addition is quite useful.

@odersky
Copy link
Contributor

odersky commented Jan 23, 2022

@mpilquist Can you rebase on latest master, then we can merge? Thank you!

@odersky odersky assigned mpilquist and unassigned odersky Jan 23, 2022
@mpilquist mpilquist force-pushed the topic/mirror-from-product-typed branch from 5d8a855 to 8df5094 Compare January 23, 2022 18:44
@mpilquist
Copy link
Contributor Author

Done, thanks!

@mpilquist mpilquist force-pushed the topic/mirror-from-product-typed branch 3 times, most recently from b49216d to 8248da2 Compare January 27, 2022 12:30
@mpilquist mpilquist force-pushed the topic/mirror-from-product-typed branch from 8248da2 to 6e6e0d2 Compare February 8, 2022 13:03
@TheElectronWill TheElectronWill merged commit fdf36a0 into scala:main Feb 28, 2022
@mpilquist mpilquist deleted the topic/mirror-from-product-typed branch September 22, 2022 01:19
@Kordyjan Kordyjan added this to the 3.1.3 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants