Instantiate enum from String

I have a simple use case, parsing JSON.
This is my typeclass, simplified:

trait JsonReader[T]:
  def parse(jValue: String): T

So basically I want to convert a String to a simple enum:

enum Color:
  case Red, Blue

I want to parse string “Red” to Color.Red enum.
The only thing I found that is close is ctx.choose(v), but that works on T values, and I only have a String…

Another thing that comes to my mind is using runtime reflection (for which I don’t have a working example).
But it would make sense to me that we can do this at compile time.

Is there an easy way I can do this with magnolia?

If you have access to the SealedTrait instance, you can get the subtypes via .subtypesArray, and then access the .typeName to find the appropriate subtype. From that, you can get the typeclass instance for the subtype.

You can then call .parse on that typeclass instance - which corresponds to the specific enum value. This should be an instance of a CaseClass. The rawConstruct with an empty field value should give you the desired instance.

Let me know if this helps :slight_smile:

1 Like

Maybe the Show typeclass and this test will help: magnolia/SumsTests.scala at f5df7e63ce41375871524f442d0438aba698c6b7 · softwaremill/magnolia · GitHub

1 Like

Thank you very much Adam!
Seems like I was missing the rawConstruct and what it does… It’s really handy. :slight_smile:

An example in Scastie if anyone needs it: Scastie - An interactive playground for Scala.

import magnolia1.*

trait ParseString[T] {
  def parse(str: String): T
}

object ParseString extends AutoDerivation[ParseString]:
  def join[T](ctx: CaseClass[ParseString, T]): ParseString[T] = value =>
    ctx.rawConstruct(Seq())

  override def split[T](ctx: SealedTrait[Typeclass, T]): ParseString[T] = value =>
    val subtype = ctx.subtypes.find(_.typeInfo.short == value).get
    subtype.typeclass.parse(value)

/////
enum Color derives ParseString:
  case Red

val tc = summon[ParseString[Color]]
println( tc.parse("Red") )

1 Like