Giter Club home page Giter Club logo

Comments (7)

daniel-shuy avatar daniel-shuy commented on July 17, 2024

After some Googling, it seems that this Enum library, Numerato, uses Macro Annotations to achieve something similar to my suggestion. It would be cool if we could have the best of both worlds :)

from enumeratum.

lloydmeta avatar lloydmeta commented on July 17, 2024

If you want to have a method that returns the index of any properly-implemented EnumEntry (as in, it is inside a companion object that extends Enum), you just need to materialise its companion object.

The following is via Ammonite

Welcome to the Ammonite Repl 0.8.1
(Scala 2.12.1 Java 1.8.0_101)
@ import $ivy.`com.beachape::enumeratum:1.5.7`, enumeratum._ 
import $ivy.$                               , enumeratum._
@ {
  // Declare 2 enums
  
  sealed trait AgeGroup extends EnumEntry
  
  case object AgeGroup extends Enum[AgeGroup] {
  
    val values = findValues
  
    case object Baby     extends AgeGroup
    case object Toddler  extends AgeGroup
    case object Teenager extends AgeGroup
    case object Adult    extends AgeGroup
    case object Senior   extends AgeGroup
  
  }
  
  sealed abstract class Light extends EnumEntry
  
  case object Light extends Enum[Light] {
    val values = findValues
    case object Red extends Light
    case object Blue extends Light
    case object Green extends Light
  }
  } 
defined trait AgeGroup
defined object AgeGroup
defined class Light
defined object Light
@ {
  // indexOf for any properly-implemented Enum
  def indexOf[A <: EnumEntry: Enum](entry: A): Int  = {
      implicitly[Enum[A]].indexOf(entry)
  }
  } 
defined function indexOf
@ indexOf(Light.Red: Light) 
res3: Int = 0
@  
@   indexOf(AgeGroup.Baby: AgeGroup) 
res4: Int = 0
@  
@   indexOf(AgeGroup.Adult: AgeGroup) 
res5: Int = 3

BTW, you'll notice that I had to add a type annotation when passing in each member; this is because otherwise, the value passed to the function will have the singleton type (e.g. AgeGroup.Adult.type instead of AgeGroup), which I suspect is what caused the error: not found: value error you saw :)


As for Macro annotations, I think they're worth exploring. When Enumeratum was written, I avoided macro annotations because IDEs didn't really understand how the macros expanded w/o writing a plugin, but with Scalameta coming around the corner, that has changed (IntelliJ for example can support Scalameta macro annotations fine).

One thing I've seen quite often with the annotation-driven enum solutions is that they exchange power/flexibility in exchange for terseness. What this means is that they usually don't support the full myriad of things that Scala allows you to do, like adhock extend other traits/classes in members (e.g. the way we do stacked traits to manipulate names) or add methods and values to each member, which can come in quite handy. By the time you can support all these use-cases, you've probably got a fairly huge and complex macro.

If we go down the macro annotation route, I'd like to try our best to keep the flexibility and power that Enumeratum currently affords users. If not all, then at least cover the most important use cases :)

from enumeratum.

daniel-shuy avatar daniel-shuy commented on July 17, 2024

Interesting, I have so much more to learn :)

Good point with the name-manipulating traits, didn't think of that, hmm...maybe it could be replicated with annotation parameters? Haha

Maybe we should wait until Macros is no longer experimental and finally becomes a part of the standard library (hopefully in 2.13?)

from enumeratum.

lloydmeta avatar lloydmeta commented on July 17, 2024

Interesting, I have so much more to learn :)

Same here ! To be fair, in this case the error message thrown at compile time was terribly cryptic and not helpful (error: not found: value). I've made an attempt to make it nicer with #110.

Maybe we should wait until Macros is no longer experimental and finally becomes a part of the standard library (hopefully in 2.13?)

I get the feeling that this will be a long ways in the future :). I played around with Scalameta for a while and found that it doesn't yet support things like finding the parent of a given type (for now the recommendation is if you need this kind of functionality, emit an annotation built with the old macro API), and mangles certain for-comprehensions.

So yeah I'm with you there; probably best to wait until things are more ready.

from enumeratum.

daniel-shuy avatar daniel-shuy commented on July 17, 2024

Just leaving my thoughts here for future reference:

I was just wondering what would the extent of possibilities be with Macro Annotations.

Theoretically, it would even be possible to combine Enum and ValueEnum, and maybe even allow multiple Value keys of different types to be indexed, maybe even with optional unique constraints.

Eg.

@Enum(CapitalWords)
sealed abstract class Grocery(
    @EnumKey(unique = true)    // should unique default to true/false?
    id: Int, 

    @EnumKey(unique = false)    // should unique default to true/false?
    category: String,

    exampleBool: Boolean
)

object Grocery {
    case object Chicken extends Grocery(101, "Meat", true)
    case object Lamb extends Grocery(102, "Meat", false)
    // case object Pork extends Grocery(102, "Meat", true) <-- will fail to compile because the id 102 must be unique
    case object Coke extends Grocery(201, "Beverage", false)
}

would expand to:

sealed abstract class Grocery(id: Int, category: String, exampleBool: Boolean) extends EnumEntry with CapitalWords

object Grocery extends Enum[Grocery] {
    case object Chicken extends Grocery(101, "Meat")
    case object Lamb extends Grocery(102, "Meat")
    case object Coke extends Grocery(201, "Beverage")

    val values: immutable.IndexedSeq[Grocery] = IndexedSeq(Chicken, Lamb, Coke)
    private val idToEntriesMap: immutable.Map[Int, Grocery] = 
        Map(
            Chicken.id -> Chicken,
            Lamb.id -> Lamb,
            Coke.id -> Coke
        )
    private val categoryToEntriesMap: immutable.Map[String, Set[Grocery]] = ???    // some MultiMap implementation (Scala's MultiMap seems lacking...maybe Google Guava's HashMultimap?)

    def withId(id: Int): Grocery = idToEntriesMap(id)
    def withCategory(category: String): Set[Grocery] = categoryToEntriesMap(category)
}

It may even be possible to make the syntax even more concise (see Numerato)

from enumeratum.

lloydmeta avatar lloydmeta commented on July 17, 2024

Theoretically, pretty much anything is possible with macros ;) Just take a look at scala-async or quill. Practically speaking, my experience is that the size and complexity of the macro grow non-linearly with every feature (language level) you want to support because essentially you're on your own building an AST transformer. The difficulty balloons even more when concise syntax is also a goal, which is why most of those brevity-focused libs aren't as powerful as Enumeratum.

Having said that, I think your design looks nice and it's worth considering how Enumeratum should evolve in the future. I'll close this issue for now and open another one for the purpose of discussing future designs.

from enumeratum.

daniel-shuy avatar daniel-shuy commented on July 17, 2024

Thanks. I'll also open a new issue for the EnumSet/EnumMap implementation.

from enumeratum.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.