question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Enum families and overlapping enums

See original GitHub issue

This works:

sealed trait Foo extends EnumEntry
object Foo extends Enum[Foo] {
  val values = findValues

  case object Foo1 extends Foo
  case object Foo2 extends Foo

  sealed trait Bar extends Foo
  object Bar extends Enum[Bar] {
    val values = findValues
    case object Bar1 extends Bar
    case object Bar2 extends Bar
  }

  sealed trait Qux extends Foo
  object Qux extends Enum[Qux] {
    val values = findValues
    case object Qux1 extends Qux
    case object Qux2 extends Qux
  }
}

What I want:

sealed trait Foo extends EnumEntry
object Foo extends Enum[Foo] {
  val values = findValues

  case object Foo1 extends Foo
  case object Foo2 extends Foo

  case object Bar1 extends Bar
  case object Bar2 extends Bar

  case object Qux1 extends Qux
  case object Qux2 extends Qux

  case object BarQux1 extends Bar with Qux
  case object BarQux2 extends Bar with Qux

  sealed trait Bar extends Foo
  object Bar extends Enum[Bar] {
    val values = findValues
  }

  sealed trait Qux extends Foo
  object Qux extends Enum[Qux] {
    val values = findValues
  }
}

Now that compiles, but at runtime Bar.values and Qux.values are empty!

Work around with Shapeless:

trait EnumValues[A] {
  def find: IndexedSeq[A]
}

object EnumValues {
  def apply[A : EnumValues]: EnumValues[A] = implicitly
  def find[A : EnumValues]: IndexedSeq[A]  = apply[A].find

  @nowarn("cat=unused")
  implicit def instance[A, C <: Coproduct, V <: HList](implicit
      generic: Generic.Aux[A, C],
      witnesses: LiftAll.Aux[Witness.Aux, C, V],
      toArraySeq: ToTraversable.Aux[V, ArraySeq, Witness.Lt[A]]
  ): EnumValues[A] = new EnumValues[A] {
    val find = toArraySeq(witnesses.instances).map(_.value)
  }
}

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
lloydmetacommented, Nov 7, 2020

I’m fine with saying this is working as intended. But note that the case objects in my example are still nested in an enclosing object.

In your original post, you stated that your issue was that

Bar.values and Qux.values are empty`

Your Bar.values is declared as

  sealed trait Bar extends Foo
  object Bar extends Enum[Bar] {
    val values = findValues
  }

There are no Bars inside the scope of the immediately enveloping Enum[Bar].

It’s not impossible to find them without using knownDirectSubclasses and still preserving all the properties you mentioned

Last I checked Shapeless conjures its Generic and friends eventually using knownDirectSubclasses anyways, then sorts them (https://github.com/milessabin/shapeless/issues/892).

In any case, you can get what you want by doing something like this:

import enumeratum._

sealed trait Foo extends EnumEntry
object Foo extends Enum[Foo] {
  val values = findValues

  case object Foo1 extends Foo
  case object Foo2 extends Foo

  case object Bar1 extends Bar
  case object Bar2 extends Bar

  case object Qux1 extends Qux
  case object Qux2 extends Qux

  case object BarQux1 extends Bar with Qux
  case object BarQux2 extends Bar with Qux

  sealed trait Bar extends Foo
  object Bar extends Enum[Bar] {
    val values = Foo.values.collect { case e: Bar => e }
  }

  sealed trait Qux extends Foo
  object Qux extends Enum[Qux] {
    val values = Foo.values.collect { case e: Qux => e }
  }
}

println(Foo.values)
println(Foo.Bar.values)
println(Foo.Qux.values)

Scastie: https://scastie.scala-lang.org/Adc4z1d3TXCh3UOhz4CM1A

0reactions
joroKr21commented, Nov 7, 2020

That’s a really nice way to make it work and no need for shapeless. Thank you 👍 Closing this now. But WDYT about a compiler warning when findValues is empty?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Enums, overlapping values, C# - Stack Overflow
Let's take a simple example enum PrivilegeLevel { None, Reporter, Reviewer, Admin, DefaultForNewUser = None, DefaultForProjectOwner ...
Read more >
Enum Class (System)
Define enumeration constants in powers of two, that is, 1, 2, 4, 8, and so on. This means the individual flags in combined...
Read more >
Structs and Enums - C# in Simple Terms
A series of overlapping metal trusses on the side of a building. ... The other kind of special object we'll discuss is an...
Read more >
5 more things you should know about enums in C# - ...
Use only powers of two when defining flags, so that you won't overlap values when combining more enums; this will give you also...
Read more >
Peeking inside a Rust enum
Now, if we look at Rust enums... it's a different story. ... It's just like struct , except everything overlaps, and it has...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found