-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #9011: Make single enum values inherit from Product #9018
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
Changes from 1 commit
1e13a92
ba466e9
67f0786
323d228
29ac3a6
1003adb
9a887fe
6bd846a
8832569
3a352e6
1cd85bc
74cc1b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package scala | ||
|
||
trait EnumValue extends Product: | ||
override def canEqual(that: Any) = true | ||
override def productArity: Int = 0 | ||
override def productPrefix: String = toString | ||
override def productElement(n: Int): Any = | ||
throw IndexOutOfBoundsException(n.toString()) | ||
override def productElementName(n: Int): String = | ||
throw IndexOutOfBoundsException(n.toString()) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
enum Opt[+T] derives Eq: | ||
case Sm(t: T) | ||
case Nn | ||
|
||
import scala.deriving._ | ||
import scala.compiletime.{erasedValue, summonInline} | ||
|
||
trait Eq[T] { | ||
def eqv(x: T, y: T): Boolean | ||
} | ||
|
||
object Eq { | ||
given Eq[Int] { | ||
def eqv(x: Int, y: Int) = x == y | ||
} | ||
|
||
inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match { | ||
case _: Unit => Nil | ||
case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts] | ||
} | ||
|
||
def check(elem: Eq[_])(x: Any, y: Any): Boolean = | ||
elem.asInstanceOf[Eq[Any]].eqv(x, y) | ||
|
||
def iterator[T](p: T) = p.asInstanceOf[Product].productIterator | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going back to the original issue (#9011), is this cast actually safe?
If this isn't actually guaranteed, then the documentation needs to be updated to not rely on this cast in an example. |
||
|
||
def eqSum[T](s: Mirror.SumOf[T], elems: List[Eq[_]]): Eq[T] = | ||
new Eq[T] { | ||
def eqv(x: T, y: T): Boolean = { | ||
val ordx = s.ordinal(x) | ||
(s.ordinal(y) == ordx) && check(elems(ordx))(x, y) | ||
} | ||
} | ||
|
||
def eqProduct[T](p: Mirror.ProductOf[T], elems: List[Eq[_]]): Eq[T] = | ||
new Eq[T] { | ||
def eqv(x: T, y: T): Boolean = | ||
iterator(x).zip(iterator(y)).zip(elems.iterator).forall { | ||
case ((x, y), elem) => check(elem)(x, y) | ||
} | ||
} | ||
|
||
inline given derived[T](using m: Mirror.Of[T]) as Eq[T] = { | ||
val elemInstances = summonAll[m.MirroredElemTypes] | ||
inline m match { | ||
case s: Mirror.SumOf[T] => eqSum(s, elemInstances) | ||
case p: Mirror.ProductOf[T] => eqProduct(p, elemInstances) | ||
} | ||
} | ||
} | ||
|
||
object Test extends App { | ||
import Opt._ | ||
val eqoi = summon[Eq[Opt[Int]]] | ||
assert(eqoi.eqv(Sm(23), Sm(23))) | ||
assert(eqoi.eqv(Nn, Nn)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For parity with case object this should extend Serializable too I think:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it probably makes more sense to have
Product
andSerializable
added as parents to the enum class itself, that way they never show up when lubbing enum cases.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but I am not sure about what other effects this will have.